• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2012 The Android Open Source Project
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/core/SkCanvas.h"
9 #include "include/core/SkFlattenable.h"
10 #include "include/core/SkImageFilter.h"
11 #include "include/core/SkPoint.h"
12 #include "include/core/SkRect.h"
13 #include "include/core/SkRefCnt.h"
14 #include "include/core/SkScalar.h"
15 #include "include/core/SkTypes.h"
16 #include "include/effects/SkImageFilters.h"
17 #include "src/core/SkImageFilter_Base.h"
18 #include "src/core/SkReadBuffer.h"
19 #include "src/core/SkSpecialImage.h"
20 #include "src/core/SkSpecialSurface.h"
21 
22 #include <memory>
23 
24 namespace {
25 
26 class SkMergeImageFilter final : public SkImageFilter_Base {
27 public:
SkMergeImageFilter(sk_sp<SkImageFilter> * const filters,int count,const SkRect * cropRect)28     SkMergeImageFilter(sk_sp<SkImageFilter>* const filters, int count,
29                        const SkRect* cropRect)
30             : INHERITED(filters, count, cropRect) {
31         SkASSERT(count >= 0);
32     }
33 
34 protected:
35     sk_sp<SkSpecialImage> onFilterImage(const Context&, SkIPoint* offset) const override;
onGetCTMCapability() const36     MatrixCapability onGetCTMCapability() const override { return MatrixCapability::kComplex; }
37 
38 private:
39     friend void ::SkRegisterMergeImageFilterFlattenable();
40     SK_FLATTENABLE_HOOKS(SkMergeImageFilter)
41 
42     using INHERITED = SkImageFilter_Base;
43 };
44 
45 } // end namespace
Merge(sk_sp<SkImageFilter> * const filters,int count,const CropRect & cropRect)46 sk_sp<SkImageFilter> SkImageFilters::Merge(sk_sp<SkImageFilter>* const filters, int count,
47                                            const CropRect& cropRect) {
48     return sk_sp<SkImageFilter>(new SkMergeImageFilter(filters, count, cropRect));
49 }
50 
SkRegisterMergeImageFilterFlattenable()51 void SkRegisterMergeImageFilterFlattenable() {
52     SK_REGISTER_FLATTENABLE(SkMergeImageFilter);
53     // TODO (michaelludwig) - Remove after grace period for SKPs to stop using old name
54     SkFlattenable::Register("SkMergeImageFilterImpl", SkMergeImageFilter::CreateProc);
55 }
56 
CreateProc(SkReadBuffer & buffer)57 sk_sp<SkFlattenable> SkMergeImageFilter::CreateProc(SkReadBuffer& buffer) {
58     Common common;
59     if (!common.unflatten(buffer, -1) || !buffer.isValid()) {
60         return nullptr;
61     }
62     return SkImageFilters::Merge(common.inputs(), common.inputCount(), common.cropRect());
63 }
64 
65 ///////////////////////////////////////////////////////////////////////////////
66 
onFilterImage(const Context & ctx,SkIPoint * offset) const67 sk_sp<SkSpecialImage> SkMergeImageFilter::onFilterImage(const Context& ctx,
68                                                         SkIPoint* offset) const {
69     int inputCount = this->countInputs();
70     if (inputCount < 1) {
71         return nullptr;
72     }
73 
74     SkIRect bounds;
75     bounds.setEmpty();
76 
77     std::unique_ptr<sk_sp<SkSpecialImage>[]> inputs(new sk_sp<SkSpecialImage>[inputCount]);
78     std::unique_ptr<SkIPoint[]> offsets(new SkIPoint[inputCount]);
79 
80     // Filter all of the inputs.
81     for (int i = 0; i < inputCount; ++i) {
82         offsets[i] = { 0, 0 };
83         inputs[i] = this->filterInput(i, ctx, &offsets[i]);
84         if (!inputs[i]) {
85             continue;
86         }
87         const SkIRect inputBounds = SkIRect::MakeXYWH(offsets[i].fX, offsets[i].fY,
88                                                       inputs[i]->width(), inputs[i]->height());
89         bounds.join(inputBounds);
90     }
91     if (bounds.isEmpty()) {
92         return nullptr;
93     }
94 
95     // Apply the crop rect to the union of the inputs' bounds.
96     // Note that the crop rect can only reduce the bounds, since this
97     // filter does not affect transparent black.
98     bool embiggen = false;
99     this->getCropRect().applyTo(bounds, ctx.ctm(), embiggen, &bounds);
100     if (!bounds.intersect(ctx.clipBounds())) {
101         return nullptr;
102     }
103 
104     const int x0 = bounds.left();
105     const int y0 = bounds.top();
106 
107     sk_sp<SkSpecialSurface> surf(ctx.makeSurface(bounds.size()));
108     if (!surf) {
109         return nullptr;
110     }
111 
112     SkCanvas* canvas = surf->getCanvas();
113     SkASSERT(canvas);
114 
115     canvas->clear(0x0);
116 
117     // Composite all of the filter inputs.
118     for (int i = 0; i < inputCount; ++i) {
119         if (!inputs[i]) {
120             continue;
121         }
122 
123         inputs[i]->draw(canvas,
124                         SkIntToScalar(offsets[i].x()) - x0, SkIntToScalar(offsets[i].y()) - y0);
125     }
126 
127     offset->fX = bounds.left();
128     offset->fY = bounds.top();
129     return surf->makeImageSnapshot();
130 }
131