1 /*
2  * Copyright 2012 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/private/SkFloatingPoint.h"
9 #include "src/core/SkRasterPipeline.h"
10 #include "src/core/SkReadBuffer.h"
11 #include "src/core/SkWriteBuffer.h"
12 #include "src/shaders/gradients/SkTwoPointConicalGradient.h"
13 
14 #include <utility>
15 
16 // Please see https://skia.org/dev/design/conical for how our shader works.
17 
set(SkScalar r0,SkScalar r1,SkMatrix * matrix)18 bool SkTwoPointConicalGradient::FocalData::set(SkScalar r0, SkScalar r1, SkMatrix* matrix) {
19     fIsSwapped = false;
20     fFocalX = sk_ieee_float_divide(r0, (r0 - r1));
21     if (SkScalarNearlyZero(fFocalX - 1)) {
22         // swap r0, r1
23         matrix->postTranslate(-1, 0);
24         matrix->postScale(-1, 1);
25         std::swap(r0, r1);
26         fFocalX = 0; // because r0 is now 0
27         fIsSwapped = true;
28     }
29 
30     // Map {focal point, (1, 0)} to {(0, 0), (1, 0)}
31     const SkPoint from[2]   = { {fFocalX, 0}, {1, 0} };
32     const SkPoint to[2]     = { {0, 0}, {1, 0} };
33     SkMatrix focalMatrix;
34     if (!focalMatrix.setPolyToPoly(from, to, 2)) {
35         return false;
36     }
37     matrix->postConcat(focalMatrix);
38     fR1 = r1 / SkScalarAbs(1 - fFocalX); // focalMatrix has a scale of 1/(1-f)
39 
40     // The following transformations are just to accelerate the shader computation by saving
41     // some arithmatic operations.
42     if (this->isFocalOnCircle()) {
43         matrix->postScale(0.5, 0.5);
44     } else {
45         matrix->postScale(fR1 / (fR1 * fR1 - 1), 1 / sqrt(SkScalarAbs(fR1 * fR1 - 1)));
46     }
47     matrix->postScale(SkScalarAbs(1 - fFocalX), SkScalarAbs(1 - fFocalX)); // scale |1 - f|
48     return true;
49 }
50 
Create(const SkPoint & c0,SkScalar r0,const SkPoint & c1,SkScalar r1,const Descriptor & desc)51 sk_sp<SkShader> SkTwoPointConicalGradient::Create(const SkPoint& c0, SkScalar r0,
52                                                   const SkPoint& c1, SkScalar r1,
53                                                   const Descriptor& desc) {
54     SkMatrix gradientMatrix;
55     Type     gradientType;
56 
57     if (SkScalarNearlyZero((c0 - c1).length())) {
58         if (SkScalarNearlyZero(SkTMax(r0, r1)) || SkScalarNearlyEqual(r0, r1)) {
59             // Degenerate case; avoid dividing by zero. Should have been caught by caller but
60             // just in case, recheck here.
61             return nullptr;
62         }
63         // Concentric case: we can pretend we're radial (with a tiny twist).
64         const SkScalar scale = sk_ieee_float_divide(1, SkTMax(r0, r1));
65         gradientMatrix = SkMatrix::MakeTrans(-c1.x(), -c1.y());
66         gradientMatrix.postScale(scale, scale);
67 
68         gradientType = Type::kRadial;
69     } else {
70         const SkPoint centers[2] = { c0    , c1     };
71         const SkPoint unitvec[2] = { {0, 0}, {1, 0} };
72 
73         if (!gradientMatrix.setPolyToPoly(centers, unitvec, 2)) {
74             // Degenerate case.
75             return nullptr;
76         }
77 
78         gradientType = SkScalarNearlyZero(r1 - r0) ? Type::kStrip : Type::kFocal;
79     }
80 
81     FocalData focalData;
82     if (gradientType == Type::kFocal) {
83         const auto dCenter = (c0 - c1).length();
84         if (!focalData.set(r0 / dCenter, r1 / dCenter, &gradientMatrix)) {
85             return nullptr;
86         }
87     }
88     return sk_sp<SkShader>(new SkTwoPointConicalGradient(c0, r0, c1, r1, desc,
89                                                          gradientType, gradientMatrix, focalData));
90 }
91 
SkTwoPointConicalGradient(const SkPoint & start,SkScalar startRadius,const SkPoint & end,SkScalar endRadius,const Descriptor & desc,Type type,const SkMatrix & gradientMatrix,const FocalData & data)92 SkTwoPointConicalGradient::SkTwoPointConicalGradient(
93         const SkPoint& start, SkScalar startRadius,
94         const SkPoint& end, SkScalar endRadius,
95         const Descriptor& desc, Type type, const SkMatrix& gradientMatrix, const FocalData& data)
96     : SkGradientShaderBase(desc, gradientMatrix)
97     , fCenter1(start)
98     , fCenter2(end)
99     , fRadius1(startRadius)
100     , fRadius2(endRadius)
101     , fType(type)
102 {
103     // this is degenerate, and should be caught by our caller
104     SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2);
105     if (type == Type::kFocal) {
106         fFocalData = data;
107     }
108 }
109 
isOpaque() const110 bool SkTwoPointConicalGradient::isOpaque() const {
111     // Because areas outside the cone are left untouched, we cannot treat the
112     // shader as opaque even if the gradient itself is opaque.
113     // TODO(junov): Compute whether the cone fills the plane crbug.com/222380
114     return false;
115 }
116 
117 // Returns the original non-sorted version of the gradient
asAGradient(GradientInfo * info) const118 SkShader::GradientType SkTwoPointConicalGradient::asAGradient(GradientInfo* info) const {
119     if (info) {
120         commonAsAGradient(info);
121         info->fPoint[0] = fCenter1;
122         info->fPoint[1] = fCenter2;
123         info->fRadius[0] = fRadius1;
124         info->fRadius[1] = fRadius2;
125     }
126     return kConical_GradientType;
127 }
128 
CreateProc(SkReadBuffer & buffer)129 sk_sp<SkFlattenable> SkTwoPointConicalGradient::CreateProc(SkReadBuffer& buffer) {
130     DescriptorScope desc;
131     if (!desc.unflatten(buffer)) {
132         return nullptr;
133     }
134     SkPoint c1 = buffer.readPoint();
135     SkPoint c2 = buffer.readPoint();
136     SkScalar r1 = buffer.readScalar();
137     SkScalar r2 = buffer.readScalar();
138 
139     if (buffer.isVersionLT(SkPicturePriv::k2PtConicalNoFlip_Version) && buffer.readBool()) {
140         using std::swap;
141         // legacy flipped gradient
142         swap(c1, c2);
143         swap(r1, r2);
144 
145         SkColor4f* colors = desc.mutableColors();
146         SkScalar* pos = desc.mutablePos();
147         const int last = desc.fCount - 1;
148         const int half = desc.fCount >> 1;
149         for (int i = 0; i < half; ++i) {
150             swap(colors[i], colors[last - i]);
151             if (pos) {
152                 SkScalar tmp = pos[i];
153                 pos[i] = SK_Scalar1 - pos[last - i];
154                 pos[last - i] = SK_Scalar1 - tmp;
155             }
156         }
157         if (pos) {
158             if (desc.fCount & 1) {
159                 pos[half] = SK_Scalar1 - pos[half];
160             }
161         }
162     }
163     if (!buffer.isValid()) {
164         return nullptr;
165     }
166     return SkGradientShader::MakeTwoPointConical(c1, r1, c2, r2, desc.fColors,
167                                                  std::move(desc.fColorSpace), desc.fPos,
168                                                  desc.fCount, desc.fTileMode, desc.fGradFlags,
169                                                  desc.fLocalMatrix);
170 }
171 
flatten(SkWriteBuffer & buffer) const172 void SkTwoPointConicalGradient::flatten(SkWriteBuffer& buffer) const {
173     this->INHERITED::flatten(buffer);
174     buffer.writePoint(fCenter1);
175     buffer.writePoint(fCenter2);
176     buffer.writeScalar(fRadius1);
177     buffer.writeScalar(fRadius2);
178 }
179 
appendGradientStages(SkArenaAlloc * alloc,SkRasterPipeline * p,SkRasterPipeline * postPipeline) const180 void SkTwoPointConicalGradient::appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline* p,
181                                                      SkRasterPipeline* postPipeline) const {
182     const auto dRadius = fRadius2 - fRadius1;
183 
184     if (fType == Type::kRadial) {
185         p->append(SkRasterPipeline::xy_to_radius);
186 
187         // Tiny twist: radial computes a t for [0, r2], but we want a t for [r1, r2].
188         auto scale =  SkTMax(fRadius1, fRadius2) / dRadius;
189         auto bias  = -fRadius1 / dRadius;
190 
191         p->append_matrix(alloc, SkMatrix::Concat(SkMatrix::MakeTrans(bias, 0),
192                                                  SkMatrix::MakeScale(scale, 1)));
193         return;
194     }
195 
196     if (fType == Type::kStrip) {
197         auto* ctx = alloc->make<SkRasterPipeline_2PtConicalCtx>();
198         SkScalar scaledR0 = fRadius1 / this->getCenterX1();
199         ctx->fP0 = scaledR0 * scaledR0;
200         p->append(SkRasterPipeline::xy_to_2pt_conical_strip, ctx);
201         p->append(SkRasterPipeline::mask_2pt_conical_nan, ctx);
202         postPipeline->append(SkRasterPipeline::apply_vector_mask, &ctx->fMask);
203         return;
204     }
205 
206     auto* ctx = alloc->make<SkRasterPipeline_2PtConicalCtx>();
207     ctx->fP0 = 1/fFocalData.fR1;
208     ctx->fP1 = fFocalData.fFocalX;
209 
210     if (fFocalData.isFocalOnCircle()) {
211         p->append(SkRasterPipeline::xy_to_2pt_conical_focal_on_circle);
212     } else if (fFocalData.isWellBehaved()) {
213         p->append(SkRasterPipeline::xy_to_2pt_conical_well_behaved, ctx);
214     } else if (fFocalData.isSwapped() || 1 - fFocalData.fFocalX < 0) {
215         p->append(SkRasterPipeline::xy_to_2pt_conical_smaller, ctx);
216     } else {
217         p->append(SkRasterPipeline::xy_to_2pt_conical_greater, ctx);
218     }
219 
220     if (!fFocalData.isWellBehaved()) {
221         p->append(SkRasterPipeline::mask_2pt_conical_degenerates, ctx);
222     }
223     if (1 - fFocalData.fFocalX < 0) {
224         p->append(SkRasterPipeline::negate_x);
225     }
226     if (!fFocalData.isNativelyFocal()) {
227         p->append(SkRasterPipeline::alter_2pt_conical_compensate_focal, ctx);
228     }
229     if (fFocalData.isSwapped()) {
230         p->append(SkRasterPipeline::alter_2pt_conical_unswap);
231     }
232     if (!fFocalData.isWellBehaved()) {
233         postPipeline->append(SkRasterPipeline::apply_vector_mask, &ctx->fMask);
234     }
235 }
236 
237 /////////////////////////////////////////////////////////////////////
238 
239 #if SK_SUPPORT_GPU
240 
241 #include "src/gpu/gradients/GrGradientShader.h"
242 
asFragmentProcessor(const GrFPArgs & args) const243 std::unique_ptr<GrFragmentProcessor> SkTwoPointConicalGradient::asFragmentProcessor(
244         const GrFPArgs& args) const {
245     return GrGradientShader::MakeConical(*this, args);
246 }
247 
248 #endif
249