SkMagnifierImageFilter.cpp revision 1a1aa9303484106a955e5549bf8ae24950f54e7a
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 "SkMagnifierImageFilter.h" 9 10#include "SkBitmap.h" 11#include "SkColorPriv.h" 12#include "SkReadBuffer.h" 13#include "SkSpecialImage.h" 14#include "SkWriteBuffer.h" 15#include "SkValidationUtils.h" 16 17//////////////////////////////////////////////////////////////////////////////// 18#if SK_SUPPORT_GPU 19#include "GrContext.h" 20#include "GrDrawContext.h" 21#include "GrInvariantOutput.h" 22#include "effects/GrSingleTextureEffect.h" 23#include "glsl/GrGLSLFragmentProcessor.h" 24#include "glsl/GrGLSLFragmentShaderBuilder.h" 25#include "glsl/GrGLSLProgramDataManager.h" 26#include "glsl/GrGLSLUniformHandler.h" 27 28class GrMagnifierEffect : public GrSingleTextureEffect { 29 30public: 31 static sk_sp<GrFragmentProcessor> Make(GrTexture* texture, 32 const SkRect& bounds, 33 float xOffset, 34 float yOffset, 35 float xInvZoom, 36 float yInvZoom, 37 float xInvInset, 38 float yInvInset) { 39 return sk_sp<GrFragmentProcessor>(new GrMagnifierEffect(texture, bounds, 40 xOffset, yOffset, 41 xInvZoom, yInvZoom, 42 xInvInset, yInvInset)); 43 } 44 45 ~GrMagnifierEffect() override {}; 46 47 const char* name() const override { return "Magnifier"; } 48 49 const SkRect& bounds() const { return fBounds; } // Bounds of source image. 50 // Offset to apply to zoomed pixels, (srcRect position / texture size). 51 float xOffset() const { return fXOffset; } 52 float yOffset() const { return fYOffset; } 53 54 // Scale to apply to zoomed pixels (srcRect size / bounds size). 55 float xInvZoom() const { return fXInvZoom; } 56 float yInvZoom() const { return fYInvZoom; } 57 58 // 1/radius over which to transition from unzoomed to zoomed pixels (bounds size / inset). 59 float xInvInset() const { return fXInvInset; } 60 float yInvInset() const { return fYInvInset; } 61 62private: 63 GrMagnifierEffect(GrTexture* texture, 64 const SkRect& bounds, 65 float xOffset, 66 float yOffset, 67 float xInvZoom, 68 float yInvZoom, 69 float xInvInset, 70 float yInvInset) 71 : INHERITED(texture, nullptr, GrCoordTransform::MakeDivByTextureWHMatrix(texture)) 72 , fBounds(bounds) 73 , fXOffset(xOffset) 74 , fYOffset(yOffset) 75 , fXInvZoom(xInvZoom) 76 , fYInvZoom(yInvZoom) 77 , fXInvInset(xInvInset) 78 , fYInvInset(yInvInset) { 79 this->initClassID<GrMagnifierEffect>(); 80 } 81 82 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; 83 84 void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override; 85 86 bool onIsEqual(const GrFragmentProcessor&) const override; 87 88 void onComputeInvariantOutput(GrInvariantOutput* inout) const override; 89 90 GR_DECLARE_FRAGMENT_PROCESSOR_TEST; 91 92 SkRect fBounds; 93 float fXOffset; 94 float fYOffset; 95 float fXInvZoom; 96 float fYInvZoom; 97 float fXInvInset; 98 float fYInvInset; 99 100 typedef GrSingleTextureEffect INHERITED; 101}; 102 103// For brevity 104typedef GrGLSLProgramDataManager::UniformHandle UniformHandle; 105 106class GrGLMagnifierEffect : public GrGLSLFragmentProcessor { 107public: 108 void emitCode(EmitArgs&) override; 109 110protected: 111 void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override; 112 113private: 114 UniformHandle fOffsetVar; 115 UniformHandle fInvZoomVar; 116 UniformHandle fInvInsetVar; 117 UniformHandle fBoundsVar; 118 119 typedef GrGLSLFragmentProcessor INHERITED; 120}; 121 122void GrGLMagnifierEffect::emitCode(EmitArgs& args) { 123 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; 124 fOffsetVar = uniformHandler->addUniform(kFragment_GrShaderFlag, 125 kVec2f_GrSLType, kDefault_GrSLPrecision, 126 "Offset"); 127 fInvZoomVar = uniformHandler->addUniform(kFragment_GrShaderFlag, 128 kVec2f_GrSLType, kDefault_GrSLPrecision, 129 "InvZoom"); 130 fInvInsetVar = uniformHandler->addUniform(kFragment_GrShaderFlag, 131 kVec2f_GrSLType, kDefault_GrSLPrecision, 132 "InvInset"); 133 fBoundsVar = uniformHandler->addUniform(kFragment_GrShaderFlag, 134 kVec4f_GrSLType, kDefault_GrSLPrecision, 135 "Bounds"); 136 137 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; 138 SkString coords2D = fragBuilder->ensureCoords2D(args.fTransformedCoords[0]); 139 fragBuilder->codeAppendf("\t\tvec2 coord = %s;\n", coords2D.c_str()); 140 fragBuilder->codeAppendf("\t\tvec2 zoom_coord = %s + %s * %s;\n", 141 uniformHandler->getUniformCStr(fOffsetVar), 142 coords2D.c_str(), 143 uniformHandler->getUniformCStr(fInvZoomVar)); 144 const char* bounds = uniformHandler->getUniformCStr(fBoundsVar); 145 fragBuilder->codeAppendf("\t\tvec2 delta = (coord - %s.xy) * %s.zw;\n", bounds, bounds); 146 fragBuilder->codeAppendf("\t\tdelta = min(delta, vec2(1.0, 1.0) - delta);\n"); 147 fragBuilder->codeAppendf("\t\tdelta = delta * %s;\n", 148 uniformHandler->getUniformCStr(fInvInsetVar)); 149 150 fragBuilder->codeAppend("\t\tfloat weight = 0.0;\n"); 151 fragBuilder->codeAppend("\t\tif (delta.s < 2.0 && delta.t < 2.0) {\n"); 152 fragBuilder->codeAppend("\t\t\tdelta = vec2(2.0, 2.0) - delta;\n"); 153 fragBuilder->codeAppend("\t\t\tfloat dist = length(delta);\n"); 154 fragBuilder->codeAppend("\t\t\tdist = max(2.0 - dist, 0.0);\n"); 155 fragBuilder->codeAppend("\t\t\tweight = min(dist * dist, 1.0);\n"); 156 fragBuilder->codeAppend("\t\t} else {\n"); 157 fragBuilder->codeAppend("\t\t\tvec2 delta_squared = delta * delta;\n"); 158 fragBuilder->codeAppend("\t\t\tweight = min(min(delta_squared.x, delta_squared.y), 1.0);\n"); 159 fragBuilder->codeAppend("\t\t}\n"); 160 161 fragBuilder->codeAppend("\t\tvec2 mix_coord = mix(coord, zoom_coord, weight);\n"); 162 fragBuilder->codeAppend("\t\tvec4 output_color = "); 163 fragBuilder->appendTextureLookup(args.fTexSamplers[0], "mix_coord"); 164 fragBuilder->codeAppend(";\n"); 165 166 fragBuilder->codeAppendf("\t\t%s = output_color;", args.fOutputColor); 167 SkString modulate; 168 GrGLSLMulVarBy4f(&modulate, args.fOutputColor, args.fInputColor); 169 fragBuilder->codeAppend(modulate.c_str()); 170} 171 172void GrGLMagnifierEffect::onSetData(const GrGLSLProgramDataManager& pdman, 173 const GrProcessor& effect) { 174 const GrMagnifierEffect& zoom = effect.cast<GrMagnifierEffect>(); 175 pdman.set2f(fOffsetVar, zoom.xOffset(), zoom.yOffset()); 176 pdman.set2f(fInvZoomVar, zoom.xInvZoom(), zoom.yInvZoom()); 177 pdman.set2f(fInvInsetVar, zoom.xInvInset(), zoom.yInvInset()); 178 pdman.set4f(fBoundsVar, zoom.bounds().x(), zoom.bounds().y(), 179 zoom.bounds().width(), zoom.bounds().height()); 180} 181 182///////////////////////////////////////////////////////////////////// 183 184void GrMagnifierEffect::onGetGLSLProcessorKey(const GrGLSLCaps& caps, 185 GrProcessorKeyBuilder* b) const { 186 GrGLMagnifierEffect::GenKey(*this, caps, b); 187} 188 189GrGLSLFragmentProcessor* GrMagnifierEffect::onCreateGLSLInstance() const { 190 return new GrGLMagnifierEffect; 191} 192 193GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrMagnifierEffect); 194 195sk_sp<GrFragmentProcessor> GrMagnifierEffect::TestCreate(GrProcessorTestData* d) { 196 GrTexture* texture = d->fTextures[0]; 197 const int kMaxWidth = 200; 198 const int kMaxHeight = 200; 199 const int kMaxInset = 20; 200 uint32_t width = d->fRandom->nextULessThan(kMaxWidth); 201 uint32_t height = d->fRandom->nextULessThan(kMaxHeight); 202 uint32_t x = d->fRandom->nextULessThan(kMaxWidth - width); 203 uint32_t y = d->fRandom->nextULessThan(kMaxHeight - height); 204 uint32_t inset = d->fRandom->nextULessThan(kMaxInset); 205 206 sk_sp<GrFragmentProcessor> effect(GrMagnifierEffect::Make( 207 texture, 208 SkRect::MakeWH(SkIntToScalar(kMaxWidth), SkIntToScalar(kMaxHeight)), 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->fBounds == s.fBounds && 224 this->fXOffset == s.fXOffset && 225 this->fYOffset == s.fYOffset && 226 this->fXInvZoom == s.fXInvZoom && 227 this->fYInvZoom == s.fYInvZoom && 228 this->fXInvInset == s.fXInvInset && 229 this->fYInvInset == s.fYInvInset); 230} 231 232void GrMagnifierEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const { 233 this->updateInvariantOutputForModulation(inout); 234} 235 236#endif 237 238//////////////////////////////////////////////////////////////////////////////// 239 240sk_sp<SkImageFilter> SkMagnifierImageFilter::Make(const SkRect& srcRect, SkScalar inset, 241 sk_sp<SkImageFilter> input, 242 const CropRect* cropRect) { 243 244 if (!SkScalarIsFinite(inset) || !SkIsValidRect(srcRect)) { 245 return nullptr; 246 } 247 // Negative numbers in src rect are not supported 248 if (srcRect.fLeft < 0 || srcRect.fTop < 0) { 249 return nullptr; 250 } 251 return sk_sp<SkImageFilter>(new SkMagnifierImageFilter(srcRect, inset, 252 std::move(input), 253 cropRect)); 254} 255 256 257SkMagnifierImageFilter::SkMagnifierImageFilter(const SkRect& srcRect, 258 SkScalar inset, 259 sk_sp<SkImageFilter> input, 260 const CropRect* cropRect) 261 : INHERITED(&input, 1, cropRect) 262 , fSrcRect(srcRect) 263 , fInset(inset) { 264 SkASSERT(srcRect.x() >= 0 && srcRect.y() >= 0 && inset >= 0); 265} 266 267sk_sp<SkFlattenable> SkMagnifierImageFilter::CreateProc(SkReadBuffer& buffer) { 268 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1); 269 SkRect src; 270 buffer.readRect(&src); 271 return Make(src, buffer.readScalar(), common.getInput(0), &common.cropRect()); 272} 273 274void SkMagnifierImageFilter::flatten(SkWriteBuffer& buffer) const { 275 this->INHERITED::flatten(buffer); 276 buffer.writeRect(fSrcRect); 277 buffer.writeScalar(fInset); 278} 279 280sk_sp<SkSpecialImage> SkMagnifierImageFilter::onFilterImage(SkSpecialImage* source, 281 const Context& ctx, 282 SkIPoint* offset) const { 283 SkIPoint inputOffset = SkIPoint::Make(0, 0); 284 sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset)); 285 if (!input) { 286 return nullptr; 287 } 288 289 const SkIRect inputBounds = SkIRect::MakeXYWH(inputOffset.x(), inputOffset.y(), 290 input->width(), input->height()); 291 292 SkIRect bounds; 293 if (!this->applyCropRect(ctx, inputBounds, &bounds)) { 294 return nullptr; 295 } 296 297 SkScalar invInset = fInset > 0 ? SkScalarInvert(fInset) : SK_Scalar1; 298 299 SkScalar invXZoom = fSrcRect.width() / bounds.width(); 300 SkScalar invYZoom = fSrcRect.height() / bounds.height(); 301 302 303#if SK_SUPPORT_GPU 304 if (source->isTextureBacked()) { 305 GrContext* context = source->getContext(); 306 307 sk_sp<GrTexture> inputTexture(input->asTextureRef(context)); 308 SkASSERT(inputTexture); 309 310 offset->fX = bounds.left(); 311 offset->fY = bounds.top(); 312 bounds.offset(-inputOffset); 313 314 SkScalar yOffset = inputTexture->origin() == kTopLeft_GrSurfaceOrigin 315 ? fSrcRect.y() 316 : inputTexture->height() - 317 fSrcRect.height() * inputTexture->height() / bounds.height() - fSrcRect.y(); 318 int boundsY = inputTexture->origin() == kTopLeft_GrSurfaceOrigin 319 ? bounds.y() 320 : inputTexture->height() - bounds.height(); 321 SkRect effectBounds = SkRect::MakeXYWH( 322 SkIntToScalar(bounds.x()) / inputTexture->width(), 323 SkIntToScalar(boundsY) / inputTexture->height(), 324 SkIntToScalar(inputTexture->width()) / bounds.width(), 325 SkIntToScalar(inputTexture->height()) / bounds.height()); 326 // SRGBTODO: Handle sRGB here 327 sk_sp<GrFragmentProcessor> fp(GrMagnifierEffect::Make( 328 inputTexture.get(), 329 effectBounds, 330 fSrcRect.x() / inputTexture->width(), 331 yOffset / inputTexture->height(), 332 invXZoom, 333 invYZoom, 334 bounds.width() * invInset, 335 bounds.height() * invInset)); 336 if (!fp) { 337 return nullptr; 338 } 339 340 return DrawWithFP(context, std::move(fp), bounds, sk_ref_sp(input->getColorSpace())); 341 } 342#endif 343 344 SkBitmap inputBM; 345 346 if (!input->getROPixels(&inputBM)) { 347 return nullptr; 348 } 349 350 if ((inputBM.colorType() != kN32_SkColorType) || 351 (fSrcRect.width() >= inputBM.width()) || (fSrcRect.height() >= inputBM.height())) { 352 return nullptr; 353 } 354 355 SkAutoLockPixels alp(inputBM); 356 SkASSERT(inputBM.getPixels()); 357 if (!inputBM.getPixels() || inputBM.width() <= 0 || inputBM.height() <= 0) { 358 return nullptr; 359 } 360 361 const SkImageInfo info = SkImageInfo::MakeN32Premul(bounds.width(), bounds.height()); 362 363 SkBitmap dst; 364 if (!dst.tryAllocPixels(info)) { 365 return nullptr; 366 } 367 368 SkAutoLockPixels dstLock(dst); 369 370 SkColor* dptr = dst.getAddr32(0, 0); 371 int dstWidth = dst.width(), dstHeight = dst.height(); 372 for (int y = 0; y < dstHeight; ++y) { 373 for (int x = 0; x < dstWidth; ++x) { 374 SkScalar x_dist = SkMin32(x, dstWidth - x - 1) * invInset; 375 SkScalar y_dist = SkMin32(y, dstHeight - y - 1) * invInset; 376 SkScalar weight = 0; 377 378 static const SkScalar kScalar2 = SkScalar(2); 379 380 // To create a smooth curve at the corners, we need to work on 381 // a square twice the size of the inset. 382 if (x_dist < kScalar2 && y_dist < kScalar2) { 383 x_dist = kScalar2 - x_dist; 384 y_dist = kScalar2 - y_dist; 385 386 SkScalar dist = SkScalarSqrt(SkScalarSquare(x_dist) + 387 SkScalarSquare(y_dist)); 388 dist = SkMaxScalar(kScalar2 - dist, 0); 389 weight = SkMinScalar(SkScalarSquare(dist), SK_Scalar1); 390 } else { 391 SkScalar sqDist = SkMinScalar(SkScalarSquare(x_dist), 392 SkScalarSquare(y_dist)); 393 weight = SkMinScalar(sqDist, SK_Scalar1); 394 } 395 396 SkScalar x_interp = SkScalarMul(weight, (fSrcRect.x() + x * invXZoom)) + 397 (SK_Scalar1 - weight) * x; 398 SkScalar y_interp = SkScalarMul(weight, (fSrcRect.y() + y * invYZoom)) + 399 (SK_Scalar1 - weight) * y; 400 401 int x_val = SkTPin(bounds.x() + SkScalarFloorToInt(x_interp), 0, inputBM.width() - 1); 402 int y_val = SkTPin(bounds.y() + SkScalarFloorToInt(y_interp), 0, inputBM.height() - 1); 403 404 *dptr = *inputBM.getAddr32(x_val, y_val); 405 dptr++; 406 } 407 } 408 409 offset->fX = bounds.left(); 410 offset->fY = bounds.top(); 411 return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()), 412 dst); 413} 414 415#ifndef SK_IGNORE_TO_STRING 416void SkMagnifierImageFilter::toString(SkString* str) const { 417 str->appendf("SkMagnifierImageFilter: ("); 418 str->appendf("src: (%f,%f,%f,%f) ", 419 fSrcRect.fLeft, fSrcRect.fTop, fSrcRect.fRight, fSrcRect.fBottom); 420 str->appendf("inset: %f", fInset); 421 str->append(")"); 422} 423#endif 424