| 1 | /* |
| 2 | * Copyright 2013 Google Inc. |
| 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 "include/effects/SkTileImageFilter.h" |
| 9 | |
| 10 | #include "include/core/SkCanvas.h" |
| 11 | #include "include/core/SkImage.h" |
| 12 | #include "include/core/SkMatrix.h" |
| 13 | #include "include/core/SkPaint.h" |
| 14 | #include "include/core/SkShader.h" |
| 15 | #include "include/core/SkSurface.h" |
| 16 | #include "include/effects/SkOffsetImageFilter.h" |
| 17 | #include "src/core/SkImageFilter_Base.h" |
| 18 | #include "src/core/SkReadBuffer.h" |
| 19 | #include "src/core/SkSpecialImage.h" |
| 20 | #include "src/core/SkSpecialSurface.h" |
| 21 | #include "src/core/SkValidationUtils.h" |
| 22 | #include "src/core/SkWriteBuffer.h" |
| 23 | |
| 24 | namespace { |
| 25 | |
| 26 | class SkTileImageFilterImpl final : public SkImageFilter_Base { |
| 27 | public: |
| 28 | SkTileImageFilterImpl(const SkRect& srcRect, const SkRect& dstRect, sk_sp<SkImageFilter> input) |
| 29 | : INHERITED(&input, 1, nullptr) |
| 30 | , fSrcRect(srcRect) |
| 31 | , fDstRect(dstRect) {} |
| 32 | |
| 33 | SkIRect onFilterBounds(const SkIRect& src, const SkMatrix& ctm, |
| 34 | MapDirection, const SkIRect* inputRect) const override; |
| 35 | SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix& ctm, |
| 36 | MapDirection, const SkIRect* inputRect) const override; |
| 37 | SkRect computeFastBounds(const SkRect& src) const override; |
| 38 | |
| 39 | protected: |
| 40 | void flatten(SkWriteBuffer& buffer) const override; |
| 41 | |
| 42 | sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override; |
| 43 | |
| 44 | private: |
| 45 | friend void SkTileImageFilter::RegisterFlattenables(); |
| 46 | SK_FLATTENABLE_HOOKS(SkTileImageFilterImpl) |
| 47 | |
| 48 | SkRect fSrcRect; |
| 49 | SkRect fDstRect; |
| 50 | |
| 51 | typedef SkImageFilter_Base INHERITED; |
| 52 | }; |
| 53 | |
| 54 | } // end namespace |
| 55 | |
| 56 | sk_sp<SkImageFilter> SkTileImageFilter::Make(const SkRect& srcRect, const SkRect& dstRect, |
| 57 | sk_sp<SkImageFilter> input) { |
| 58 | if (!SkIsValidRect(srcRect) || !SkIsValidRect(dstRect)) { |
| 59 | return nullptr; |
| 60 | } |
| 61 | if (srcRect.width() == dstRect.width() && srcRect.height() == dstRect.height()) { |
| 62 | SkRect ir = dstRect; |
| 63 | if (!ir.intersect(srcRect)) { |
| 64 | return input; |
| 65 | } |
| 66 | SkImageFilter::CropRect cropRect(ir); |
| 67 | return SkOffsetImageFilter::Make(dstRect.x() - srcRect.x(), |
| 68 | dstRect.y() - srcRect.y(), |
| 69 | std::move(input), |
| 70 | &cropRect); |
| 71 | } |
| 72 | return sk_sp<SkImageFilter>(new SkTileImageFilterImpl(srcRect, dstRect, std::move(input))); |
| 73 | } |
| 74 | |
| 75 | void SkTileImageFilter::RegisterFlattenables() { |
| 76 | SK_REGISTER_FLATTENABLE(SkTileImageFilterImpl); |
| 77 | // TODO (michaelludwig) - Remove after grace period for SKPs to stop using old name |
| 78 | SkFlattenable::Register("SkTileImageFilter" , SkTileImageFilterImpl::CreateProc); |
| 79 | } |
| 80 | |
| 81 | /////////////////////////////////////////////////////////////////////////////////////////////////// |
| 82 | |
| 83 | sk_sp<SkFlattenable> SkTileImageFilterImpl::CreateProc(SkReadBuffer& buffer) { |
| 84 | SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1); |
| 85 | SkRect src, dst; |
| 86 | buffer.readRect(&src); |
| 87 | buffer.readRect(&dst); |
| 88 | return SkTileImageFilter::Make(src, dst, common.getInput(0)); |
| 89 | } |
| 90 | |
| 91 | void SkTileImageFilterImpl::flatten(SkWriteBuffer& buffer) const { |
| 92 | this->INHERITED::flatten(buffer); |
| 93 | buffer.writeRect(fSrcRect); |
| 94 | buffer.writeRect(fDstRect); |
| 95 | } |
| 96 | |
| 97 | sk_sp<SkSpecialImage> SkTileImageFilterImpl::onFilterImage(const Context& ctx, |
| 98 | SkIPoint* offset) const { |
| 99 | SkIPoint inputOffset = SkIPoint::Make(0, 0); |
| 100 | sk_sp<SkSpecialImage> input(this->filterInput(0, ctx, &inputOffset)); |
| 101 | if (!input) { |
| 102 | return nullptr; |
| 103 | } |
| 104 | |
| 105 | SkRect dstRect; |
| 106 | ctx.ctm().mapRect(&dstRect, fDstRect); |
| 107 | if (!dstRect.intersect(SkRect::Make(ctx.clipBounds()))) { |
| 108 | return nullptr; |
| 109 | } |
| 110 | |
| 111 | const SkIRect dstIRect = dstRect.roundOut(); |
| 112 | if (!fSrcRect.width() || !fSrcRect.height() || !dstIRect.width() || !dstIRect.height()) { |
| 113 | return nullptr; |
| 114 | } |
| 115 | |
| 116 | SkRect srcRect; |
| 117 | ctx.ctm().mapRect(&srcRect, fSrcRect); |
| 118 | SkIRect srcIRect; |
| 119 | srcRect.roundOut(&srcIRect); |
| 120 | srcIRect.offset(-inputOffset); |
| 121 | const SkIRect inputBounds = SkIRect::MakeWH(input->width(), input->height()); |
| 122 | |
| 123 | if (!SkIRect::Intersects(srcIRect, inputBounds)) { |
| 124 | return nullptr; |
| 125 | } |
| 126 | |
| 127 | // We create an SkImage here b.c. it needs to be a tight fit for the tiling |
| 128 | sk_sp<SkImage> subset; |
| 129 | if (inputBounds.contains(srcIRect)) { |
| 130 | subset = input->asImage(&srcIRect); |
| 131 | } else { |
| 132 | sk_sp<SkSurface> surf(input->makeTightSurface(ctx.colorType(), ctx.colorSpace(), |
| 133 | srcIRect.size())); |
| 134 | if (!surf) { |
| 135 | return nullptr; |
| 136 | } |
| 137 | |
| 138 | SkCanvas* canvas = surf->getCanvas(); |
| 139 | SkASSERT(canvas); |
| 140 | |
| 141 | SkPaint paint; |
| 142 | paint.setBlendMode(SkBlendMode::kSrc); |
| 143 | |
| 144 | input->draw(canvas, |
| 145 | SkIntToScalar(inputOffset.x()), SkIntToScalar(inputOffset.y()), |
| 146 | &paint); |
| 147 | |
| 148 | subset = surf->makeImageSnapshot(); |
| 149 | } |
| 150 | if (!subset) { |
| 151 | return nullptr; |
| 152 | } |
| 153 | SkASSERT(subset->width() == srcIRect.width()); |
| 154 | SkASSERT(subset->height() == srcIRect.height()); |
| 155 | |
| 156 | sk_sp<SkSpecialSurface> surf(ctx.makeSurface(dstIRect.size())); |
| 157 | if (!surf) { |
| 158 | return nullptr; |
| 159 | } |
| 160 | |
| 161 | SkCanvas* canvas = surf->getCanvas(); |
| 162 | SkASSERT(canvas); |
| 163 | |
| 164 | SkPaint paint; |
| 165 | paint.setBlendMode(SkBlendMode::kSrc); |
| 166 | paint.setShader(subset->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat)); |
| 167 | canvas->translate(-dstRect.fLeft, -dstRect.fTop); |
| 168 | canvas->drawRect(dstRect, paint); |
| 169 | offset->fX = dstIRect.fLeft; |
| 170 | offset->fY = dstIRect.fTop; |
| 171 | return surf->makeImageSnapshot(); |
| 172 | } |
| 173 | |
| 174 | SkIRect SkTileImageFilterImpl::onFilterNodeBounds( |
| 175 | const SkIRect& src, const SkMatrix& ctm, MapDirection dir, const SkIRect* inputRect) const { |
| 176 | SkRect rect = kReverse_MapDirection == dir ? fSrcRect : fDstRect; |
| 177 | ctm.mapRect(&rect); |
| 178 | return rect.roundOut(); |
| 179 | } |
| 180 | |
| 181 | SkIRect SkTileImageFilterImpl::onFilterBounds(const SkIRect& src, const SkMatrix&, |
| 182 | MapDirection, const SkIRect* inputRect) const { |
| 183 | // Don't recurse into inputs. |
| 184 | return src; |
| 185 | } |
| 186 | |
| 187 | SkRect SkTileImageFilterImpl::computeFastBounds(const SkRect& src) const { |
| 188 | return fDstRect; |
| 189 | } |
| 190 | |
| 191 | |