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