1
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10#include "SkEmbossMaskFilter.h"
11#include "SkBlurMaskFilter.h"
12#include "SkBlurMask.h"
13#include "SkEmbossMask.h"
14#include "SkFlattenableBuffers.h"
15
16static inline int pin2byte(int n) {
17    if (n < 0) {
18        n = 0;
19    } else if (n > 0xFF) {
20        n = 0xFF;
21    }
22    return n;
23}
24
25SkMaskFilter* SkBlurMaskFilter::CreateEmboss(const SkScalar direction[3],
26                                             SkScalar ambient, SkScalar specular,
27                                             SkScalar blurRadius) {
28    if (direction == NULL) {
29        return NULL;
30    }
31
32    // ambient should be 0...1 as a scalar
33    int am = pin2byte(SkScalarToFixed(ambient) >> 8);
34
35    // specular should be 0..15.99 as a scalar
36    int sp = pin2byte(SkScalarToFixed(specular) >> 12);
37
38    SkEmbossMaskFilter::Light   light;
39
40    memcpy(light.fDirection, direction, sizeof(light.fDirection));
41    light.fAmbient = SkToU8(am);
42    light.fSpecular = SkToU8(sp);
43
44    return SkNEW_ARGS(SkEmbossMaskFilter, (light, blurRadius));
45}
46
47///////////////////////////////////////////////////////////////////////////////
48
49static void normalize(SkScalar v[3]) {
50    SkScalar mag = SkScalarSquare(v[0]) + SkScalarSquare(v[1]) + SkScalarSquare(v[2]);
51    mag = SkScalarSqrt(mag);
52
53    for (int i = 0; i < 3; i++) {
54        v[i] = SkScalarDiv(v[i], mag);
55    }
56}
57
58SkEmbossMaskFilter::SkEmbossMaskFilter(const Light& light, SkScalar blurRadius)
59        : fLight(light), fBlurRadius(blurRadius) {
60    normalize(fLight.fDirection);
61}
62
63SkMask::Format SkEmbossMaskFilter::getFormat() const {
64    return SkMask::k3D_Format;
65}
66
67bool SkEmbossMaskFilter::filterMask(SkMask* dst, const SkMask& src,
68                            const SkMatrix& matrix, SkIPoint* margin) const {
69    SkScalar radius = matrix.mapRadius(fBlurRadius);
70
71    if (!SkBlurMask::Blur(dst, src, radius, SkBlurMask::kInner_Style,
72                          SkBlurMask::kLow_Quality)) {
73        return false;
74    }
75
76    dst->fFormat = SkMask::k3D_Format;
77    if (margin) {
78        margin->set(SkScalarCeil(radius), SkScalarCeil(radius));
79    }
80
81    if (src.fImage == NULL) {
82        return true;
83    }
84
85    // create a larger buffer for the other two channels (should force fBlur to do this for us)
86
87    {
88        uint8_t* alphaPlane = dst->fImage;
89        size_t   planeSize = dst->computeImageSize();
90        if (0 == planeSize) {
91            return false;   // too big to allocate, abort
92        }
93        dst->fImage = SkMask::AllocImage(planeSize * 3);
94        memcpy(dst->fImage, alphaPlane, planeSize);
95        SkMask::FreeImage(alphaPlane);
96    }
97
98    // run the light direction through the matrix...
99    Light   light = fLight;
100    matrix.mapVectors((SkVector*)(void*)light.fDirection,
101                      (SkVector*)(void*)fLight.fDirection, 1);
102
103    // now restore the length of the XY component
104    // cast to SkVector so we can call setLength (this double cast silences alias warnings)
105    SkVector* vec = (SkVector*)(void*)light.fDirection;
106    vec->setLength(light.fDirection[0],
107                   light.fDirection[1],
108                   SkPoint::Length(fLight.fDirection[0], fLight.fDirection[1]));
109
110    SkEmbossMask::Emboss(dst, light);
111
112    // restore original alpha
113    memcpy(dst->fImage, src.fImage, src.computeImageSize());
114
115    return true;
116}
117
118SkEmbossMaskFilter::SkEmbossMaskFilter(SkFlattenableReadBuffer& buffer)
119        : SkMaskFilter(buffer) {
120    SkASSERT(buffer.getArrayCount() == sizeof(Light));
121    buffer.readByteArray(&fLight);
122    SkASSERT(fLight.fPad == 0); // for the font-cache lookup to be clean
123    fBlurRadius = buffer.readScalar();
124}
125
126void SkEmbossMaskFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
127    this->INHERITED::flatten(buffer);
128
129    Light tmpLight = fLight;
130    tmpLight.fPad = 0;    // for the font-cache lookup to be clean
131    buffer.writeByteArray(&tmpLight, sizeof(tmpLight));
132    buffer.writeScalar(fBlurRadius);
133}
134