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 #include "SkImageFilterCacheKey.h" 10 11 #include "SkBitmap.h" 12 #include "SkBitmapDevice.h" 13 #include "SkChecksum.h" 14 #include "SkDevice.h" 15 #include "SkLocalMatrixImageFilter.h" 16 #include "SkMatrixImageFilter.h" 17 #include "SkOncePtr.h" 18 #include "SkReadBuffer.h" 19 #include "SkRect.h" 20 #include "SkSpecialImage.h" 21 #include "SkTDynamicHash.h" 22 #include "SkTInternalLList.h" 23 #include "SkValidationUtils.h" 24 #include "SkWriteBuffer.h" 25 #if SK_SUPPORT_GPU 26 #include "GrContext.h" 27 #include "GrDrawContext.h" 28 #include "SkGrPixelRef.h" 29 #include "SkGr.h" 30 #endif 31 32 #ifdef SK_BUILD_FOR_IOS 33 enum { kDefaultCacheSize = 2 * 1024 * 1024 }; 34 #else 35 enum { kDefaultCacheSize = 128 * 1024 * 1024 }; 36 #endif 37 38 #ifndef SK_IGNORE_TO_STRING 39 void SkImageFilter::CropRect::toString(SkString* str) const { 40 if (!fFlags) { 41 return; 42 } 43 44 str->appendf("cropRect ("); 45 if (fFlags & CropRect::kHasLeft_CropEdge) { 46 str->appendf("%.2f, ", fRect.fLeft); 47 } else { 48 str->appendf("X, "); 49 } 50 if (fFlags & CropRect::kHasTop_CropEdge) { 51 str->appendf("%.2f, ", fRect.fTop); 52 } else { 53 str->appendf("X, "); 54 } 55 if (fFlags & CropRect::kHasWidth_CropEdge) { 56 str->appendf("%.2f, ", fRect.width()); 57 } else { 58 str->appendf("X, "); 59 } 60 if (fFlags & CropRect::kHasHeight_CropEdge) { 61 str->appendf("%.2f", fRect.height()); 62 } else { 63 str->appendf("X"); 64 } 65 str->appendf(") "); 66 } 67 #endif 68 69 void SkImageFilter::CropRect::applyTo(const SkIRect& imageBounds, 70 const SkMatrix& ctm, 71 SkIRect* cropped) const { 72 *cropped = imageBounds; 73 if (fFlags) { 74 SkRect devCropR; 75 ctm.mapRect(&devCropR, fRect); 76 const SkIRect devICropR = devCropR.roundOut(); 77 78 // Compute the left/top first, in case we have to read them to compute right/bottom 79 if (fFlags & kHasLeft_CropEdge) { 80 cropped->fLeft = devICropR.fLeft; 81 } 82 if (fFlags & kHasTop_CropEdge) { 83 cropped->fTop = devICropR.fTop; 84 } 85 if (fFlags & kHasWidth_CropEdge) { 86 cropped->fRight = cropped->fLeft + devICropR.width(); 87 } 88 if (fFlags & kHasHeight_CropEdge) { 89 cropped->fBottom = cropped->fTop + devICropR.height(); 90 } 91 } 92 } 93 94 /////////////////////////////////////////////////////////////////////////////////////////////////// 95 96 static int32_t next_image_filter_unique_id() { 97 static int32_t gImageFilterUniqueID; 98 99 // Never return 0. 100 int32_t id; 101 do { 102 id = sk_atomic_inc(&gImageFilterUniqueID) + 1; 103 } while (0 == id); 104 return id; 105 } 106 107 SkImageFilter::Common::~Common() { 108 for (int i = 0; i < fInputs.count(); ++i) { 109 SkSafeUnref(fInputs[i]); 110 } 111 } 112 113 void SkImageFilter::Common::allocInputs(int count) { 114 const size_t size = count * sizeof(SkImageFilter*); 115 fInputs.reset(count); 116 sk_bzero(fInputs.get(), size); 117 } 118 119 void SkImageFilter::Common::detachInputs(SkImageFilter** inputs) { 120 const size_t size = fInputs.count() * sizeof(SkImageFilter*); 121 memcpy(inputs, fInputs.get(), size); 122 sk_bzero(fInputs.get(), size); 123 } 124 125 bool SkImageFilter::Common::unflatten(SkReadBuffer& buffer, int expectedCount) { 126 const int count = buffer.readInt(); 127 if (!buffer.validate(count >= 0)) { 128 return false; 129 } 130 if (!buffer.validate(expectedCount < 0 || count == expectedCount)) { 131 return false; 132 } 133 134 this->allocInputs(count); 135 for (int i = 0; i < count; i++) { 136 if (buffer.readBool()) { 137 fInputs[i] = buffer.readImageFilter(); 138 } 139 if (!buffer.isValid()) { 140 return false; 141 } 142 } 143 SkRect rect; 144 buffer.readRect(&rect); 145 if (!buffer.isValid() || !buffer.validate(SkIsValidRect(rect))) { 146 return false; 147 } 148 149 uint32_t flags = buffer.readUInt(); 150 fCropRect = CropRect(rect, flags); 151 if (buffer.isVersionLT(SkReadBuffer::kImageFilterNoUniqueID_Version)) { 152 153 (void) buffer.readUInt(); 154 } 155 return buffer.isValid(); 156 } 157 158 /////////////////////////////////////////////////////////////////////////////////////////////////// 159 160 SkImageFilter::SkImageFilter(int inputCount, SkImageFilter** inputs, const CropRect* cropRect) 161 : fInputCount(inputCount), 162 fInputs(new SkImageFilter*[inputCount]), 163 fUsesSrcInput(false), 164 fCropRect(cropRect ? *cropRect : CropRect(SkRect(), 0x0)), 165 fUniqueID(next_image_filter_unique_id()) { 166 for (int i = 0; i < inputCount; ++i) { 167 if (nullptr == inputs[i] || inputs[i]->usesSrcInput()) { 168 fUsesSrcInput = true; 169 } 170 fInputs[i] = inputs[i]; 171 SkSafeRef(fInputs[i]); 172 } 173 } 174 175 SkImageFilter::~SkImageFilter() { 176 for (int i = 0; i < fInputCount; i++) { 177 SkSafeUnref(fInputs[i]); 178 } 179 delete[] fInputs; 180 Cache::Get()->purgeByKeys(fCacheKeys.begin(), fCacheKeys.count()); 181 } 182 183 SkImageFilter::SkImageFilter(int inputCount, SkReadBuffer& buffer) 184 : fUsesSrcInput(false) 185 , fUniqueID(next_image_filter_unique_id()) { 186 Common common; 187 if (common.unflatten(buffer, inputCount)) { 188 fCropRect = common.cropRect(); 189 fInputCount = common.inputCount(); 190 fInputs = new SkImageFilter* [fInputCount]; 191 common.detachInputs(fInputs); 192 for (int i = 0; i < fInputCount; ++i) { 193 if (nullptr == fInputs[i] || fInputs[i]->usesSrcInput()) { 194 fUsesSrcInput = true; 195 } 196 } 197 } else { 198 fInputCount = 0; 199 fInputs = nullptr; 200 } 201 } 202 203 void SkImageFilter::flatten(SkWriteBuffer& buffer) const { 204 buffer.writeInt(fInputCount); 205 for (int i = 0; i < fInputCount; i++) { 206 SkImageFilter* input = this->getInput(i); 207 buffer.writeBool(input != nullptr); 208 if (input != nullptr) { 209 buffer.writeFlattenable(input); 210 } 211 } 212 buffer.writeRect(fCropRect.rect()); 213 buffer.writeUInt(fCropRect.flags()); 214 } 215 216 bool SkImageFilter::filterImageDeprecated(Proxy* proxy, const SkBitmap& src, 217 const Context& context, 218 SkBitmap* result, SkIPoint* offset) const { 219 SkASSERT(result); 220 SkASSERT(offset); 221 uint32_t srcGenID = fUsesSrcInput ? src.getGenerationID() : 0; 222 Cache::Key key(fUniqueID, context.ctm(), context.clipBounds(), 223 srcGenID, SkIRect::MakeWH(0, 0)); 224 if (context.cache()) { 225 if (context.cache()->get(key, result, offset)) { 226 return true; 227 } 228 } 229 /* 230 * Give the proxy first shot at the filter. If it returns false, ask 231 * the filter to do it. 232 */ 233 if ((proxy && proxy->filterImage(this, src, context, result, offset)) || 234 this->onFilterImageDeprecated(proxy, src, context, result, offset)) { 235 if (context.cache()) { 236 context.cache()->set(key, *result, *offset); 237 SkAutoMutexAcquire mutex(fMutex); 238 fCacheKeys.push_back(key); 239 } 240 return true; 241 } 242 return false; 243 } 244 245 bool SkImageFilter::filterInputDeprecated(int index, Proxy* proxy, const SkBitmap& src, 246 const Context& ctx, 247 SkBitmap* result, SkIPoint* offset) const { 248 SkImageFilter* input = this->getInput(index); 249 if (!input) { 250 return true; 251 } 252 return input->filterImageDeprecated(proxy, src, this->mapContext(ctx), result, offset); 253 } 254 255 bool SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst, 256 MapDirection direction) const { 257 SkASSERT(dst); 258 SkIRect bounds; 259 if (kReverse_MapDirection == direction) { 260 this->onFilterNodeBounds(src, ctm, &bounds, direction); 261 return this->onFilterBounds(bounds, ctm, dst, direction); 262 } else { 263 SkIRect temp; 264 if (!this->onFilterBounds(src, ctm, &bounds, direction)) { 265 return false; 266 } 267 this->onFilterNodeBounds(bounds, ctm, &temp, direction); 268 this->getCropRect().applyTo(temp, ctm, dst); 269 return true; 270 } 271 } 272 273 void SkImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const { 274 if (0 == fInputCount) { 275 *dst = src; 276 return; 277 } 278 if (this->getInput(0)) { 279 this->getInput(0)->computeFastBounds(src, dst); 280 } else { 281 *dst = src; 282 } 283 for (int i = 1; i < fInputCount; i++) { 284 SkImageFilter* input = this->getInput(i); 285 if (input) { 286 SkRect bounds; 287 input->computeFastBounds(src, &bounds); 288 dst->join(bounds); 289 } else { 290 dst->join(src); 291 } 292 } 293 } 294 295 bool SkImageFilter::canComputeFastBounds() const { 296 for (int i = 0; i < fInputCount; i++) { 297 SkImageFilter* input = this->getInput(i); 298 if (input && !input->canComputeFastBounds()) { 299 return false; 300 } 301 } 302 return true; 303 } 304 305 bool SkImageFilter::onFilterImageDeprecated(Proxy*, const SkBitmap&, const Context&, 306 SkBitmap*, SkIPoint*) const { 307 return false; 308 } 309 310 bool SkImageFilter::canFilterImageGPU() const { 311 return this->asFragmentProcessor(nullptr, nullptr, SkMatrix::I(), SkIRect()); 312 } 313 314 bool SkImageFilter::filterImageGPUDeprecated(Proxy* proxy, const SkBitmap& src, const Context& ctx, 315 SkBitmap* result, SkIPoint* offset) const { 316 #if SK_SUPPORT_GPU 317 SkBitmap input = src; 318 SkASSERT(fInputCount == 1); 319 SkIPoint srcOffset = SkIPoint::Make(0, 0); 320 if (!this->filterInputGPUDeprecated(0, proxy, src, ctx, &input, &srcOffset)) { 321 return false; 322 } 323 GrTexture* srcTexture = input.getTexture(); 324 SkIRect bounds; 325 if (!this->applyCropRectDeprecated(ctx, proxy, input, &srcOffset, &bounds, &input)) { 326 return false; 327 } 328 GrContext* context = srcTexture->getContext(); 329 330 GrSurfaceDesc desc; 331 desc.fFlags = kRenderTarget_GrSurfaceFlag, 332 desc.fWidth = bounds.width(); 333 desc.fHeight = bounds.height(); 334 desc.fConfig = kRGBA_8888_GrPixelConfig; 335 336 SkAutoTUnref<GrTexture> dst(context->textureProvider()->createApproxTexture(desc)); 337 if (!dst) { 338 return false; 339 } 340 341 GrFragmentProcessor* fp; 342 offset->fX = bounds.left(); 343 offset->fY = bounds.top(); 344 bounds.offset(-srcOffset); 345 SkMatrix matrix(ctx.ctm()); 346 matrix.postTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top())); 347 GrPaint paint; 348 if (this->asFragmentProcessor(&fp, srcTexture, matrix, bounds)) { 349 SkASSERT(fp); 350 paint.addColorFragmentProcessor(fp)->unref(); 351 paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); 352 353 SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(dst->asRenderTarget())); 354 if (drawContext) { 355 SkRect srcRect = SkRect::Make(bounds); 356 SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); 357 GrClip clip(dstRect); 358 drawContext->fillRectToRect(clip, paint, SkMatrix::I(), dstRect, srcRect); 359 360 GrWrapTextureInBitmap(dst, bounds.width(), bounds.height(), false, result); 361 return true; 362 } 363 } 364 #endif 365 return false; 366 } 367 368 bool SkImageFilter::asAColorFilter(SkColorFilter** filterPtr) const { 369 SkASSERT(nullptr != filterPtr); 370 if (!this->isColorFilterNode(filterPtr)) { 371 return false; 372 } 373 if (nullptr != this->getInput(0) || (*filterPtr)->affectsTransparentBlack()) { 374 (*filterPtr)->unref(); 375 return false; 376 } 377 return true; 378 } 379 380 bool SkImageFilter::applyCropRect(const Context& ctx, const SkIRect& srcBounds, 381 SkIRect* dstBounds) const { 382 this->onFilterNodeBounds(srcBounds, ctx.ctm(), dstBounds, kForward_MapDirection); 383 fCropRect.applyTo(*dstBounds, ctx.ctm(), dstBounds); 384 // Intersect against the clip bounds, in case the crop rect has 385 // grown the bounds beyond the original clip. This can happen for 386 // example in tiling, where the clip is much smaller than the filtered 387 // primitive. If we didn't do this, we would be processing the filter 388 // at the full crop rect size in every tile. 389 return dstBounds->intersect(ctx.clipBounds()); 390 } 391 392 bool SkImageFilter::applyCropRectDeprecated(const Context& ctx, Proxy* proxy, const SkBitmap& src, 393 SkIPoint* srcOffset, SkIRect* bounds, 394 SkBitmap* dst) const { 395 SkIRect srcBounds; 396 src.getBounds(&srcBounds); 397 srcBounds.offset(*srcOffset); 398 SkIRect dstBounds; 399 this->onFilterNodeBounds(srcBounds, ctx.ctm(), &dstBounds, kForward_MapDirection); 400 fCropRect.applyTo(dstBounds, ctx.ctm(), bounds); 401 if (!bounds->intersect(ctx.clipBounds())) { 402 return false; 403 } 404 405 if (srcBounds.contains(*bounds)) { 406 *dst = src; 407 return true; 408 } else { 409 SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds->width(), bounds->height())); 410 if (!device) { 411 return false; 412 } 413 SkCanvas canvas(device); 414 canvas.clear(0x00000000); 415 canvas.drawBitmap(src, srcOffset->x() - bounds->x(), srcOffset->y() - bounds->y()); 416 *srcOffset = SkIPoint::Make(bounds->x(), bounds->y()); 417 *dst = device->accessBitmap(false); 418 return true; 419 } 420 } 421 422 bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, 423 SkIRect* dst, MapDirection direction) const { 424 if (fInputCount < 1) { 425 *dst = src; 426 return true; 427 } 428 429 SkIRect totalBounds; 430 for (int i = 0; i < fInputCount; ++i) { 431 SkImageFilter* filter = this->getInput(i); 432 SkIRect rect = src; 433 if (filter && !filter->filterBounds(src, ctm, &rect, direction)) { 434 return false; 435 } 436 if (0 == i) { 437 totalBounds = rect; 438 } else { 439 totalBounds.join(rect); 440 } 441 } 442 443 // don't modify dst until now, so we don't accidentally change it in the 444 // loop, but then return false on the next filter. 445 *dst = totalBounds; 446 return true; 447 } 448 449 void SkImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix&, 450 SkIRect* dst, MapDirection) const { 451 *dst = src; 452 } 453 454 455 SkImageFilter::Context SkImageFilter::mapContext(const Context& ctx) const { 456 SkIRect clipBounds; 457 this->onFilterNodeBounds(ctx.clipBounds(), ctx.ctm(), &clipBounds, 458 MapDirection::kReverse_MapDirection); 459 return Context(ctx.ctm(), clipBounds, ctx.cache()); 460 } 461 462 bool SkImageFilter::asFragmentProcessor(GrFragmentProcessor**, GrTexture*, 463 const SkMatrix&, const SkIRect&) const { 464 return false; 465 } 466 467 SkImageFilter* SkImageFilter::CreateMatrixFilter(const SkMatrix& matrix, 468 SkFilterQuality filterQuality, 469 SkImageFilter* input) { 470 return SkMatrixImageFilter::Create(matrix, filterQuality, input); 471 } 472 473 SkImageFilter* SkImageFilter::newWithLocalMatrix(const SkMatrix& matrix) const { 474 // SkLocalMatrixImageFilter takes SkImage* in its factory, but logically that parameter 475 // is *always* treated as a const ptr. Hence the const-cast here. 476 // 477 return SkLocalMatrixImageFilter::Create(matrix, const_cast<SkImageFilter*>(this)); 478 } 479 480 #if SK_SUPPORT_GPU 481 482 bool SkImageFilter::filterInputGPUDeprecated(int index, SkImageFilter::Proxy* proxy, 483 const SkBitmap& src, const Context& ctx, 484 SkBitmap* result, SkIPoint* offset) const { 485 SkImageFilter* input = this->getInput(index); 486 if (!input) { 487 return true; 488 } 489 // Ensure that GrContext calls under filterImage and filterImageGPU below will see an identity 490 // matrix with no clip and that the matrix, clip, and render target set before this function was 491 // called are restored before we return to the caller. 492 GrContext* context = src.getTexture()->getContext(); 493 if (input->filterImageDeprecated(proxy, src, this->mapContext(ctx), result, offset)) { 494 if (!result->getTexture()) { 495 const SkImageInfo info = result->info(); 496 if (kUnknown_SkColorType == info.colorType()) { 497 return false; 498 } 499 SkAutoTUnref<GrTexture> resultTex( 500 GrRefCachedBitmapTexture(context, *result, GrTextureParams::ClampNoFilter())); 501 if (!resultTex) { 502 return false; 503 } 504 result->setPixelRef(new SkGrPixelRef(info, resultTex))->unref(); 505 } 506 return true; 507 } else { 508 return false; 509 } 510 } 511 #endif 512 513 namespace { 514 515 class CacheImpl : public SkImageFilter::Cache { 516 public: 517 CacheImpl(size_t maxBytes) : fMaxBytes(maxBytes), fCurrentBytes(0) { } 518 ~CacheImpl() override { 519 SkTDynamicHash<Value, Key>::Iter iter(&fLookup); 520 521 while (!iter.done()) { 522 Value* v = &*iter; 523 ++iter; 524 delete v; 525 } 526 } 527 struct Value { 528 Value(const Key& key, const SkBitmap& bitmap, const SkIPoint& offset) 529 : fKey(key), fBitmap(bitmap), fOffset(offset) {} 530 Value(const Key& key, SkSpecialImage* image, const SkIPoint& offset) 531 : fKey(key), fImage(SkRef(image)), fOffset(offset) {} 532 533 Key fKey; 534 SkBitmap fBitmap; 535 SkAutoTUnref<SkSpecialImage> fImage; 536 SkIPoint fOffset; 537 static const Key& GetKey(const Value& v) { 538 return v.fKey; 539 } 540 static uint32_t Hash(const Key& key) { 541 return SkChecksum::Murmur3(reinterpret_cast<const uint32_t*>(&key), sizeof(Key)); 542 } 543 SK_DECLARE_INTERNAL_LLIST_INTERFACE(Value); 544 }; 545 546 bool get(const Key& key, SkBitmap* result, SkIPoint* offset) const override { 547 SkAutoMutexAcquire mutex(fMutex); 548 if (Value* v = fLookup.find(key)) { 549 *result = v->fBitmap; 550 *offset = v->fOffset; 551 if (v != fLRU.head()) { 552 fLRU.remove(v); 553 fLRU.addToHead(v); 554 } 555 return true; 556 } 557 return false; 558 } 559 560 SkSpecialImage* get(const Key& key, SkIPoint* offset) const override { 561 SkAutoMutexAcquire mutex(fMutex); 562 if (Value* v = fLookup.find(key)) { 563 *offset = v->fOffset; 564 if (v != fLRU.head()) { 565 fLRU.remove(v); 566 fLRU.addToHead(v); 567 } 568 return v->fImage; 569 } 570 return nullptr; 571 } 572 573 void set(const Key& key, const SkBitmap& result, const SkIPoint& offset) override { 574 SkAutoMutexAcquire mutex(fMutex); 575 if (Value* v = fLookup.find(key)) { 576 this->removeInternal(v); 577 } 578 Value* v = new Value(key, result, offset); 579 fLookup.add(v); 580 fLRU.addToHead(v); 581 fCurrentBytes += result.getSize(); 582 while (fCurrentBytes > fMaxBytes) { 583 Value* tail = fLRU.tail(); 584 SkASSERT(tail); 585 if (tail == v) { 586 break; 587 } 588 this->removeInternal(tail); 589 } 590 } 591 592 void set(const Key& key, SkSpecialImage* image, const SkIPoint& offset) override { 593 SkAutoMutexAcquire mutex(fMutex); 594 if (Value* v = fLookup.find(key)) { 595 this->removeInternal(v); 596 } 597 Value* v = new Value(key, image, offset); 598 fLookup.add(v); 599 fLRU.addToHead(v); 600 fCurrentBytes += image->getSize(); 601 while (fCurrentBytes > fMaxBytes) { 602 Value* tail = fLRU.tail(); 603 SkASSERT(tail); 604 if (tail == v) { 605 break; 606 } 607 this->removeInternal(tail); 608 } 609 } 610 611 void purge() override { 612 SkAutoMutexAcquire mutex(fMutex); 613 while (fCurrentBytes > 0) { 614 Value* tail = fLRU.tail(); 615 SkASSERT(tail); 616 this->removeInternal(tail); 617 } 618 } 619 620 void purgeByKeys(const Key keys[], int count) override { 621 SkAutoMutexAcquire mutex(fMutex); 622 for (int i = 0; i < count; i++) { 623 if (Value* v = fLookup.find(keys[i])) { 624 this->removeInternal(v); 625 } 626 } 627 } 628 629 private: 630 void removeInternal(Value* v) { 631 if (v->fImage) { 632 fCurrentBytes -= v->fImage->getSize(); 633 } else { 634 fCurrentBytes -= v->fBitmap.getSize(); 635 } 636 fLRU.remove(v); 637 fLookup.remove(v->fKey); 638 delete v; 639 } 640 private: 641 SkTDynamicHash<Value, Key> fLookup; 642 mutable SkTInternalLList<Value> fLRU; 643 size_t fMaxBytes; 644 size_t fCurrentBytes; 645 mutable SkMutex fMutex; 646 }; 647 648 } // namespace 649 650 SkImageFilter::Cache* SkImageFilter::Cache::Create(size_t maxBytes) { 651 return new CacheImpl(maxBytes); 652 } 653 654 SK_DECLARE_STATIC_ONCE_PTR(SkImageFilter::Cache, cache); 655 SkImageFilter::Cache* SkImageFilter::Cache::Get() { 656 return cache.get([]{ return SkImageFilter::Cache::Create(kDefaultCacheSize); }); 657 } 658 659 void SkImageFilter::PurgeCache() { 660 Cache::Get()->purge(); 661 } 662 663 /////////////////////////////////////////////////////////////////////////////////////////////////// 664 665 SkBaseDevice* SkImageFilter::DeviceProxy::createDevice(int w, int h, TileUsage usage) { 666 SkBaseDevice::CreateInfo cinfo(SkImageInfo::MakeN32Premul(w, h), 667 kPossible_TileUsage == usage ? SkBaseDevice::kPossible_TileUsage 668 : SkBaseDevice::kNever_TileUsage, 669 kUnknown_SkPixelGeometry, 670 false, /* preserveLCDText */ 671 true /*forImageFilter*/); 672 SkBaseDevice* dev = fDevice->onCreateDevice(cinfo, nullptr); 673 if (nullptr == dev) { 674 const SkSurfaceProps surfaceProps(fDevice->fSurfaceProps.flags(), 675 kUnknown_SkPixelGeometry); 676 dev = SkBitmapDevice::Create(cinfo.fInfo, surfaceProps); 677 } 678 return dev; 679 } 680 681 bool SkImageFilter::DeviceProxy::filterImage(const SkImageFilter* filter, const SkBitmap& src, 682 const SkImageFilter::Context& ctx, 683 SkBitmap* result, SkIPoint* offset) { 684 return fDevice->filterImage(filter, src, ctx, result, offset); 685 } 686 687