SkMagnifierImageFilter.cpp revision 82973dbf4f22928e8dd75c8bc5b155f842c8a557
1/* 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 "SkReadBuffer.h" 12#include "SkWriteBuffer.h" 13#include "SkValidationUtils.h" 14 15//////////////////////////////////////////////////////////////////////////////// 16#if SK_SUPPORT_GPU 17#include "GrInvariantOutput.h" 18#include "effects/GrSingleTextureEffect.h" 19#include "gl/GrGLProcessor.h" 20#include "gl/GrGLSL.h" 21#include "gl/GrGLTexture.h" 22#include "gl/builders/GrGLProgramBuilder.h" 23 24class GrMagnifierEffect : public GrSingleTextureEffect { 25 26public: 27 static GrFragmentProcessor* Create(GrTexture* texture, 28 float xOffset, 29 float yOffset, 30 float xInvZoom, 31 float yInvZoom, 32 float xInvInset, 33 float yInvInset) { 34 return SkNEW_ARGS(GrMagnifierEffect, (texture, 35 xOffset, 36 yOffset, 37 xInvZoom, 38 yInvZoom, 39 xInvInset, 40 yInvInset)); 41 } 42 43 virtual ~GrMagnifierEffect() {}; 44 45 const char* name() const override { return "Magnifier"; } 46 47 void getGLProcessorKey(const GrGLCaps&, GrProcessorKeyBuilder*) const override; 48 49 GrGLFragmentProcessor* createGLInstance() const override; 50 51 float x_offset() const { return fXOffset; } 52 float y_offset() const { return fYOffset; } 53 float x_inv_zoom() const { return fXInvZoom; } 54 float y_inv_zoom() const { return fYInvZoom; } 55 float x_inv_inset() const { return fXInvInset; } 56 float y_inv_inset() const { return fYInvInset; } 57 58private: 59 GrMagnifierEffect(GrTexture* texture, 60 float xOffset, 61 float yOffset, 62 float xInvZoom, 63 float yInvZoom, 64 float xInvInset, 65 float yInvInset) 66 : GrSingleTextureEffect(texture, GrCoordTransform::MakeDivByTextureWHMatrix(texture)) 67 , fXOffset(xOffset) 68 , fYOffset(yOffset) 69 , fXInvZoom(xInvZoom) 70 , fYInvZoom(yInvZoom) 71 , fXInvInset(xInvInset) 72 , fYInvInset(yInvInset) { 73 this->initClassID<GrMagnifierEffect>(); 74 } 75 76 bool onIsEqual(const GrFragmentProcessor&) const override; 77 78 void onComputeInvariantOutput(GrInvariantOutput* inout) const override; 79 80 GR_DECLARE_FRAGMENT_PROCESSOR_TEST; 81 82 float fXOffset; 83 float fYOffset; 84 float fXInvZoom; 85 float fYInvZoom; 86 float fXInvInset; 87 float fYInvInset; 88 89 typedef GrSingleTextureEffect INHERITED; 90}; 91 92// For brevity 93typedef GrGLProgramDataManager::UniformHandle UniformHandle; 94 95class GrGLMagnifierEffect : public GrGLFragmentProcessor { 96public: 97 GrGLMagnifierEffect(const GrProcessor&); 98 99 virtual void emitCode(GrGLFPBuilder*, 100 const GrFragmentProcessor&, 101 const char* outputColor, 102 const char* inputColor, 103 const TransformedCoordsArray&, 104 const TextureSamplerArray&) override; 105 106 void setData(const GrGLProgramDataManager&, const GrProcessor&) override; 107 108private: 109 UniformHandle fOffsetVar; 110 UniformHandle fInvZoomVar; 111 UniformHandle fInvInsetVar; 112 113 typedef GrGLFragmentProcessor INHERITED; 114}; 115 116GrGLMagnifierEffect::GrGLMagnifierEffect(const GrProcessor&) { 117} 118 119void GrGLMagnifierEffect::emitCode(GrGLFPBuilder* builder, 120 const GrFragmentProcessor&, 121 const char* outputColor, 122 const char* inputColor, 123 const TransformedCoordsArray& coords, 124 const TextureSamplerArray& samplers) { 125 fOffsetVar = builder->addUniform( 126 GrGLProgramBuilder::kFragment_Visibility | 127 GrGLProgramBuilder::kVertex_Visibility, 128 kVec2f_GrSLType, kDefault_GrSLPrecision, "Offset"); 129 fInvZoomVar = builder->addUniform( 130 GrGLProgramBuilder::kFragment_Visibility | 131 GrGLProgramBuilder::kVertex_Visibility, 132 kVec2f_GrSLType, kDefault_GrSLPrecision, "InvZoom"); 133 fInvInsetVar = builder->addUniform( 134 GrGLProgramBuilder::kFragment_Visibility | 135 GrGLProgramBuilder::kVertex_Visibility, 136 kVec2f_GrSLType, kDefault_GrSLPrecision, "InvInset"); 137 138 GrGLFPFragmentBuilder* fsBuilder = builder->getFragmentShaderBuilder(); 139 SkString coords2D = fsBuilder->ensureFSCoords2D(coords, 0); 140 fsBuilder->codeAppendf("\t\tvec2 coord = %s;\n", coords2D.c_str()); 141 fsBuilder->codeAppendf("\t\tvec2 zoom_coord = %s + %s * %s;\n", 142 builder->getUniformCStr(fOffsetVar), 143 coords2D.c_str(), 144 builder->getUniformCStr(fInvZoomVar)); 145 146 fsBuilder->codeAppend("\t\tvec2 delta = min(coord, vec2(1.0, 1.0) - coord);\n"); 147 148 fsBuilder->codeAppendf("\t\tdelta = delta * %s;\n", builder->getUniformCStr(fInvInsetVar)); 149 150 fsBuilder->codeAppend("\t\tfloat weight = 0.0;\n"); 151 fsBuilder->codeAppend("\t\tif (delta.s < 2.0 && delta.t < 2.0) {\n"); 152 fsBuilder->codeAppend("\t\t\tdelta = vec2(2.0, 2.0) - delta;\n"); 153 fsBuilder->codeAppend("\t\t\tfloat dist = length(delta);\n"); 154 fsBuilder->codeAppend("\t\t\tdist = max(2.0 - dist, 0.0);\n"); 155 fsBuilder->codeAppend("\t\t\tweight = min(dist * dist, 1.0);\n"); 156 fsBuilder->codeAppend("\t\t} else {\n"); 157 fsBuilder->codeAppend("\t\t\tvec2 delta_squared = delta * delta;\n"); 158 fsBuilder->codeAppend("\t\t\tweight = min(min(delta_squared.x, delta_squared.y), 1.0);\n"); 159 fsBuilder->codeAppend("\t\t}\n"); 160 161 fsBuilder->codeAppend("\t\tvec2 mix_coord = mix(coord, zoom_coord, weight);\n"); 162 fsBuilder->codeAppend("\t\tvec4 output_color = "); 163 fsBuilder->appendTextureLookup(samplers[0], "mix_coord"); 164 fsBuilder->codeAppend(";\n"); 165 166 fsBuilder->codeAppendf("\t\t%s = output_color;", outputColor); 167 SkString modulate; 168 GrGLSLMulVarBy4f(&modulate, outputColor, inputColor); 169 fsBuilder->codeAppend(modulate.c_str()); 170} 171 172void GrGLMagnifierEffect::setData(const GrGLProgramDataManager& pdman, 173 const GrProcessor& effect) { 174 const GrMagnifierEffect& zoom = effect.cast<GrMagnifierEffect>(); 175 pdman.set2f(fOffsetVar, zoom.x_offset(), zoom.y_offset()); 176 pdman.set2f(fInvZoomVar, zoom.x_inv_zoom(), zoom.y_inv_zoom()); 177 pdman.set2f(fInvInsetVar, zoom.x_inv_inset(), zoom.y_inv_inset()); 178} 179 180///////////////////////////////////////////////////////////////////// 181 182void GrMagnifierEffect::getGLProcessorKey(const GrGLCaps& caps, 183 GrProcessorKeyBuilder* b) const { 184 GrGLMagnifierEffect::GenKey(*this, caps, b); 185} 186 187GrGLFragmentProcessor* GrMagnifierEffect::createGLInstance() const { 188 return SkNEW_ARGS(GrGLMagnifierEffect, (*this)); 189} 190 191GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrMagnifierEffect); 192 193GrFragmentProcessor* GrMagnifierEffect::TestCreate(SkRandom* random, 194 GrContext* context, 195 const GrDrawTargetCaps&, 196 GrTexture** textures) { 197 GrTexture* texture = textures[0]; 198 const int kMaxWidth = 200; 199 const int kMaxHeight = 200; 200 const int kMaxInset = 20; 201 uint32_t width = random->nextULessThan(kMaxWidth); 202 uint32_t height = random->nextULessThan(kMaxHeight); 203 uint32_t x = random->nextULessThan(kMaxWidth - width); 204 uint32_t y = random->nextULessThan(kMaxHeight - height); 205 uint32_t inset = random->nextULessThan(kMaxInset); 206 207 GrFragmentProcessor* effect = GrMagnifierEffect::Create( 208 texture, 209 (float) width / texture->width(), 210 (float) height / texture->height(), 211 texture->width() / (float) x, 212 texture->height() / (float) y, 213 (float) inset / texture->width(), 214 (float) inset / texture->height()); 215 SkASSERT(effect); 216 return effect; 217} 218 219/////////////////////////////////////////////////////////////////////////////// 220 221bool GrMagnifierEffect::onIsEqual(const GrFragmentProcessor& sBase) const { 222 const GrMagnifierEffect& s = sBase.cast<GrMagnifierEffect>(); 223 return (this->fXOffset == s.fXOffset && 224 this->fYOffset == s.fYOffset && 225 this->fXInvZoom == s.fXInvZoom && 226 this->fYInvZoom == s.fYInvZoom && 227 this->fXInvInset == s.fXInvInset && 228 this->fYInvInset == s.fYInvInset); 229} 230 231void GrMagnifierEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const { 232 this->updateInvariantOutputForModulation(inout); 233} 234 235#endif 236 237//////////////////////////////////////////////////////////////////////////////// 238 239SkImageFilter* SkMagnifierImageFilter::Create(const SkRect& srcRect, SkScalar inset, 240 SkImageFilter* input) { 241 242 if (!SkScalarIsFinite(inset) || !SkIsValidRect(srcRect)) { 243 return NULL; 244 } 245 // Negative numbers in src rect are not supported 246 if (srcRect.fLeft < 0 || srcRect.fTop < 0) { 247 return NULL; 248 } 249 return SkNEW_ARGS(SkMagnifierImageFilter, (srcRect, inset, input)); 250} 251 252 253SkMagnifierImageFilter::SkMagnifierImageFilter(const SkRect& srcRect, SkScalar inset, 254 SkImageFilter* input) 255 : INHERITED(1, &input), fSrcRect(srcRect), fInset(inset) { 256 SkASSERT(srcRect.x() >= 0 && srcRect.y() >= 0 && inset >= 0); 257} 258 259#if SK_SUPPORT_GPU 260bool SkMagnifierImageFilter::asFragmentProcessor(GrFragmentProcessor** fp, GrTexture* texture, 261 const SkMatrix&, const SkIRect&) const { 262 if (fp) { 263 SkScalar yOffset = (texture->origin() == kTopLeft_GrSurfaceOrigin) ? fSrcRect.y() : 264 (texture->height() - (fSrcRect.y() + fSrcRect.height())); 265 SkScalar invInset = fInset > 0 ? SkScalarInvert(fInset) : SK_Scalar1; 266 *fp = GrMagnifierEffect::Create(texture, 267 fSrcRect.x() / texture->width(), 268 yOffset / texture->height(), 269 fSrcRect.width() / texture->width(), 270 fSrcRect.height() / texture->height(), 271 texture->width() * invInset, 272 texture->height() * invInset); 273 } 274 return true; 275} 276#endif 277 278SkFlattenable* SkMagnifierImageFilter::CreateProc(SkReadBuffer& buffer) { 279 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1); 280 SkRect src; 281 buffer.readRect(&src); 282 return Create(src, buffer.readScalar(), common.getInput(0)); 283} 284 285void SkMagnifierImageFilter::flatten(SkWriteBuffer& buffer) const { 286 this->INHERITED::flatten(buffer); 287 buffer.writeRect(fSrcRect); 288 buffer.writeScalar(fInset); 289} 290 291bool SkMagnifierImageFilter::onFilterImage(Proxy*, const SkBitmap& src, 292 const Context&, SkBitmap* dst, 293 SkIPoint* offset) const { 294 if ((src.colorType() != kN32_SkColorType) || 295 (fSrcRect.width() >= src.width()) || 296 (fSrcRect.height() >= src.height())) { 297 return false; 298 } 299 300 SkAutoLockPixels alp(src); 301 SkASSERT(src.getPixels()); 302 if (!src.getPixels() || src.width() <= 0 || src.height() <= 0) { 303 return false; 304 } 305 306 if (!dst->tryAllocPixels(src.info())) { 307 return false; 308 } 309 310 SkScalar inv_inset = fInset > 0 ? SkScalarInvert(fInset) : SK_Scalar1; 311 312 SkScalar inv_x_zoom = fSrcRect.width() / src.width(); 313 SkScalar inv_y_zoom = fSrcRect.height() / src.height(); 314 315 SkColor* sptr = src.getAddr32(0, 0); 316 SkColor* dptr = dst->getAddr32(0, 0); 317 int width = src.width(), height = src.height(); 318 for (int y = 0; y < height; ++y) { 319 for (int x = 0; x < width; ++x) { 320 SkScalar x_dist = SkMin32(x, width - x - 1) * inv_inset; 321 SkScalar y_dist = SkMin32(y, height - y - 1) * inv_inset; 322 SkScalar weight = 0; 323 324 static const SkScalar kScalar2 = SkScalar(2); 325 326 // To create a smooth curve at the corners, we need to work on 327 // a square twice the size of the inset. 328 if (x_dist < kScalar2 && y_dist < kScalar2) { 329 x_dist = kScalar2 - x_dist; 330 y_dist = kScalar2 - y_dist; 331 332 SkScalar dist = SkScalarSqrt(SkScalarSquare(x_dist) + 333 SkScalarSquare(y_dist)); 334 dist = SkMaxScalar(kScalar2 - dist, 0); 335 weight = SkMinScalar(SkScalarSquare(dist), SK_Scalar1); 336 } else { 337 SkScalar sqDist = SkMinScalar(SkScalarSquare(x_dist), 338 SkScalarSquare(y_dist)); 339 weight = SkMinScalar(sqDist, SK_Scalar1); 340 } 341 342 SkScalar x_interp = SkScalarMul(weight, (fSrcRect.x() + x * inv_x_zoom)) + 343 (SK_Scalar1 - weight) * x; 344 SkScalar y_interp = SkScalarMul(weight, (fSrcRect.y() + y * inv_y_zoom)) + 345 (SK_Scalar1 - weight) * y; 346 347 int x_val = SkPin32(SkScalarFloorToInt(x_interp), 0, width - 1); 348 int y_val = SkPin32(SkScalarFloorToInt(y_interp), 0, height - 1); 349 350 *dptr = sptr[y_val * width + x_val]; 351 dptr++; 352 } 353 } 354 return true; 355} 356 357#ifndef SK_IGNORE_TO_STRING 358void SkMagnifierImageFilter::toString(SkString* str) const { 359 str->appendf("SkMagnifierImageFilter: ("); 360 str->appendf("src: (%f,%f,%f,%f) ", 361 fSrcRect.fLeft, fSrcRect.fTop, fSrcRect.fRight, fSrcRect.fBottom); 362 str->appendf("inset: %f", fInset); 363 str->append(")"); 364} 365#endif 366