1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com/*
2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Copyright 2011 Google Inc.
3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com *
4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Use of this source code is governed by a BSD-style license that can be
5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * found in the LICENSE file.
6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com */
7daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com
88a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkBlurDrawLooper.h"
97ce661d19c5cf4484305a1b20c44bd111f129847robertphillips@google.com#include "SkBlurMask.h"     // just for SkBlurMask::ConvertRadiusToSigma
108a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkBlurMaskFilter.h"
118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkCanvas.h"
124991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com#include "SkColorFilter.h"
138b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.org#include "SkReadBuffer.h"
148b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.org#include "SkWriteBuffer.h"
158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkMaskFilter.h"
164991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com#include "SkPaint.h"
174991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com#include "SkString.h"
184991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com#include "SkStringUtils.h"
198a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
207bd141dce43ea3405bc60c9c84e6f910b851b079skia.committer@gmail.comSkBlurDrawLooper::SkBlurDrawLooper(SkColor color, SkScalar sigma,
217ce661d19c5cf4484305a1b20c44bd111f129847robertphillips@google.com                                   SkScalar dx, SkScalar dy, uint32_t flags) {
227ce661d19c5cf4484305a1b20c44bd111f129847robertphillips@google.com    this->init(sigma, dx, dy, color, flags);
237ce661d19c5cf4484305a1b20c44bd111f129847robertphillips@google.com}
247ce661d19c5cf4484305a1b20c44bd111f129847robertphillips@google.com
25daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com// only call from constructor
26daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.comvoid SkBlurDrawLooper::initEffects() {
27daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com    SkASSERT(fBlurFlags <= kAll_BlurFlag);
28daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com    if (fSigma > 0) {
29daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com        uint32_t flags = fBlurFlags & kIgnoreTransform_BlurFlag ?
30daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com                            SkBlurMaskFilter::kIgnoreTransform_BlurFlag :
31daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com                            SkBlurMaskFilter::kNone_BlurFlag;
32eeaeafebdeec2c546134b741ab8b3c6b7c5190abskia.committer@gmail.com
33daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com        flags |= fBlurFlags & kHighQuality_BlurFlag ?
34daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com                    SkBlurMaskFilter::kHighQuality_BlurFlag :
35daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com                    SkBlurMaskFilter::kNone_BlurFlag;
36eeaeafebdeec2c546134b741ab8b3c6b7c5190abskia.committer@gmail.com
37daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com        fBlur = SkBlurMaskFilter::Create(kNormal_SkBlurStyle, fSigma, flags);
384e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com    } else {
398a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        fBlur = NULL;
404868e6b221a4a98e40f977851af5fcf09631ea15senorblanco@chromium.org    }
41eeaeafebdeec2c546134b741ab8b3c6b7c5190abskia.committer@gmail.com
42daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com    if (fBlurFlags & kOverrideColor_BlurFlag) {
43a6398911174d5445456ecb2f5f4f0565db2f100bjunov@google.com        // Set alpha to 1 for the override since transparency will already
44a6398911174d5445456ecb2f5f4f0565db2f100bjunov@google.com        // be baked into the blurred mask.
45daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com        SkColor opaqueColor = SkColorSetA(fBlurColor, 255);
464868e6b221a4a98e40f977851af5fcf09631ea15senorblanco@chromium.org        //The SrcIn xfer mode will multiply 'color' by the incoming alpha
474e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com        fColorFilter = SkColorFilter::CreateModeFilter(opaqueColor,
484e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com                                                       SkXfermode::kSrcIn_Mode);
494e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com    } else {
504868e6b221a4a98e40f977851af5fcf09631ea15senorblanco@chromium.org        fColorFilter = NULL;
514868e6b221a4a98e40f977851af5fcf09631ea15senorblanco@chromium.org    }
528a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
538a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
54daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.comvoid SkBlurDrawLooper::init(SkScalar sigma, SkScalar dx, SkScalar dy,
55daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com                            SkColor color, uint32_t flags) {
56daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com    fSigma = sigma;
57daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com    fDx = dx;
58daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com    fDy = dy;
59daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com    fBlurColor = color;
60daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com    fBlurFlags = flags;
61daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com
62daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com    this->initEffects();
63daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com}
64daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com
659fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed#ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
66daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.comSkBlurDrawLooper::SkBlurDrawLooper(SkReadBuffer& buffer) : INHERITED(buffer) {
676bac947cd5bc460dd9166ada6310d678fd2e39f8reed@google.com
68daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com    fSigma = buffer.readScalar();
698a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    fDx = buffer.readScalar();
708a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    fDy = buffer.readScalar();
71c73dd5c6880739f26216f198c757028fd28df1a4djsollen@google.com    fBlurColor = buffer.readColor();
72c73dd5c6880739f26216f198c757028fd28df1a4djsollen@google.com    fBlurFlags = buffer.readUInt() & kAll_BlurFlag;
738a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
74daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com    this->initEffects();
758a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
769fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed#endif
779fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed
789fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reedSkFlattenable* SkBlurDrawLooper::CreateProc(SkReadBuffer& buffer) {
799fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed    const SkColor color = buffer.readColor();
809fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed    const SkScalar sigma = buffer.readScalar();
819fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed    const SkScalar dx = buffer.readScalar();
829fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed    const SkScalar dy = buffer.readScalar();
839fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed    const uint32_t flags = buffer.read32();
849fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed    return Create(color, sigma, dx, dy, flags);
859fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed}
868a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
878b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.orgvoid SkBlurDrawLooper::flatten(SkWriteBuffer& buffer) const {
889fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed    buffer.writeColor(fBlurColor);
89daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com    buffer.writeScalar(fSigma);
908a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    buffer.writeScalar(fDx);
918a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    buffer.writeScalar(fDy);
92daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com    buffer.write32(fBlurFlags);
93daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com}
94daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com
95daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.comSkBlurDrawLooper::~SkBlurDrawLooper() {
96daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com    SkSafeUnref(fBlur);
97daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com    SkSafeUnref(fColorFilter);
988a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
998a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
100daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.combool SkBlurDrawLooper::asABlurShadow(BlurShadowRec* rec) const {
101daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com    if (fSigma <= 0 || (fBlurFlags & fBlurFlags & kIgnoreTransform_BlurFlag)) {
102daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com        return false;
103daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com    }
104daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com
105daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com    if (rec) {
106daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com        rec->fSigma = fSigma;
107daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com        rec->fColor = fBlurColor;
108daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com        rec->fOffset.set(fDx, fDy);
109daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com        rec->fStyle = kNormal_SkBlurStyle;
110daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com        rec->fQuality = (fBlurFlags & kHighQuality_BlurFlag) ?
111daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com                        kHigh_SkBlurQuality : kLow_SkBlurQuality;
112daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com    }
113daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com    return true;
114daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com}
115daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com
116daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com////////////////////////////////////////////////////////////////////////////////////////
117daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com
11879fbb40bca9d815ef79b896b31ba6ee736817e0fcommit-bot@chromium.orgSkDrawLooper::Context* SkBlurDrawLooper::createContext(SkCanvas*, void* storage) const {
11979fbb40bca9d815ef79b896b31ba6ee736817e0fcommit-bot@chromium.org    return SkNEW_PLACEMENT_ARGS(storage, BlurDrawLooperContext, (this));
1208a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
1218a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
12279fbb40bca9d815ef79b896b31ba6ee736817e0fcommit-bot@chromium.orgSkBlurDrawLooper::BlurDrawLooperContext::BlurDrawLooperContext(
12379fbb40bca9d815ef79b896b31ba6ee736817e0fcommit-bot@chromium.org        const SkBlurDrawLooper* looper)
12479fbb40bca9d815ef79b896b31ba6ee736817e0fcommit-bot@chromium.org    : fLooper(looper), fState(SkBlurDrawLooper::kBeforeEdge) {}
12579fbb40bca9d815ef79b896b31ba6ee736817e0fcommit-bot@chromium.org
12679fbb40bca9d815ef79b896b31ba6ee736817e0fcommit-bot@chromium.orgbool SkBlurDrawLooper::BlurDrawLooperContext::next(SkCanvas* canvas,
12779fbb40bca9d815ef79b896b31ba6ee736817e0fcommit-bot@chromium.org                                                   SkPaint* paint) {
1288a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    switch (fState) {
1294e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com        case kBeforeEdge:
1304e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com            // we do nothing if a maskfilter is already installed
1314e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com            if (paint->getMaskFilter()) {
1324e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com                fState = kDone;
1334e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com                return false;
1344e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com            }
13556c69773aea56c6c6bd47bc7e7970dd081205184djsollen@google.com#ifdef SK_BUILD_FOR_ANDROID
136f5dbe2f00f853c6a1719924bdd0c33335a53423adjsollen@google.com            SkColor blurColor;
13779fbb40bca9d815ef79b896b31ba6ee736817e0fcommit-bot@chromium.org            blurColor = fLooper->fBlurColor;
138f5dbe2f00f853c6a1719924bdd0c33335a53423adjsollen@google.com            if (SkColorGetA(blurColor) == 255) {
139f5dbe2f00f853c6a1719924bdd0c33335a53423adjsollen@google.com                blurColor = SkColorSetA(blurColor, paint->getAlpha());
140f5dbe2f00f853c6a1719924bdd0c33335a53423adjsollen@google.com            }
141f5dbe2f00f853c6a1719924bdd0c33335a53423adjsollen@google.com            paint->setColor(blurColor);
142f5dbe2f00f853c6a1719924bdd0c33335a53423adjsollen@google.com#else
14379fbb40bca9d815ef79b896b31ba6ee736817e0fcommit-bot@chromium.org            paint->setColor(fLooper->fBlurColor);
144f5dbe2f00f853c6a1719924bdd0c33335a53423adjsollen@google.com#endif
14579fbb40bca9d815ef79b896b31ba6ee736817e0fcommit-bot@chromium.org            paint->setMaskFilter(fLooper->fBlur);
14679fbb40bca9d815ef79b896b31ba6ee736817e0fcommit-bot@chromium.org            paint->setColorFilter(fLooper->fColorFilter);
14774e608cdc1e13593b54d54f11e65c1c309d19370commit-bot@chromium.org            canvas->save();
14879fbb40bca9d815ef79b896b31ba6ee736817e0fcommit-bot@chromium.org            if (fLooper->fBlurFlags & kIgnoreTransform_BlurFlag) {
1494e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com                SkMatrix transform(canvas->getTotalMatrix());
15079fbb40bca9d815ef79b896b31ba6ee736817e0fcommit-bot@chromium.org                transform.postTranslate(fLooper->fDx, fLooper->fDy);
1514e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com                canvas->setMatrix(transform);
1524e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com            } else {
15379fbb40bca9d815ef79b896b31ba6ee736817e0fcommit-bot@chromium.org                canvas->translate(fLooper->fDx, fLooper->fDy);
1544e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com            }
1554e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com            fState = kAfterEdge;
1564e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com            return true;
1574e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com        case kAfterEdge:
1584e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com            canvas->restore();
1594e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com            fState = kDone;
1604e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com            return true;
1614e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com        default:
1624e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com            SkASSERT(kDone == fState);
1634e2b3d3fb1288c6dc0f3ea1c0aa4a0d7c603bd7breed@google.com            return false;
1648a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
1658a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
1664991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com
1670f10f7bf1fb43ca6346dc220a076773b1f19a367commit-bot@chromium.org#ifndef SK_IGNORE_TO_STRING
1684991b8f23482afc1494fd17647421ce68de53331robertphillips@google.comvoid SkBlurDrawLooper::toString(SkString* str) const {
1694991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com    str->append("SkBlurDrawLooper: ");
1704991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com
1714991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com    str->append("dx: ");
1724991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com    str->appendScalar(fDx);
1734991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com
1744991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com    str->append(" dy: ");
1754991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com    str->appendScalar(fDy);
1764991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com
1774991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com    str->append(" color: ");
1784991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com    str->appendHex(fBlurColor);
1794991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com
1804991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com    str->append(" flags: (");
1814991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com    if (kNone_BlurFlag == fBlurFlags) {
1824991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com        str->append("None");
1834991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com    } else {
1844991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com        bool needsSeparator = false;
185cdcb2ce2744c7e5c47453328dbf292edee79ab37skia.committer@gmail.com        SkAddFlagToString(str, SkToBool(kIgnoreTransform_BlurFlag & fBlurFlags), "IgnoreTransform",
1864991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com                          &needsSeparator);
1874991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com        SkAddFlagToString(str, SkToBool(kOverrideColor_BlurFlag & fBlurFlags), "OverrideColor",
1884991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com                          &needsSeparator);
189cdcb2ce2744c7e5c47453328dbf292edee79ab37skia.committer@gmail.com        SkAddFlagToString(str, SkToBool(kHighQuality_BlurFlag & fBlurFlags), "HighQuality",
1904991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com                          &needsSeparator);
1914991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com    }
1924991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com    str->append(")");
1934991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com
1944991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com    // TODO: add optional "fBlurFilter->toString(str);" when SkMaskFilter::toString is added
1954991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com    // alternatively we could cache the radius in SkBlurDrawLooper and just add it here
1964991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com}
1974991b8f23482afc1494fd17647421ce68de53331robertphillips@google.com#endif
198