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