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