1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com/*
2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Copyright 2006 The Android Open Source Project
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 */
7ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com
88a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkEmbossMaskFilter.h"
98a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkBlurMaskFilter.h"
108a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkBlurMask.h"
118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkEmbossMask.h"
128b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.org#include "SkReadBuffer.h"
138b0e8ac5f582de80356019406e2975079bf0829dcommit-bot@chromium.org#include "SkWriteBuffer.h"
140bd80fa01bba2b3f0f49937fcb17928c74bde5a6robertphillips@google.com#include "SkString.h"
158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
16daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.comSkEmbossMaskFilter* SkEmbossMaskFilter::Create(SkScalar blurSigma, const Light& light) {
17daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com    return SkNEW_ARGS(SkEmbossMaskFilter, (blurSigma, light));
18daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com}
19daaafa6e81860e3dc52660ba019c336f0a43f1e7reed@google.com
203334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.orgstatic inline int pin2byte(int n) {
213334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org    if (n < 0) {
223334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org        n = 0;
233334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org    } else if (n > 0xFF) {
243334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org        n = 0xFF;
253334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org    }
263334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org    return n;
273334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org}
283334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org
298a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comSkMaskFilter* SkBlurMaskFilter::CreateEmboss(const SkScalar direction[3],
308a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                                             SkScalar ambient, SkScalar specular,
313334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org                                             SkScalar blurRadius) {
327ce661d19c5cf4484305a1b20c44bd111f129847robertphillips@google.com    return SkBlurMaskFilter::CreateEmboss(SkBlurMask::ConvertRadiusToSigma(blurRadius),
337ce661d19c5cf4484305a1b20c44bd111f129847robertphillips@google.com                                          direction, ambient, specular);
347ce661d19c5cf4484305a1b20c44bd111f129847robertphillips@google.com}
357ce661d19c5cf4484305a1b20c44bd111f129847robertphillips@google.com
367ce661d19c5cf4484305a1b20c44bd111f129847robertphillips@google.comSkMaskFilter* SkBlurMaskFilter::CreateEmboss(SkScalar blurSigma, const SkScalar direction[3],
377ce661d19c5cf4484305a1b20c44bd111f129847robertphillips@google.com                                             SkScalar ambient, SkScalar specular) {
383334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org    if (direction == NULL) {
398a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return NULL;
403334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org    }
418a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
428a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // ambient should be 0...1 as a scalar
433334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org    int am = pin2byte(SkScalarToFixed(ambient) >> 8);
448a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
458a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // specular should be 0..15.99 as a scalar
463334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org    int sp = pin2byte(SkScalarToFixed(specular) >> 12);
478a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
488a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkEmbossMaskFilter::Light   light;
49fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
508a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    memcpy(light.fDirection, direction, sizeof(light.fDirection));
518a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    light.fAmbient = SkToU8(am);
528a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    light.fSpecular = SkToU8(sp);
53fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
547c9d0f3104408a64d52b84643f116179022d73bdcommit-bot@chromium.org    return SkEmbossMaskFilter::Create(blurSigma, light);
558a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
568a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
573334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org///////////////////////////////////////////////////////////////////////////////
588a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
593334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.orgstatic void normalize(SkScalar v[3]) {
608a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkScalar mag = SkScalarSquare(v[0]) + SkScalarSquare(v[1]) + SkScalarSquare(v[2]);
618a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    mag = SkScalarSqrt(mag);
628a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
633334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org    for (int i = 0; i < 3; i++) {
648a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        v[i] = SkScalarDiv(v[i], mag);
653334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org    }
668a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
678a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
687ce661d19c5cf4484305a1b20c44bd111f129847robertphillips@google.comSkEmbossMaskFilter::SkEmbossMaskFilter(SkScalar blurSigma, const Light& light)
69b75233b7fabb818aea0f5c940848c0d6f9f6181erobertphillips@google.com    : fLight(light), fBlurSigma(blurSigma) {
707ce661d19c5cf4484305a1b20c44bd111f129847robertphillips@google.com    normalize(fLight.fDirection);
717ce661d19c5cf4484305a1b20c44bd111f129847robertphillips@google.com}
727ce661d19c5cf4484305a1b20c44bd111f129847robertphillips@google.com
7330711b764be6bbb58caa30a0ac5d1474c894efe7reed@google.comSkMask::Format SkEmbossMaskFilter::getFormat() const {
748a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    return SkMask::k3D_Format;
758a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
773334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.orgbool SkEmbossMaskFilter::filterMask(SkMask* dst, const SkMask& src,
787ce661d19c5cf4484305a1b20c44bd111f129847robertphillips@google.com                                    const SkMatrix& matrix, SkIPoint* margin) const {
797ce661d19c5cf4484305a1b20c44bd111f129847robertphillips@google.com    SkScalar sigma = matrix.mapRadius(fBlurSigma);
808a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
81e396455d2d60ddf8e625b5037254f3c09fbcdcf5commit-bot@chromium.org    if (!SkBlurMask::BoxBlur(dst, src, sigma, kInner_SkBlurStyle, kLow_SkBlurQuality)) {
828a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return false;
833334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org    }
848a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
858a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    dst->fFormat = SkMask::k3D_Format;
863334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org    if (margin) {
87e1ca705cac4b946993f6cbf798e2a0ba27e739f3reed@google.com        margin->set(SkScalarCeilToInt(3*sigma), SkScalarCeilToInt(3*sigma));
883334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org    }
898a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
903334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org    if (src.fImage == NULL) {
918a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return true;
923334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org    }
938a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
948a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // create a larger buffer for the other two channels (should force fBlur to do this for us)
958a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
968a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    {
978a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        uint8_t* alphaPlane = dst->fImage;
988a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        size_t   planeSize = dst->computeImageSize();
99543ed9352c7dfd93071c08b14930cca2e82a08d4reed@android.com        if (0 == planeSize) {
100543ed9352c7dfd93071c08b14930cca2e82a08d4reed@android.com            return false;   // too big to allocate, abort
101543ed9352c7dfd93071c08b14930cca2e82a08d4reed@android.com        }
1028a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        dst->fImage = SkMask::AllocImage(planeSize * 3);
1038a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        memcpy(dst->fImage, alphaPlane, planeSize);
1048a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        SkMask::FreeImage(alphaPlane);
1058a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
1068a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1078a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // run the light direction through the matrix...
1088a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    Light   light = fLight;
1093334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org    matrix.mapVectors((SkVector*)(void*)light.fDirection,
1103334c3a1fa05b5ee0cab0f2f1ec7b19939737337mike@reedtribe.org                      (SkVector*)(void*)fLight.fDirection, 1);
1118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1128a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // now restore the length of the XY component
1138a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // cast to SkVector so we can call setLength (this double cast silences alias warnings)
1148a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkVector* vec = (SkVector*)(void*)light.fDirection;
1158a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    vec->setLength(light.fDirection[0],
1168a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                   light.fDirection[1],
1178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                   SkPoint::Length(fLight.fDirection[0], fLight.fDirection[1]));
1188a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1198a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkEmbossMask::Emboss(dst, light);
1208a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1218a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // restore original alpha
1228a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    memcpy(dst->fImage, src.fImage, src.computeImageSize());
1238a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1248a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    return true;
1258a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
1268a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1279fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed#ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
1289fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reedSkEmbossMaskFilter::SkEmbossMaskFilter(SkReadBuffer& buffer) : SkMaskFilter(buffer) {
129c73dd5c6880739f26216f198c757028fd28df1a4djsollen@google.com    SkASSERT(buffer.getArrayCount() == sizeof(Light));
130025128811219dc45fd99b6c4d1d14f833cf7a26ecommit-bot@chromium.org    buffer.readByteArray(&fLight, sizeof(Light));
1318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkASSERT(fLight.fPad == 0); // for the font-cache lookup to be clean
132fed2ab648341ec153ad2af746a31d368963171e4commit-bot@chromium.org    fBlurSigma = buffer.readScalar();
1338a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
1349fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed#endif
1358a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1369fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reedSkFlattenable* SkEmbossMaskFilter::CreateProc(SkReadBuffer& buffer) {
1379fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed    Light light;
1389fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed    if (buffer.readByteArray(&light, sizeof(Light))) {
1399fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed        light.fPad = 0; // for the font-cache lookup to be clean
1409fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed        const SkScalar sigma = buffer.readScalar();
1419fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed        return Create(sigma, light);
1429fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed    }
1439fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed    return NULL;
1449fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reed}
1458a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1469fa60daad4d5f54c0dbe3dbcc7608a8f6d721187reedvoid SkEmbossMaskFilter::flatten(SkWriteBuffer& buffer) const {
14754924243c1b65b3ee6d8fa064b50a9b1bb2a19a5djsollen@google.com    Light tmpLight = fLight;
14854924243c1b65b3ee6d8fa064b50a9b1bb2a19a5djsollen@google.com    tmpLight.fPad = 0;    // for the font-cache lookup to be clean
149c73dd5c6880739f26216f198c757028fd28df1a4djsollen@google.com    buffer.writeByteArray(&tmpLight, sizeof(tmpLight));
15011e055518a0cbe5329232a55fe2cd177e83836d8robertphillips@google.com    buffer.writeScalar(fBlurSigma);
1518a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
1520bd80fa01bba2b3f0f49937fcb17928c74bde5a6robertphillips@google.com
1530f10f7bf1fb43ca6346dc220a076773b1f19a367commit-bot@chromium.org#ifndef SK_IGNORE_TO_STRING
1540bd80fa01bba2b3f0f49937fcb17928c74bde5a6robertphillips@google.comvoid SkEmbossMaskFilter::toString(SkString* str) const {
1550bd80fa01bba2b3f0f49937fcb17928c74bde5a6robertphillips@google.com    str->append("SkEmbossMaskFilter: (");
1560bd80fa01bba2b3f0f49937fcb17928c74bde5a6robertphillips@google.com
1570bd80fa01bba2b3f0f49937fcb17928c74bde5a6robertphillips@google.com    str->append("direction: (");
1580bd80fa01bba2b3f0f49937fcb17928c74bde5a6robertphillips@google.com    str->appendScalar(fLight.fDirection[0]);
1590bd80fa01bba2b3f0f49937fcb17928c74bde5a6robertphillips@google.com    str->append(", ");
1600bd80fa01bba2b3f0f49937fcb17928c74bde5a6robertphillips@google.com    str->appendScalar(fLight.fDirection[1]);
1610bd80fa01bba2b3f0f49937fcb17928c74bde5a6robertphillips@google.com    str->append(", ");
1620bd80fa01bba2b3f0f49937fcb17928c74bde5a6robertphillips@google.com    str->appendScalar(fLight.fDirection[2]);
1630bd80fa01bba2b3f0f49937fcb17928c74bde5a6robertphillips@google.com    str->append(") ");
1640bd80fa01bba2b3f0f49937fcb17928c74bde5a6robertphillips@google.com
1658eaddb0089a170760e157646192813bd940c26e7skia.committer@gmail.com    str->appendf("ambient: %d specular: %d ",
1660bd80fa01bba2b3f0f49937fcb17928c74bde5a6robertphillips@google.com        fLight.fAmbient, fLight.fSpecular);
1670bd80fa01bba2b3f0f49937fcb17928c74bde5a6robertphillips@google.com
1687ce661d19c5cf4484305a1b20c44bd111f129847robertphillips@google.com    str->append("blurSigma: ");
1697ce661d19c5cf4484305a1b20c44bd111f129847robertphillips@google.com    str->appendScalar(fBlurSigma);
1700bd80fa01bba2b3f0f49937fcb17928c74bde5a6robertphillips@google.com    str->append(")");
1710bd80fa01bba2b3f0f49937fcb17928c74bde5a6robertphillips@google.com}
1720bd80fa01bba2b3f0f49937fcb17928c74bde5a6robertphillips@google.com#endif
173