blob: dbd9135891592cca8470b129c453721722c6f655 [file] [log] [blame]
bsalomon@google.com82aa7482012-08-13 14:22:17 +00001/*
2 * Copyright 2012 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 "SkBitmap.h"
9#include "SkMagnifierImageFilter.h"
10#include "SkColorPriv.h"
11#include "SkFlattenableBuffers.h"
12#include "gl/GrGLTexture.h"
13
14#include <algorithm>
15
16////////////////////////////////////////////////////////////////////////////////
17#include "effects/GrSingleTextureEffect.h"
18#include "gl/GrGLProgramStage.h"
19#include "gl/GrGLSL.h"
20#include "gl/GrGLTexture.h"
21#include "GrProgramStageFactory.h"
22
23class GrGLMagnifierEffect;
24
25class GrMagnifierEffect : public GrSingleTextureEffect {
26
27public:
28 GrMagnifierEffect(GrTexture* texture,
29 float xOffset,
30 float yOffset,
31 float xZoom,
32 float yZoom,
33 float xInset,
34 float yInset)
35 : GrSingleTextureEffect(texture)
36 , fXOffset(xOffset)
37 , fYOffset(yOffset)
38 , fXZoom(xZoom)
39 , fYZoom(yZoom)
40 , fXInset(xInset)
41 , fYInset(yInset) {}
42
43 virtual ~GrMagnifierEffect() {};
44
45 static const char* Name() { return "Magnifier"; }
46
47 virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE;
48 virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE;
49
50 float x_offset() const { return fXOffset; }
51 float y_offset() const { return fYOffset; }
52 float x_zoom() const { return fXZoom; }
53 float y_zoom() const { return fYZoom; }
54 float x_inset() const { return fXInset; }
55 float y_inset() const { return fYInset; }
56
57 typedef GrGLMagnifierEffect GLProgramStage;
58
59private:
60 GR_DECLARE_CUSTOM_STAGE_TEST;
61
62 float fXOffset;
63 float fYOffset;
64 float fXZoom;
65 float fYZoom;
66 float fXInset;
67 float fYInset;
68
69 typedef GrSingleTextureEffect INHERITED;
70};
71
72// For brevity
73typedef GrGLUniformManager::UniformHandle UniformHandle;
74
75class GrGLMagnifierEffect : public GrGLProgramStage {
76public:
77 GrGLMagnifierEffect(const GrProgramStageFactory& factory,
78 const GrCustomStage& stage);
79
80 virtual void setupVariables(GrGLShaderBuilder* state) SK_OVERRIDE;
81 virtual void emitVS(GrGLShaderBuilder* state,
82 const char* vertexCoords) SK_OVERRIDE;
83 virtual void emitFS(GrGLShaderBuilder* state,
84 const char* outputColor,
85 const char* inputColor,
86 const char* samplerName) SK_OVERRIDE;
87
88 virtual void setData(const GrGLUniformManager& uman,
89 const GrCustomStage& data,
90 const GrRenderTarget*,
91 int stageNum) SK_OVERRIDE;
92
93 static inline StageKey GenKey(const GrCustomStage&, const GrGLCaps&);
94
95private:
96
97 UniformHandle fOffsetVar;
98 UniformHandle fZoomVar;
99 UniformHandle fInsetVar;
100
101 typedef GrGLProgramStage INHERITED;
102};
103
104GrGLMagnifierEffect::GrGLMagnifierEffect(const GrProgramStageFactory& factory,
105 const GrCustomStage& stage)
106 : GrGLProgramStage(factory)
107 , fOffsetVar(GrGLUniformManager::kInvalidUniformHandle)
108 , fZoomVar(GrGLUniformManager::kInvalidUniformHandle)
109 , fInsetVar(GrGLUniformManager::kInvalidUniformHandle) {
110}
111
112void GrGLMagnifierEffect::setupVariables(GrGLShaderBuilder* state) {
113 fOffsetVar = state->addUniform(
114 GrGLShaderBuilder::kFragment_ShaderType |
115 GrGLShaderBuilder::kVertex_ShaderType,
116 kVec2f_GrSLType, "uOffset");
117 fZoomVar = state->addUniform(
118 GrGLShaderBuilder::kFragment_ShaderType |
119 GrGLShaderBuilder::kVertex_ShaderType,
120 kVec2f_GrSLType, "uZoom");
121 fInsetVar = state->addUniform(
122 GrGLShaderBuilder::kFragment_ShaderType |
123 GrGLShaderBuilder::kVertex_ShaderType,
124 kVec2f_GrSLType, "uInset");
125}
126
127void GrGLMagnifierEffect::emitVS(GrGLShaderBuilder* state,
128 const char* vertexCoords) {
129}
130
131void GrGLMagnifierEffect::emitFS(GrGLShaderBuilder* state,
132 const char* outputColor,
133 const char* inputColor,
134 const char* samplerName) {
135 SkString* code = &state->fFSCode;
136
137 code->appendf("\t\tvec2 coord = %s;\n", state->fSampleCoords.c_str());
138 code->appendf("\t\tvec2 zoom_coord = %s + %s / %s;\n",
139 state->getUniformCStr(fOffsetVar),
140 state->fSampleCoords.c_str(),
141 state->getUniformCStr(fZoomVar));
142
143 code->appendf("\t\tvec2 delta = min(coord, vec2(1.0, 1.0) - coord);\n");
144
145 code->appendf(
146 "\t\tdelta = delta / %s;\n", state->getUniformCStr(fInsetVar));
147
148 code->appendf("\t\tfloat weight = 0.0;\n");
149 code->appendf("\t\tif (delta.s < 2.0 && delta.t < 2.0) {\n");
150 code->appendf("\t\t\tdelta = vec2(2.0, 2.0) - delta;\n");
151 code->appendf("\t\t\tfloat dist = length(delta);\n");
152 code->appendf("\t\t\tdist = max(2.0 - dist, 0.0);\n");
153 code->appendf("\t\t\tweight = min(dist * dist, 1.0);\n");
154 code->appendf("\t\t} else {\n");
155 code->appendf("\t\t\tvec2 delta_squared = delta * delta;\n");
156 code->appendf(
157 "\t\t\tweight = min(min(delta_squared.s, delta_squared.y), 1.0);\n");
158 code->appendf("\t\t}\n");
159
160 code->appendf("\t\tvec2 mix_coord = mix(coord, zoom_coord, weight);\n");
161 code->appendf("\t\tvec4 output_color = ");
162 state->emitTextureLookup(samplerName, "mix_coord");
163 code->appendf(";\n");
164
165 code->appendf("\t\t%s = output_color;", outputColor);
166}
167
168void GrGLMagnifierEffect::setData(const GrGLUniformManager& uman,
169 const GrCustomStage& data,
170 const GrRenderTarget*,
171 int stageNum) {
172 const GrMagnifierEffect& zoom =
173 static_cast<const GrMagnifierEffect&>(data);
174
175 uman.set2f(fOffsetVar, zoom.x_offset(), zoom.y_offset());
176 uman.set2f(fZoomVar, zoom.x_zoom(), zoom.y_zoom());
177 uman.set2f(fInsetVar, zoom.x_inset(), zoom.y_inset());
178}
179
180GrGLProgramStage::StageKey GrGLMagnifierEffect::GenKey(const GrCustomStage& s,
181 const GrGLCaps& caps) {
182 return 0;
183}
184
185/////////////////////////////////////////////////////////////////////
186
187GR_DEFINE_CUSTOM_STAGE_TEST(GrMagnifierEffect);
188
189GrCustomStage* GrMagnifierEffect::TestCreate(SkRandom* random,
190 GrContext* context,
191 GrTexture** textures) {
192 const int kMaxWidth = 200;
193 const int kMaxHeight = 200;
194 const int kMaxInset = 20;
195 SkScalar width = random->nextULessThan(kMaxWidth);
196 SkScalar height = random->nextULessThan(kMaxHeight);
197 SkScalar x = random->nextULessThan(kMaxWidth - width);
198 SkScalar y = random->nextULessThan(kMaxHeight - height);
199 SkScalar inset = random->nextULessThan(kMaxInset);
200
201 SkAutoTUnref<SkImageFilter> filter(
202 new SkMagnifierImageFilter(
203 SkRect::MakeXYWH(x, y, width, height),
204 inset));
205 GrSamplerState sampler;
206 GrCustomStage* stage;
207 filter->asNewCustomStage(&stage, textures[0]);
208 GrAssert(NULL != stage);
209 return stage;
210}
211
212///////////////////////////////////////////////////////////////////////////////
213
214const GrProgramStageFactory& GrMagnifierEffect::getFactory() const {
215 return GrTProgramStageFactory<GrMagnifierEffect>::getInstance();
216}
217
218bool GrMagnifierEffect::isEqual(const GrCustomStage& sBase) const {
219 const GrMagnifierEffect& s =
220 static_cast<const GrMagnifierEffect&>(sBase);
221 return (this->fXOffset == s.fXOffset &&
222 this->fYOffset == s.fYOffset &&
223 this->fXZoom == s.fXZoom &&
224 this->fYZoom == s.fYZoom &&
225 this->fXInset == s.fXInset &&
226 this->fYInset == s.fYInset);
227}
228
229////////////////////////////////////////////////////////////////////////////////
230SkMagnifierImageFilter::SkMagnifierImageFilter(SkFlattenableReadBuffer& buffer)
231 : INHERITED(buffer) {
232 float x = buffer.readScalar();
233 float y = buffer.readScalar();
234 float width = buffer.readScalar();
235 float height = buffer.readScalar();
236 fSrcRect = SkRect::MakeXYWH(x, y, width, height);
237 fInset = buffer.readScalar();
238}
239
240SkMagnifierImageFilter::SkMagnifierImageFilter(SkRect srcRect, SkScalar inset)
241 : fSrcRect(srcRect), fInset(inset) {
242 SkASSERT(srcRect.x() >= 0 && srcRect.y() >= 0 && inset >= 0);
243}
244
245bool SkMagnifierImageFilter::asNewCustomStage(GrCustomStage** stage,
246 GrTexture* texture) const {
247 if (stage) {
248 *stage =
249 SkNEW_ARGS(GrMagnifierEffect, (texture,
250 fSrcRect.x() / texture->width(),
251 fSrcRect.y() / texture->height(),
252 texture->width() / fSrcRect.width(),
253 texture->height() / fSrcRect.height(),
254 fInset / texture->width(),
255 fInset / texture->height()));
256 }
257 return true;
258}
259
260void SkMagnifierImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
261 this->INHERITED::flatten(buffer);
262 buffer.writeScalar(fSrcRect.x());
263 buffer.writeScalar(fSrcRect.y());
264 buffer.writeScalar(fSrcRect.width());
265 buffer.writeScalar(fSrcRect.height());
266 buffer.writeScalar(fInset);
267}
268
269bool SkMagnifierImageFilter::onFilterImage(Proxy*, const SkBitmap& src,
270 const SkMatrix&, SkBitmap* dst,
271 SkIPoint* offset) {
272 SkASSERT(src.config() == SkBitmap::kARGB_8888_Config);
273 SkASSERT(fSrcRect.width() < src.width());
274 SkASSERT(fSrcRect.height() < src.height());
275
276 if (src.config() != SkBitmap::kARGB_8888_Config) {
277 return false;
278 }
279
280 SkAutoLockPixels alp(src);
281 SkASSERT(src.getPixels());
282 if (!src.getPixels() || src.width() <= 0 || src.height() <= 0) {
283 return false;
284 }
285
286 float inv_inset = fInset > 0 ? 1.0f / SkScalarToFloat(fInset) : 1.0f;
287
288 float inv_x_zoom = fSrcRect.width() / src.width();
289 float inv_y_zoom = fSrcRect.height() / src.height();
290
291 dst->setConfig(src.config(), src.width(), src.height());
292 dst->allocPixels();
293 SkColor* sptr = src.getAddr32(0, 0);
294 SkColor* dptr = dst->getAddr32(0, 0);
295 int width = src.width(), height = src.height();
296 for (int y = 0; y < height; ++y) {
297 for (int x = 0; x < width; ++x) {
298 float x_dist = std::min(x, width - x - 1) * inv_inset;
299 float y_dist = std::min(y, height - y - 1) * inv_inset;
300 float weight = 0;
301
302 // To create a smooth curve at the corners, we need to work on
303 // a square twice the size of the inset.
304 if (x_dist < 2 && y_dist < 2) {
305 x_dist = 2 - x_dist;
306 y_dist = 2 - y_dist;
307
308 float dist = sqrt(x_dist * x_dist + y_dist * y_dist);
309 dist = std::max(2 - dist, 0.0f);
310 weight = std::min(dist * dist, 1.0f);
311 } else {
312 float sq_dist = std::min(x_dist * x_dist, y_dist * y_dist);
313 weight = std::min(sq_dist, 1.0f);
314 }
315
316 int x_val = weight * (fSrcRect.x() + x * inv_x_zoom) +
317 (1 - weight) * x;
318 int y_val = weight * (fSrcRect.y() + y * inv_y_zoom) +
319 (1 - weight) * y;
320
321 x_val = std::min(x_val, width - 1);
322 y_val = std::min(y_val, height - 1);
323
324 *dptr = sptr[y_val * width + x_val];
325 dptr++;
326 }
327 }
328 return true;
329}
330
331SK_DEFINE_FLATTENABLE_REGISTRAR(SkMagnifierImageFilter)