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 "SkTileImageFilter.h" 9 #include "SkColorSpaceXformer.h" 10 #include "SkCanvas.h" 11 #include "SkImage.h" 12 #include "SkImageFilterPriv.h" 13 #include "SkMatrix.h" 14 #include "SkOffsetImageFilter.h" 15 #include "SkPaint.h" 16 #include "SkReadBuffer.h" 17 #include "SkShader.h" 18 #include "SkSpecialImage.h" 19 #include "SkSpecialSurface.h" 20 #include "SkSurface.h" 21 #include "SkValidationUtils.h" 22 #include "SkWriteBuffer.h" 23 24 sk_sp<SkImageFilter> SkTileImageFilter::Make(const SkRect& srcRect, const SkRect& dstRect, 25 sk_sp<SkImageFilter> input) { 26 if (!SkIsValidRect(srcRect) || !SkIsValidRect(dstRect)) { 27 return nullptr; 28 } 29 if (srcRect.width() == dstRect.width() && srcRect.height() == dstRect.height()) { 30 SkRect ir = dstRect; 31 if (!ir.intersect(srcRect)) { 32 return input; 33 } 34 CropRect cropRect(ir); 35 return SkOffsetImageFilter::Make(dstRect.x() - srcRect.x(), 36 dstRect.y() - srcRect.y(), 37 std::move(input), 38 &cropRect); 39 } 40 return sk_sp<SkImageFilter>(new SkTileImageFilter(srcRect, dstRect, std::move(input))); 41 } 42 43 sk_sp<SkSpecialImage> SkTileImageFilter::onFilterImage(SkSpecialImage* source, 44 const Context& ctx, 45 SkIPoint* offset) const { 46 SkIPoint inputOffset = SkIPoint::Make(0, 0); 47 sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset)); 48 if (!input) { 49 return nullptr; 50 } 51 52 SkRect dstRect; 53 ctx.ctm().mapRect(&dstRect, fDstRect); 54 if (!dstRect.intersect(SkRect::Make(ctx.clipBounds()))) { 55 return nullptr; 56 } 57 58 const SkIRect dstIRect = dstRect.roundOut(); 59 if (!fSrcRect.width() || !fSrcRect.height() || !dstIRect.width() || !dstIRect.height()) { 60 return nullptr; 61 } 62 63 SkRect srcRect; 64 ctx.ctm().mapRect(&srcRect, fSrcRect); 65 SkIRect srcIRect; 66 srcRect.roundOut(&srcIRect); 67 srcIRect.offset(-inputOffset); 68 const SkIRect inputBounds = SkIRect::MakeWH(input->width(), input->height()); 69 70 if (!SkIRect::Intersects(srcIRect, inputBounds)) { 71 return nullptr; 72 } 73 74 // We create an SkImage here b.c. it needs to be a tight fit for the tiling 75 sk_sp<SkImage> subset; 76 if (inputBounds.contains(srcIRect)) { 77 subset = input->asImage(&srcIRect); 78 } else { 79 sk_sp<SkSurface> surf(input->makeTightSurface(ctx.outputProperties(), srcIRect.size())); 80 if (!surf) { 81 return nullptr; 82 } 83 84 SkCanvas* canvas = surf->getCanvas(); 85 SkASSERT(canvas); 86 87 SkPaint paint; 88 paint.setBlendMode(SkBlendMode::kSrc); 89 90 input->draw(canvas, 91 SkIntToScalar(inputOffset.x()), SkIntToScalar(inputOffset.y()), 92 &paint); 93 94 subset = surf->makeImageSnapshot(); 95 } 96 if (!subset) { 97 return nullptr; 98 } 99 SkASSERT(subset->width() == srcIRect.width()); 100 SkASSERT(subset->height() == srcIRect.height()); 101 102 sk_sp<SkSpecialSurface> surf(source->makeSurface(ctx.outputProperties(), dstIRect.size())); 103 if (!surf) { 104 return nullptr; 105 } 106 107 SkCanvas* canvas = surf->getCanvas(); 108 SkASSERT(canvas); 109 110 SkPaint paint; 111 paint.setBlendMode(SkBlendMode::kSrc); 112 paint.setShader(subset->makeShader(SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode)); 113 canvas->translate(-dstRect.fLeft, -dstRect.fTop); 114 canvas->drawRect(dstRect, paint); 115 offset->fX = dstIRect.fLeft; 116 offset->fY = dstIRect.fTop; 117 return surf->makeImageSnapshot(); 118 } 119 120 sk_sp<SkImageFilter> SkTileImageFilter::onMakeColorSpace(SkColorSpaceXformer* xformer) const { 121 SkASSERT(1 == this->countInputs()); 122 123 auto input = xformer->apply(this->getInput(0)); 124 if (input.get() != this->getInput(0)) { 125 return SkTileImageFilter::Make(fSrcRect, fDstRect, std::move(input)); 126 } 127 return this->refMe(); 128 } 129 130 SkIRect SkTileImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm, 131 MapDirection direction) const { 132 SkRect rect = kReverse_MapDirection == direction ? fSrcRect : fDstRect; 133 ctm.mapRect(&rect); 134 return rect.roundOut(); 135 } 136 137 SkIRect SkTileImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix&, MapDirection) const { 138 // Don't recurse into inputs. 139 return src; 140 } 141 142 SkRect SkTileImageFilter::computeFastBounds(const SkRect& src) const { 143 return fDstRect; 144 } 145 146 sk_sp<SkFlattenable> SkTileImageFilter::CreateProc(SkReadBuffer& buffer) { 147 SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1); 148 SkRect src, dst; 149 buffer.readRect(&src); 150 buffer.readRect(&dst); 151 return Make(src, dst, common.getInput(0)); 152 } 153 154 void SkTileImageFilter::flatten(SkWriteBuffer& buffer) const { 155 this->INHERITED::flatten(buffer); 156 buffer.writeRect(fSrcRect); 157 buffer.writeRect(fDstRect); 158 } 159 160 #ifndef SK_IGNORE_TO_STRING 161 void SkTileImageFilter::toString(SkString* str) const { 162 str->appendf("SkTileImageFilter: ("); 163 str->appendf("src: %.2f %.2f %.2f %.2f", 164 fSrcRect.fLeft, fSrcRect.fTop, fSrcRect.fRight, fSrcRect.fBottom); 165 str->appendf(" dst: %.2f %.2f %.2f %.2f", 166 fDstRect.fLeft, fDstRect.fTop, fDstRect.fRight, fDstRect.fBottom); 167 if (this->getInput(0)) { 168 str->appendf("input: ("); 169 this->getInput(0)->toString(str); 170 str->appendf(")"); 171 } 172 str->append(")"); 173 } 174 #endif 175