Home | History | Annotate | Download | only in effects
      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