SkImageFilter.cpp 23 KB

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