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