1 /*
2 * Copyright 2006 The Android Open Source Project
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 "src/effects/SkEmbossMaskFilter.h"
9
10 #include "include/core/SkBlurTypes.h"
11 #include "include/core/SkMatrix.h"
12 #include "include/core/SkPoint.h"
13 #include "include/core/SkPoint3.h"
14 #include "include/core/SkTypes.h"
15 #include "include/private/base/SkFloatingPoint.h"
16 #include "src/core/SkBlurMask.h"
17 #include "src/core/SkReadBuffer.h"
18 #include "src/core/SkWriteBuffer.h"
19 #include "src/effects/SkEmbossMask.h"
20
21 #if defined(SK_SUPPORT_LEGACY_EMBOSSMASKFILTER)
22 #include "include/effects/SkBlurMaskFilter.h"
23 #endif
24
25 #include <cstring>
26
Make(SkScalar blurSigma,const Light & light)27 sk_sp<SkMaskFilter> SkEmbossMaskFilter::Make(SkScalar blurSigma, const Light& light) {
28 if (!SkIsFinite(blurSigma) || blurSigma <= 0) {
29 return nullptr;
30 }
31
32 SkPoint3 lightDir{light.fDirection[0], light.fDirection[1], light.fDirection[2]};
33 if (!lightDir.normalize()) {
34 return nullptr;
35 }
36 Light newLight = light;
37 newLight.fDirection[0] = lightDir.x();
38 newLight.fDirection[1] = lightDir.y();
39 newLight.fDirection[2] = lightDir.z();
40
41 return sk_sp<SkMaskFilter>(new SkEmbossMaskFilter(blurSigma, newLight));
42 }
43
44 #ifdef SK_SUPPORT_LEGACY_EMBOSSMASKFILTER
MakeEmboss(SkScalar blurSigma,const SkScalar direction[3],SkScalar ambient,SkScalar specular)45 sk_sp<SkMaskFilter> SkBlurMaskFilter::MakeEmboss(SkScalar blurSigma, const SkScalar direction[3],
46 SkScalar ambient, SkScalar specular) {
47 if (direction == nullptr) {
48 return nullptr;
49 }
50
51 SkEmbossMaskFilter::Light light;
52
53 memcpy(light.fDirection, direction, sizeof(light.fDirection));
54 // ambient should be 0...1 as a scalar
55 light.fAmbient = SkUnitScalarClampToByte(ambient);
56 // specular should be 0..15.99 as a scalar
57 static const SkScalar kSpecularMultiplier = SkIntToScalar(255) / 16;
58 light.fSpecular = static_cast<U8CPU>(SkTPin(specular, 0.0f, 16.0f) * kSpecularMultiplier + 0.5);
59
60 return SkEmbossMaskFilter::Make(blurSigma, light);
61 }
62 #endif
63
64 ///////////////////////////////////////////////////////////////////////////////
65
SkEmbossMaskFilter(SkScalar blurSigma,const Light & light)66 SkEmbossMaskFilter::SkEmbossMaskFilter(SkScalar blurSigma, const Light& light)
67 : fLight(light), fBlurSigma(blurSigma)
68 {
69 SkASSERT(fBlurSigma > 0);
70 SkASSERT(SkIsFinite(fLight.fDirection, 3));
71 }
72
getFormat() const73 SkMask::Format SkEmbossMaskFilter::getFormat() const {
74 return SkMask::k3D_Format;
75 }
76
filterMask(SkMaskBuilder * dst,const SkMask & src,const SkMatrix & matrix,SkIPoint * margin) const77 bool SkEmbossMaskFilter::filterMask(SkMaskBuilder* dst, const SkMask& src,
78 const SkMatrix& matrix, SkIPoint* margin) const {
79 if (src.fFormat != SkMask::kA8_Format) {
80 return false;
81 }
82
83 SkScalar sigma = matrix.mapRadius(fBlurSigma);
84
85 if (!SkBlurMask::BoxBlur(dst, src, sigma, kInner_SkBlurStyle)) {
86 return false;
87 }
88
89 dst->format() = SkMask::k3D_Format;
90 if (margin) {
91 margin->set(SkScalarCeilToInt(3*sigma), SkScalarCeilToInt(3*sigma));
92 }
93
94 if (src.fImage == nullptr) {
95 return true;
96 }
97
98 // create a larger buffer for the other two channels (should force fBlur to do this for us)
99
100 {
101 uint8_t* alphaPlane = dst->image();
102 size_t totalSize = dst->computeTotalImageSize();
103 if (totalSize == 0) {
104 return false; // too big to allocate, abort
105 }
106 size_t planeSize = dst->computeImageSize();
107 SkASSERT(planeSize != 0); // if totalSize didn't overflow, this can't either
108 dst->image() = SkMaskBuilder::AllocImage(totalSize);
109 memcpy(dst->image(), alphaPlane, planeSize);
110 SkMaskBuilder::FreeImage(alphaPlane);
111 }
112
113 // run the light direction through the matrix...
114 Light light = fLight;
115 matrix.mapVectors((SkVector*)(void*)light.fDirection,
116 (SkVector*)(void*)fLight.fDirection, 1);
117
118 // now restore the length of the XY component
119 // cast to SkVector so we can call setLength (this double cast silences alias warnings)
120 SkVector* vec = (SkVector*)(void*)light.fDirection;
121 vec->setLength(light.fDirection[0],
122 light.fDirection[1],
123 SkPoint::Length(fLight.fDirection[0], fLight.fDirection[1]));
124
125 SkEmbossMask::Emboss(dst, light);
126
127 // restore original alpha
128 memcpy(dst->image(), src.fImage, src.computeImageSize());
129
130 return true;
131 }
132
CreateProc(SkReadBuffer & buffer)133 sk_sp<SkFlattenable> SkEmbossMaskFilter::CreateProc(SkReadBuffer& buffer) {
134 Light light;
135 if (buffer.readByteArray(&light, sizeof(Light))) {
136 light.fPad = 0; // for the font-cache lookup to be clean
137 const SkScalar sigma = buffer.readScalar();
138 return Make(sigma, light);
139 }
140 return nullptr;
141 }
142
flatten(SkWriteBuffer & buffer) const143 void SkEmbossMaskFilter::flatten(SkWriteBuffer& buffer) const {
144 Light tmpLight = fLight;
145 tmpLight.fPad = 0; // for the font-cache lookup to be clean
146 buffer.writeByteArray(&tmpLight, sizeof(tmpLight));
147 buffer.writeScalar(fBlurSigma);
148 }
149