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