1/*
2 * Copyright 2015 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 "SkImageSource.h"
9
10#include "SkCanvas.h"
11#include "SkColorSpaceXformer.h"
12#include "SkImage.h"
13#include "SkReadBuffer.h"
14#include "SkSpecialImage.h"
15#include "SkSpecialSurface.h"
16#include "SkWriteBuffer.h"
17#include "SkString.h"
18
19sk_sp<SkImageFilter> SkImageSource::Make(sk_sp<SkImage> image) {
20    if (!image) {
21        return nullptr;
22    }
23
24    return sk_sp<SkImageFilter>(new SkImageSource(std::move(image)));
25}
26
27sk_sp<SkImageFilter> SkImageSource::Make(sk_sp<SkImage> image,
28                                         const SkRect& srcRect,
29                                         const SkRect& dstRect,
30                                         SkFilterQuality filterQuality) {
31    if (!image || srcRect.width() <= 0.0f || srcRect.height() <= 0.0f) {
32        return nullptr;
33    }
34
35    return sk_sp<SkImageFilter>(new SkImageSource(std::move(image),
36                                                  srcRect, dstRect,
37                                                  filterQuality));
38}
39
40SkImageSource::SkImageSource(sk_sp<SkImage> image)
41    : INHERITED(nullptr, 0, nullptr)
42    , fImage(std::move(image))
43    , fSrcRect(SkRect::MakeIWH(fImage->width(), fImage->height()))
44    , fDstRect(fSrcRect)
45    , fFilterQuality(kHigh_SkFilterQuality) {
46}
47
48SkImageSource::SkImageSource(sk_sp<SkImage> image,
49                             const SkRect& srcRect,
50                             const SkRect& dstRect,
51                             SkFilterQuality filterQuality)
52    : INHERITED(nullptr, 0, nullptr)
53    , fImage(std::move(image))
54    , fSrcRect(srcRect)
55    , fDstRect(dstRect)
56    , fFilterQuality(filterQuality) {
57}
58
59sk_sp<SkFlattenable> SkImageSource::CreateProc(SkReadBuffer& buffer) {
60    SkFilterQuality filterQuality = (SkFilterQuality)buffer.readInt();
61
62    SkRect src, dst;
63    buffer.readRect(&src);
64    buffer.readRect(&dst);
65
66    sk_sp<SkImage> image(buffer.readImage());
67    if (!image) {
68        return nullptr;
69    }
70
71    return SkImageSource::Make(std::move(image), src, dst, filterQuality);
72}
73
74void SkImageSource::flatten(SkWriteBuffer& buffer) const {
75    buffer.writeInt(fFilterQuality);
76    buffer.writeRect(fSrcRect);
77    buffer.writeRect(fDstRect);
78    buffer.writeImage(fImage.get());
79}
80
81sk_sp<SkSpecialImage> SkImageSource::onFilterImage(SkSpecialImage* source, const Context& ctx,
82                                                   SkIPoint* offset) const {
83    SkRect dstRect;
84    ctx.ctm().mapRect(&dstRect, fDstRect);
85
86    SkRect bounds = SkRect::MakeIWH(fImage->width(), fImage->height());
87    if (fSrcRect == bounds) {
88        int iLeft = dstRect.fLeft;
89        int iTop = dstRect.fTop;
90        // TODO: this seems to be a very noise-prone way to determine this (esp. the floating-point
91        // widths & heights).
92        if (dstRect.width() == bounds.width() && dstRect.height() == bounds.height() &&
93            iLeft == dstRect.fLeft && iTop == dstRect.fTop) {
94            // The dest is just an un-scaled integer translation of the entire image; return it
95            offset->fX = iLeft;
96            offset->fY = iTop;
97
98            return SkSpecialImage::MakeFromImage(SkIRect::MakeWH(fImage->width(), fImage->height()),
99                                                 fImage, ctx.outputProperties().colorSpace(),
100                                                 &source->props());
101        }
102    }
103
104    const SkIRect dstIRect = dstRect.roundOut();
105
106    sk_sp<SkSpecialSurface> surf(source->makeSurface(ctx.outputProperties(), dstIRect.size()));
107    if (!surf) {
108        return nullptr;
109    }
110
111    SkCanvas* canvas = surf->getCanvas();
112    SkASSERT(canvas);
113
114    // TODO: it seems like this clear shouldn't be necessary (see skbug.com/5075)
115    canvas->clear(0x0);
116
117    SkPaint paint;
118
119    // Subtract off the integer component of the translation (will be applied in offset, below).
120    dstRect.offset(-SkIntToScalar(dstIRect.fLeft), -SkIntToScalar(dstIRect.fTop));
121    paint.setBlendMode(SkBlendMode::kSrc);
122    // FIXME: this probably shouldn't be necessary, but drawImageRect asserts
123    // None filtering when it's translate-only
124    paint.setFilterQuality(
125        fSrcRect.width() == dstRect.width() && fSrcRect.height() == dstRect.height() ?
126               kNone_SkFilterQuality : fFilterQuality);
127    canvas->drawImageRect(fImage.get(), fSrcRect, dstRect, &paint,
128                          SkCanvas::kStrict_SrcRectConstraint);
129
130    offset->fX = dstIRect.fLeft;
131    offset->fY = dstIRect.fTop;
132    return surf->makeImageSnapshot();
133}
134
135sk_sp<SkImageFilter> SkImageSource::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
136    SkASSERT(0 == this->countInputs());
137
138    auto image = xformer->apply(fImage.get());
139    if (image != fImage) {
140        return SkImageSource::Make(image, fSrcRect, fDstRect, fFilterQuality);
141    }
142    return this->refMe();
143}
144
145SkRect SkImageSource::computeFastBounds(const SkRect& src) const {
146    return fDstRect;
147}
148
149#ifndef SK_IGNORE_TO_STRING
150void SkImageSource::toString(SkString* str) const {
151    str->appendf("SkImageSource: (");
152    str->appendf("src: (%f,%f,%f,%f) dst: (%f,%f,%f,%f) ",
153                 fSrcRect.fLeft, fSrcRect.fTop, fSrcRect.fRight, fSrcRect.fBottom,
154                 fDstRect.fLeft, fDstRect.fTop, fDstRect.fRight, fDstRect.fBottom);
155    str->appendf("image: (%d,%d)",
156                 fImage->width(), fImage->height());
157    str->append(")");
158}
159#endif
160