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 "SkImageFilter.h" 9 10 #include "SkCanvas.h" 11 #include "SkColorSpace_Base.h" 12 #include "SkFuzzLogging.h" 13 #include "SkImageFilterCache.h" 14 #include "SkLocalMatrixImageFilter.h" 15 #include "SkMatrixImageFilter.h" 16 #include "SkReadBuffer.h" 17 #include "SkRect.h" 18 #include "SkSpecialImage.h" 19 #include "SkSpecialSurface.h" 20 #include "SkValidationUtils.h" 21 #include "SkWriteBuffer.h" 22 #if SK_SUPPORT_GPU 23 #include "GrContext.h" 24 #include "GrFixedClip.h" 25 #include "GrRenderTargetContext.h" 26 #include "GrTextureProxy.h" 27 #include "SkGr.h" 28 #endif 29 30 #ifndef SK_IGNORE_TO_STRING 31 void SkImageFilter::CropRect::toString(SkString* str) const { 32 if (!fFlags) { 33 return; 34 } 35 36 str->appendf("cropRect ("); 37 if (fFlags & CropRect::kHasLeft_CropEdge) { 38 str->appendf("%.2f, ", fRect.fLeft); 39 } else { 40 str->appendf("X, "); 41 } 42 if (fFlags & CropRect::kHasTop_CropEdge) { 43 str->appendf("%.2f, ", fRect.fTop); 44 } else { 45 str->appendf("X, "); 46 } 47 if (fFlags & CropRect::kHasWidth_CropEdge) { 48 str->appendf("%.2f, ", fRect.width()); 49 } else { 50 str->appendf("X, "); 51 } 52 if (fFlags & CropRect::kHasHeight_CropEdge) { 53 str->appendf("%.2f", fRect.height()); 54 } else { 55 str->appendf("X"); 56 } 57 str->appendf(") "); 58 } 59 #endif 60 61 void SkImageFilter::CropRect::applyTo(const SkIRect& imageBounds, 62 const SkMatrix& ctm, 63 bool embiggen, 64 SkIRect* cropped) const { 65 *cropped = imageBounds; 66 if (fFlags) { 67 SkRect devCropR; 68 ctm.mapRect(&devCropR, fRect); 69 SkIRect devICropR = devCropR.roundOut(); 70 71 // Compute the left/top first, in case we need to modify the right/bottom for a missing edge 72 if (fFlags & kHasLeft_CropEdge) { 73 if (embiggen || devICropR.fLeft > cropped->fLeft) { 74 cropped->fLeft = devICropR.fLeft; 75 } 76 } else { 77 devICropR.fRight = cropped->fLeft + devICropR.width(); 78 } 79 if (fFlags & kHasTop_CropEdge) { 80 if (embiggen || devICropR.fTop > cropped->fTop) { 81 cropped->fTop = devICropR.fTop; 82 } 83 } else { 84 devICropR.fBottom = cropped->fTop + devICropR.height(); 85 } 86 if (fFlags & kHasWidth_CropEdge) { 87 if (embiggen || devICropR.fRight < cropped->fRight) { 88 cropped->fRight = devICropR.fRight; 89 } 90 } 91 if (fFlags & kHasHeight_CropEdge) { 92 if (embiggen || devICropR.fBottom < cropped->fBottom) { 93 cropped->fBottom = devICropR.fBottom; 94 } 95 } 96 } 97 } 98 99 /////////////////////////////////////////////////////////////////////////////////////////////////// 100 101 static int32_t next_image_filter_unique_id() { 102 static int32_t gImageFilterUniqueID; 103 104 // Never return 0. 105 int32_t id; 106 do { 107 id = sk_atomic_inc(&gImageFilterUniqueID) + 1; 108 } while (0 == id); 109 return id; 110 } 111 112 void SkImageFilter::Common::allocInputs(int count) { 113 fInputs.reset(count); 114 } 115 116 bool SkImageFilter::Common::unflatten(SkReadBuffer& buffer, int expectedCount) { 117 const int count = buffer.readInt(); 118 if (!buffer.validate(count >= 0)) { 119 return false; 120 } 121 if (!buffer.validate(expectedCount < 0 || count == expectedCount)) { 122 return false; 123 } 124 125 SkFUZZF(("allocInputs: %d\n", count)); 126 this->allocInputs(count); 127 for (int i = 0; i < count; i++) { 128 if (buffer.readBool()) { 129 fInputs[i] = sk_sp<SkImageFilter>(buffer.readImageFilter()); 130 } 131 if (!buffer.isValid()) { 132 return false; 133 } 134 } 135 SkRect rect; 136 buffer.readRect(&rect); 137 if (!buffer.isValid() || !buffer.validate(SkIsValidRect(rect))) { 138 return false; 139 } 140 141 uint32_t flags = buffer.readUInt(); 142 fCropRect = CropRect(rect, flags); 143 return buffer.isValid(); 144 } 145 146 /////////////////////////////////////////////////////////////////////////////////////////////////// 147 148 void SkImageFilter::init(sk_sp<SkImageFilter> const* inputs, 149 int inputCount, 150 const CropRect* cropRect) { 151 fCropRect = cropRect ? *cropRect : CropRect(SkRect(), 0x0); 152 153 fInputs.reset(inputCount); 154 155 for (int i = 0; i < inputCount; ++i) { 156 if (!inputs[i] || inputs[i]->usesSrcInput()) { 157 fUsesSrcInput = true; 158 } 159 fInputs[i] = inputs[i]; 160 } 161 } 162 163 SkImageFilter::SkImageFilter(sk_sp<SkImageFilter> const* inputs, 164 int inputCount, 165 const CropRect* cropRect) 166 : fUsesSrcInput(false) 167 , fUniqueID(next_image_filter_unique_id()) { 168 this->init(inputs, inputCount, cropRect); 169 } 170 171 SkImageFilter::~SkImageFilter() { 172 SkImageFilterCache::Get()->purgeByKeys(fCacheKeys.begin(), fCacheKeys.count()); 173 } 174 175 SkImageFilter::SkImageFilter(int inputCount, SkReadBuffer& buffer) 176 : fUsesSrcInput(false) 177 , fCropRect(SkRect(), 0x0) 178 , fUniqueID(next_image_filter_unique_id()) { 179 Common common; 180 if (common.unflatten(buffer, inputCount)) { 181 this->init(common.inputs(), common.inputCount(), &common.cropRect()); 182 } 183 } 184 185 void SkImageFilter::flatten(SkWriteBuffer& buffer) const { 186 buffer.writeInt(fInputs.count()); 187 for (int i = 0; i < fInputs.count(); i++) { 188 SkImageFilter* input = this->getInput(i); 189 buffer.writeBool(input != nullptr); 190 if (input != nullptr) { 191 buffer.writeFlattenable(input); 192 } 193 } 194 buffer.writeRect(fCropRect.rect()); 195 buffer.writeUInt(fCropRect.flags()); 196 } 197 198 sk_sp<SkSpecialImage> SkImageFilter::filterImage(SkSpecialImage* src, const Context& context, 199 SkIPoint* offset) const { 200 SkASSERT(src && offset); 201 202 uint32_t srcGenID = fUsesSrcInput ? src->uniqueID() : 0; 203 const SkIRect srcSubset = fUsesSrcInput ? src->subset() : SkIRect::MakeWH(0, 0); 204 SkImageFilterCacheKey key(fUniqueID, context.ctm(), context.clipBounds(), srcGenID, srcSubset); 205 if (context.cache()) { 206 sk_sp<SkSpecialImage> result = context.cache()->get(key, offset); 207 if (result) { 208 return result; 209 } 210 } 211 212 sk_sp<SkSpecialImage> result(this->onFilterImage(src, context, offset)); 213 214 #if SK_SUPPORT_GPU 215 if (src->isTextureBacked() && result && !result->isTextureBacked()) { 216 // Keep the result on the GPU - this is still required for some 217 // image filters that don't support GPU in all cases 218 GrContext* context = src->getContext(); 219 result = result->makeTextureImage(context); 220 } 221 #endif 222 223 if (result && context.cache()) { 224 context.cache()->set(key, result.get(), *offset, this); 225 SkAutoMutexAcquire mutex(fMutex); 226 fCacheKeys.push_back(key); 227 } 228 229 return result; 230 } 231 232 void SkImageFilter::removeKey(const SkImageFilterCacheKey& key) const { 233 SkAutoMutexAcquire mutex(fMutex); 234 for (int i = 0; i < fCacheKeys.count(); i++) { 235 if (fCacheKeys[i] == key) { 236 fCacheKeys.removeShuffle(i); 237 break; 238 } 239 } 240 #ifdef SK_DEBUG 241 for (int i = 0; i < fCacheKeys.count(); i++) { 242 if (fCacheKeys[i] == key) { 243 SkASSERT(false); 244 } 245 } 246 #endif 247 } 248 249 SkIRect SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm, 250 MapDirection direction) const { 251 if (kReverse_MapDirection == direction) { 252 SkIRect bounds = this->onFilterNodeBounds(src, ctm, direction); 253 return this->onFilterBounds(bounds, ctm, direction); 254 } else { 255 SkIRect bounds = this->onFilterBounds(src, ctm, direction); 256 bounds = this->onFilterNodeBounds(bounds, ctm, direction); 257 SkIRect dst; 258 this->getCropRect().applyTo(bounds, ctm, this->affectsTransparentBlack(), &dst); 259 return dst; 260 } 261 } 262 263 SkRect SkImageFilter::computeFastBounds(const SkRect& src) const { 264 if (0 == this->countInputs()) { 265 return src; 266 } 267 SkRect combinedBounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src; 268 for (int i = 1; i < this->countInputs(); i++) { 269 SkImageFilter* input = this->getInput(i); 270 if (input) { 271 combinedBounds.join(input->computeFastBounds(src)); 272 } else { 273 combinedBounds.join(src); 274 } 275 } 276 return combinedBounds; 277 } 278 279 bool SkImageFilter::canComputeFastBounds() const { 280 if (this->affectsTransparentBlack()) { 281 return false; 282 } 283 for (int i = 0; i < this->countInputs(); i++) { 284 SkImageFilter* input = this->getInput(i); 285 if (input && !input->canComputeFastBounds()) { 286 return false; 287 } 288 } 289 return true; 290 } 291 292 #if SK_SUPPORT_GPU 293 sk_sp<SkSpecialImage> SkImageFilter::DrawWithFP(GrContext* context, 294 sk_sp<GrFragmentProcessor> fp, 295 const SkIRect& bounds, 296 const OutputProperties& outputProperties) { 297 GrPaint paint; 298 paint.addColorFragmentProcessor(std::move(fp)); 299 paint.setPorterDuffXPFactory(SkBlendMode::kSrc); 300 301 sk_sp<SkColorSpace> colorSpace = sk_ref_sp(outputProperties.colorSpace()); 302 GrPixelConfig config = GrRenderableConfigForColorSpace(colorSpace.get()); 303 sk_sp<GrRenderTargetContext> renderTargetContext(context->makeDeferredRenderTargetContext( 304 SkBackingFit::kApprox, bounds.width(), bounds.height(), config, std::move(colorSpace))); 305 if (!renderTargetContext) { 306 return nullptr; 307 } 308 paint.setGammaCorrect(renderTargetContext->isGammaCorrect()); 309 310 SkIRect dstIRect = SkIRect::MakeWH(bounds.width(), bounds.height()); 311 SkRect srcRect = SkRect::Make(bounds); 312 SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); 313 GrFixedClip clip(dstIRect); 314 renderTargetContext->fillRectToRect(clip, std::move(paint), GrAA::kNo, SkMatrix::I(), dstRect, 315 srcRect); 316 317 return SkSpecialImage::MakeDeferredFromGpu(context, dstIRect, 318 kNeedNewImageUniqueID_SpecialImage, 319 renderTargetContext->asTextureProxyRef(), 320 renderTargetContext->refColorSpace()); 321 } 322 #endif 323 324 bool SkImageFilter::asAColorFilter(SkColorFilter** filterPtr) const { 325 SkASSERT(nullptr != filterPtr); 326 if (!this->isColorFilterNode(filterPtr)) { 327 return false; 328 } 329 if (nullptr != this->getInput(0) || (*filterPtr)->affectsTransparentBlack()) { 330 (*filterPtr)->unref(); 331 return false; 332 } 333 return true; 334 } 335 336 bool SkImageFilter::canHandleComplexCTM() const { 337 if (!this->onCanHandleComplexCTM()) { 338 return false; 339 } 340 const int count = this->countInputs(); 341 for (int i = 0; i < count; ++i) { 342 SkImageFilter* input = this->getInput(i); 343 if (input && !input->canHandleComplexCTM()) { 344 return false; 345 } 346 } 347 return true; 348 } 349 350 bool SkImageFilter::applyCropRect(const Context& ctx, const SkIRect& srcBounds, 351 SkIRect* dstBounds) const { 352 SkIRect temp = this->onFilterNodeBounds(srcBounds, ctx.ctm(), kForward_MapDirection); 353 fCropRect.applyTo(temp, ctx.ctm(), this->affectsTransparentBlack(), dstBounds); 354 // Intersect against the clip bounds, in case the crop rect has 355 // grown the bounds beyond the original clip. This can happen for 356 // example in tiling, where the clip is much smaller than the filtered 357 // primitive. If we didn't do this, we would be processing the filter 358 // at the full crop rect size in every tile. 359 return dstBounds->intersect(ctx.clipBounds()); 360 } 361 362 #if SK_SUPPORT_GPU 363 sk_sp<SkSpecialImage> SkImageFilter::ImageToColorSpace(SkSpecialImage* src, 364 const OutputProperties& outProps) { 365 // There are several conditions that determine if we actually need to convert the source to the 366 // destination's color space. Rather than duplicate that logic here, just try to make an xform 367 // object. If that produces something, then both are tagged, and the source is in a different 368 // gamut than the dest. There is some overhead to making the xform, but those are cached, and 369 // if we get one back, that means we're about to use it during the conversion anyway. 370 sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(src->getColorSpace(), 371 outProps.colorSpace()); 372 373 if (!colorSpaceXform) { 374 // No xform needed, just return the original image 375 return sk_ref_sp(src); 376 } 377 378 sk_sp<SkSpecialSurface> surf(src->makeSurface(outProps, 379 SkISize::Make(src->width(), src->height()))); 380 if (!surf) { 381 return sk_ref_sp(src); 382 } 383 384 SkCanvas* canvas = surf->getCanvas(); 385 SkASSERT(canvas); 386 SkPaint p; 387 p.setBlendMode(SkBlendMode::kSrc); 388 src->draw(canvas, 0, 0, &p); 389 return surf->makeImageSnapshot(); 390 } 391 #endif 392 393 // Return a larger (newWidth x newHeight) copy of 'src' with black padding 394 // around it. 395 static sk_sp<SkSpecialImage> pad_image(SkSpecialImage* src, 396 const SkImageFilter::OutputProperties& outProps, 397 int newWidth, int newHeight, int offX, int offY) { 398 // We would like to operate in the source's color space (so that we return an "identical" 399 // image, other than the padding. To achieve that, we'd create new output properties: 400 // 401 // SkImageFilter::OutputProperties outProps(src->getColorSpace()); 402 // 403 // That fails in at least two ways. For formats that are texturable but not renderable (like 404 // F16 on some ES implementations), we can't create a surface to do the work. For sRGB, images 405 // may be tagged with an sRGB color space (which leads to an sRGB config in makeSurface). But 406 // the actual config of that sRGB image on a device with no sRGB support is non-sRGB. 407 // 408 // Rather than try to special case these situations, we execute the image padding in the 409 // destination color space. This should not affect the output of the DAG in (almost) any case, 410 // because the result of this call is going to be used as an input, where it would have been 411 // switched to the destination space anyway. The one exception would be a filter that expected 412 // to consume unclamped F16 data, but the padded version of the image is pre-clamped to 8888. 413 // We can revisit this logic if that ever becomes an actual problem. 414 sk_sp<SkSpecialSurface> surf(src->makeSurface(outProps, SkISize::Make(newWidth, newHeight))); 415 if (!surf) { 416 return nullptr; 417 } 418 419 SkCanvas* canvas = surf->getCanvas(); 420 SkASSERT(canvas); 421 422 canvas->clear(0x0); 423 424 src->draw(canvas, offX, offY, nullptr); 425 426 return surf->makeImageSnapshot(); 427 } 428 429 sk_sp<SkSpecialImage> SkImageFilter::applyCropRect(const Context& ctx, 430 SkSpecialImage* src, 431 SkIPoint* srcOffset, 432 SkIRect* bounds) const { 433 const SkIRect srcBounds = SkIRect::MakeXYWH(srcOffset->x(), srcOffset->y(), 434 src->width(), src->height()); 435 436 SkIRect dstBounds = this->onFilterNodeBounds(srcBounds, ctx.ctm(), kForward_MapDirection); 437 fCropRect.applyTo(dstBounds, ctx.ctm(), this->affectsTransparentBlack(), bounds); 438 if (!bounds->intersect(ctx.clipBounds())) { 439 return nullptr; 440 } 441 442 if (srcBounds.contains(*bounds)) { 443 return sk_sp<SkSpecialImage>(SkRef(src)); 444 } else { 445 sk_sp<SkSpecialImage> img(pad_image(src, ctx.outputProperties(), 446 bounds->width(), bounds->height(), 447 srcOffset->x() - bounds->x(), 448 srcOffset->y() - bounds->y())); 449 *srcOffset = SkIPoint::Make(bounds->x(), bounds->y()); 450 return img; 451 } 452 } 453 454 SkIRect SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, 455 MapDirection direction) const { 456 if (this->countInputs() < 1) { 457 return src; 458 } 459 460 SkIRect totalBounds; 461 for (int i = 0; i < this->countInputs(); ++i) { 462 SkImageFilter* filter = this->getInput(i); 463 SkIRect rect = filter ? filter->filterBounds(src, ctm, direction) : src; 464 if (0 == i) { 465 totalBounds = rect; 466 } else { 467 totalBounds.join(rect); 468 } 469 } 470 471 return totalBounds; 472 } 473 474 SkIRect SkImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix&, MapDirection) const { 475 return src; 476 } 477 478 479 SkImageFilter::Context SkImageFilter::mapContext(const Context& ctx) const { 480 SkIRect clipBounds = this->onFilterNodeBounds(ctx.clipBounds(), ctx.ctm(), 481 MapDirection::kReverse_MapDirection); 482 return Context(ctx.ctm(), clipBounds, ctx.cache(), ctx.outputProperties()); 483 } 484 485 sk_sp<SkImageFilter> SkImageFilter::MakeMatrixFilter(const SkMatrix& matrix, 486 SkFilterQuality filterQuality, 487 sk_sp<SkImageFilter> input) { 488 return SkMatrixImageFilter::Make(matrix, filterQuality, std::move(input)); 489 } 490 491 sk_sp<SkImageFilter> SkImageFilter::makeWithLocalMatrix(const SkMatrix& matrix) const { 492 // SkLocalMatrixImageFilter takes SkImage* in its factory, but logically that parameter 493 // is *always* treated as a const ptr. Hence the const-cast here. 494 // 495 SkImageFilter* nonConstThis = const_cast<SkImageFilter*>(this); 496 return SkLocalMatrixImageFilter::Make(matrix, sk_ref_sp<SkImageFilter>(nonConstThis)); 497 } 498 499 sk_sp<SkSpecialImage> SkImageFilter::filterInput(int index, 500 SkSpecialImage* src, 501 const Context& ctx, 502 SkIPoint* offset) const { 503 SkImageFilter* input = this->getInput(index); 504 if (!input) { 505 return sk_sp<SkSpecialImage>(SkRef(src)); 506 } 507 508 sk_sp<SkSpecialImage> result(input->filterImage(src, this->mapContext(ctx), offset)); 509 510 SkASSERT(!result || src->isTextureBacked() == result->isTextureBacked()); 511 512 return result; 513 } 514 515 void SkImageFilter::PurgeCache() { 516 SkImageFilterCache::Get()->purge(); 517 } 518