blob: 51c19b24a7f5ce861f1e5fd58462d80dca766137 [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001/* libs/graphics/effects/SkEmbossMaskFilter.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#include "SkEmbossMaskFilter.h"
19#include "SkBlurMaskFilter.h"
20#include "SkBlurMask.h"
21#include "SkEmbossMask.h"
22#include "SkBuffer.h"
23
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000024static inline int pin2byte(int n) {
25 if (n < 0) {
26 n = 0;
27 } else if (n > 0xFF) {
28 n = 0xFF;
29 }
30 return n;
31}
32
reed@android.com8a1c16f2008-12-17 15:59:43 +000033SkMaskFilter* SkBlurMaskFilter::CreateEmboss(const SkScalar direction[3],
34 SkScalar ambient, SkScalar specular,
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000035 SkScalar blurRadius) {
36 if (direction == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000037 return NULL;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000038 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000039
40 // ambient should be 0...1 as a scalar
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000041 int am = pin2byte(SkScalarToFixed(ambient) >> 8);
reed@android.com8a1c16f2008-12-17 15:59:43 +000042
43 // specular should be 0..15.99 as a scalar
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000044 int sp = pin2byte(SkScalarToFixed(specular) >> 12);
reed@android.com8a1c16f2008-12-17 15:59:43 +000045
46 SkEmbossMaskFilter::Light light;
47
48 memcpy(light.fDirection, direction, sizeof(light.fDirection));
49 light.fAmbient = SkToU8(am);
50 light.fSpecular = SkToU8(sp);
51
52 return SkNEW_ARGS(SkEmbossMaskFilter, (light, blurRadius));
53}
54
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000055///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000056
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000057static void normalize(SkScalar v[3]) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000058 SkScalar mag = SkScalarSquare(v[0]) + SkScalarSquare(v[1]) + SkScalarSquare(v[2]);
59 mag = SkScalarSqrt(mag);
60
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000061 for (int i = 0; i < 3; i++) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000062 v[i] = SkScalarDiv(v[i], mag);
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000063 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000064}
65
66SkEmbossMaskFilter::SkEmbossMaskFilter(const Light& light, SkScalar blurRadius)
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000067 : fLight(light), fBlurRadius(blurRadius) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000068 normalize(fLight.fDirection);
69}
70
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000071SkMask::Format SkEmbossMaskFilter::getFormat() {
reed@android.com8a1c16f2008-12-17 15:59:43 +000072 return SkMask::k3D_Format;
73}
74
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000075bool SkEmbossMaskFilter::filterMask(SkMask* dst, const SkMask& src,
76 const SkMatrix& matrix, SkIPoint* margin) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000077 SkScalar radius = matrix.mapRadius(fBlurRadius);
78
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000079 if (!SkBlurMask::Blur(dst, src, radius, SkBlurMask::kInner_Style,
80 SkBlurMask::kLow_Quality)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000081 return false;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000082 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000083
84 dst->fFormat = SkMask::k3D_Format;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000085 if (margin) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000086 margin->set(SkScalarCeil(radius), SkScalarCeil(radius));
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000087 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000088
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000089 if (src.fImage == NULL) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000090 return true;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +000091 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000092
93 // create a larger buffer for the other two channels (should force fBlur to do this for us)
94
95 {
96 uint8_t* alphaPlane = dst->fImage;
97 size_t planeSize = dst->computeImageSize();
reed@android.com543ed932009-04-24 12:43:40 +000098 if (0 == planeSize) {
99 return false; // too big to allocate, abort
100 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000101 dst->fImage = SkMask::AllocImage(planeSize * 3);
102 memcpy(dst->fImage, alphaPlane, planeSize);
103 SkMask::FreeImage(alphaPlane);
104 }
105
106 // run the light direction through the matrix...
107 Light light = fLight;
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000108 matrix.mapVectors((SkVector*)(void*)light.fDirection,
109 (SkVector*)(void*)fLight.fDirection, 1);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000110
111 // now restore the length of the XY component
112 // cast to SkVector so we can call setLength (this double cast silences alias warnings)
113 SkVector* vec = (SkVector*)(void*)light.fDirection;
114 vec->setLength(light.fDirection[0],
115 light.fDirection[1],
116 SkPoint::Length(fLight.fDirection[0], fLight.fDirection[1]));
117
118 SkEmbossMask::Emboss(dst, light);
119
120 // restore original alpha
121 memcpy(dst->fImage, src.fImage, src.computeImageSize());
122
123 return true;
124}
125
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000126SkFlattenable* SkEmbossMaskFilter::CreateProc(SkFlattenableReadBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127 return SkNEW_ARGS(SkEmbossMaskFilter, (buffer));
128}
129
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000130SkFlattenable::Factory SkEmbossMaskFilter::getFactory() {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131 return CreateProc;
132}
133
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000134SkEmbossMaskFilter::SkEmbossMaskFilter(SkFlattenableReadBuffer& buffer)
135 : SkMaskFilter(buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000136 buffer.read(&fLight, sizeof(fLight));
137 SkASSERT(fLight.fPad == 0); // for the font-cache lookup to be clean
138 fBlurRadius = buffer.readScalar();
139}
140
mike@reedtribe.org3334c3a2011-04-20 11:39:28 +0000141void SkEmbossMaskFilter::flatten(SkFlattenableWriteBuffer& buffer) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000142 this->INHERITED::flatten(buffer);
143
144 fLight.fPad = 0; // for the font-cache lookup to be clean
145 buffer.writeMul4(&fLight, sizeof(fLight));
146 buffer.writeScalar(fBlurRadius);
147}
148