• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "SkTwoPointConicalGradient.h"
9 
10 #include "SkRasterPipeline.h"
11 #include "../../jumper/SkJumper.h"
12 
SkTwoPointConicalGradient(const SkPoint & start,SkScalar startRadius,const SkPoint & end,SkScalar endRadius,bool flippedGrad,const Descriptor & desc)13 SkTwoPointConicalGradient::SkTwoPointConicalGradient(
14         const SkPoint& start, SkScalar startRadius,
15         const SkPoint& end, SkScalar endRadius,
16         bool flippedGrad, const Descriptor& desc)
17     : SkGradientShaderBase(desc, SkMatrix::I())
18     , fCenter1(start)
19     , fCenter2(end)
20     , fRadius1(startRadius)
21     , fRadius2(endRadius)
22     , fFlippedGrad(flippedGrad)
23 {
24     // this is degenerate, and should be caught by our caller
25     SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2);
26 }
27 
isOpaque() const28 bool SkTwoPointConicalGradient::isOpaque() const {
29     // Because areas outside the cone are left untouched, we cannot treat the
30     // shader as opaque even if the gradient itself is opaque.
31     // TODO(junov): Compute whether the cone fills the plane crbug.com/222380
32     return false;
33 }
34 
35 // Returns the original non-sorted version of the gradient
asAGradient(GradientInfo * info) const36 SkShader::GradientType SkTwoPointConicalGradient::asAGradient(
37     GradientInfo* info) const {
38     if (info) {
39         commonAsAGradient(info, fFlippedGrad);
40         info->fPoint[0] = fCenter1;
41         info->fPoint[1] = fCenter2;
42         info->fRadius[0] = fRadius1;
43         info->fRadius[1] = fRadius2;
44         if (fFlippedGrad) {
45             SkTSwap(info->fPoint[0], info->fPoint[1]);
46             SkTSwap(info->fRadius[0], info->fRadius[1]);
47         }
48     }
49     return kConical_GradientType;
50 }
51 
CreateProc(SkReadBuffer & buffer)52 sk_sp<SkFlattenable> SkTwoPointConicalGradient::CreateProc(SkReadBuffer& buffer) {
53     DescriptorScope desc;
54     if (!desc.unflatten(buffer)) {
55         return nullptr;
56     }
57     SkPoint c1 = buffer.readPoint();
58     SkPoint c2 = buffer.readPoint();
59     SkScalar r1 = buffer.readScalar();
60     SkScalar r2 = buffer.readScalar();
61 
62     if (buffer.readBool()) {    // flipped
63         SkTSwap(c1, c2);
64         SkTSwap(r1, r2);
65 
66         SkColor4f* colors = desc.mutableColors();
67         SkScalar* pos = desc.mutablePos();
68         const int last = desc.fCount - 1;
69         const int half = desc.fCount >> 1;
70         for (int i = 0; i < half; ++i) {
71             SkTSwap(colors[i], colors[last - i]);
72             if (pos) {
73                 SkScalar tmp = pos[i];
74                 pos[i] = SK_Scalar1 - pos[last - i];
75                 pos[last - i] = SK_Scalar1 - tmp;
76             }
77         }
78         if (pos) {
79             if (desc.fCount & 1) {
80                 pos[half] = SK_Scalar1 - pos[half];
81             }
82         }
83     }
84 
85     return SkGradientShader::MakeTwoPointConical(c1, r1, c2, r2, desc.fColors,
86                                                  std::move(desc.fColorSpace), desc.fPos,
87                                                  desc.fCount, desc.fTileMode, desc.fGradFlags,
88                                                  desc.fLocalMatrix);
89 }
90 
flatten(SkWriteBuffer & buffer) const91 void SkTwoPointConicalGradient::flatten(SkWriteBuffer& buffer) const {
92     this->INHERITED::flatten(buffer);
93     buffer.writePoint(fCenter1);
94     buffer.writePoint(fCenter2);
95     buffer.writeScalar(fRadius1);
96     buffer.writeScalar(fRadius2);
97     buffer.writeBool(fFlippedGrad);
98 }
99 
100 #if SK_SUPPORT_GPU
101 
102 #include "SkGr.h"
103 #include "SkTwoPointConicalGradient_gpu.h"
104 
asFragmentProcessor(const AsFPArgs & args) const105 sk_sp<GrFragmentProcessor> SkTwoPointConicalGradient::asFragmentProcessor(
106         const AsFPArgs& args) const {
107     SkASSERT(args.fContext);
108     SkASSERT(fPtsToUnit.isIdentity());
109     sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
110                                                                        args.fDstColorSpace);
111     sk_sp<GrFragmentProcessor> inner(Gr2PtConicalGradientEffect::Make(
112         GrGradientEffect::CreateArgs(args.fContext, this, args.fLocalMatrix, fTileMode,
113                                      std::move(colorSpaceXform), SkToBool(args.fDstColorSpace))));
114     if (!inner) {
115         return nullptr;
116     }
117     return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
118 }
119 
120 #endif
121 
onMakeColorSpace(SkColorSpaceXformer * xformer) const122 sk_sp<SkShader> SkTwoPointConicalGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
123     SkSTArray<8, SkColor> origColorsStorage(fColorCount);
124     SkSTArray<8, SkScalar> origPosStorage(fColorCount);
125     SkSTArray<8, SkColor> xformedColorsStorage(fColorCount);
126     SkColor* origColors = origColorsStorage.begin();
127     SkScalar* origPos = fOrigPos ? origPosStorage.begin() : nullptr;
128     SkColor* xformedColors = xformedColorsStorage.begin();
129 
130     // Flip if necessary
131     SkPoint center1 = fFlippedGrad ? fCenter2 : fCenter1;
132     SkPoint center2 = fFlippedGrad ? fCenter1 : fCenter2;
133     SkScalar radius1 = fFlippedGrad ? fRadius2 : fRadius1;
134     SkScalar radius2 = fFlippedGrad ? fRadius1 : fRadius2;
135     for (int i = 0; i < fColorCount; i++) {
136         origColors[i] = fFlippedGrad ? fOrigColors[fColorCount - i - 1] : fOrigColors[i];
137         if (origPos) {
138             origPos[i] = fFlippedGrad ? 1.0f - fOrigPos[fColorCount - i - 1] : fOrigPos[i];
139         }
140     }
141 
142     xformer->apply(xformedColors, origColors, fColorCount);
143     return SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, xformedColors,
144                                                  origPos, fColorCount, fTileMode, fGradFlags,
145                                                  &this->getLocalMatrix());
146 }
147 
148 
149 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const150 void SkTwoPointConicalGradient::toString(SkString* str) const {
151     str->append("SkTwoPointConicalGradient: (");
152 
153     str->append("center1: (");
154     str->appendScalar(fCenter1.fX);
155     str->append(", ");
156     str->appendScalar(fCenter1.fY);
157     str->append(") radius1: ");
158     str->appendScalar(fRadius1);
159     str->append(" ");
160 
161     str->append("center2: (");
162     str->appendScalar(fCenter2.fX);
163     str->append(", ");
164     str->appendScalar(fCenter2.fY);
165     str->append(") radius2: ");
166     str->appendScalar(fRadius2);
167     str->append(" ");
168 
169     this->INHERITED::toString(str);
170 
171     str->append(")");
172 }
173 #endif
174 
adjustMatrixAndAppendStages(SkArenaAlloc * alloc,SkMatrix * matrix,SkRasterPipeline * p,SkRasterPipeline * postPipeline) const175 bool SkTwoPointConicalGradient::adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
176                                                             SkMatrix* matrix,
177                                                             SkRasterPipeline* p,
178                                                             SkRasterPipeline* postPipeline) const {
179     const auto dCenter = (fCenter1 - fCenter2).length();
180     const auto dRadius = fRadius2 - fRadius1;
181     SkASSERT(dRadius >= 0);
182 
183     // When the two circles are concentric, we can pretend we're radial (with a tiny *twist).
184     if (SkScalarNearlyZero(dCenter)) {
185         matrix->postTranslate(-fCenter1.fX, -fCenter1.fY);
186         matrix->postScale(1 / fRadius2, 1 / fRadius2);
187         p->append(SkRasterPipeline::xy_to_radius);
188 
189         // Tiny twist: radial computes a t for [0, r2], but we want a t for [r1, r2].
190         auto scale =  fRadius2 / dRadius;
191         auto bias  = -fRadius1 / dRadius;
192 
193         p->append_matrix(alloc, SkMatrix::Concat(SkMatrix::MakeTrans(bias, 0),
194                                                  SkMatrix::MakeScale(scale, 1)));
195 
196         return true;
197     }
198 
199     // To simplify the stage math, we transform the universe (translate/scale/rotate)
200     // such that fCenter1 -> (0, 0) and fCenter2 -> (1, 0).
201     SkMatrix map_to_unit_vector;
202     const SkPoint centers[2] = { fCenter1, fCenter2 };
203     const SkPoint unitvec[2] = { {0, 0}, {1, 0} };
204     if (!map_to_unit_vector.setPolyToPoly(centers, unitvec, 2)) {
205         return false;
206     }
207     matrix->postConcat(map_to_unit_vector);
208 
209     // Since we've squashed the centers into a unit vector, we must also scale
210     // all the coefficient variables by (1 / dCenter).
211     const auto coeffA = 1 - dRadius * dRadius / (dCenter * dCenter);
212     auto* ctx = alloc->make<SkJumper_2PtConicalCtx>();
213     ctx->fCoeffA    = coeffA;
214     ctx->fInvCoeffA = 1 / coeffA;
215     ctx->fR0        = fRadius1 / dCenter;
216     ctx->fDR        = dRadius  / dCenter;
217 
218     // Is the solver guaranteed to not produce degenerates?
219     bool isWellBehaved = true;
220 
221     if (SkScalarNearlyZero(coeffA)) {
222         // The focal point is on the edge of the end circle.
223         p->append(SkRasterPipeline::xy_to_2pt_conical_linear, ctx);
224         isWellBehaved = false;
225     } else {
226         if (dCenter + fRadius1 > fRadius2) {
227             // The focal point is outside the end circle.
228 
229             // We want the larger root, per spec:
230             //   "For all values of ω where r(ω) > 0, starting with the value of ω nearest
231             //    to positive infinity and ending with the value of ω nearest to negative
232             //    infinity, draw the circumference of the circle with radius r(ω) at position
233             //    (x(ω), y(ω)), with the color at ω, but only painting on the parts of the
234             //    bitmap that have not yet been painted on by earlier circles in this step for
235             //    this rendering of the gradient."
236             // (https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-createradialgradient)
237             p->append(fFlippedGrad ? SkRasterPipeline::xy_to_2pt_conical_quadratic_min
238                                    : SkRasterPipeline::xy_to_2pt_conical_quadratic_max, ctx);
239             isWellBehaved = false;
240         } else {
241             // The focal point is inside (well-behaved case).
242             p->append(SkRasterPipeline::xy_to_2pt_conical_quadratic_max, ctx);
243         }
244     }
245 
246     if (!isWellBehaved) {
247         p->append(SkRasterPipeline::mask_2pt_conical_degenerates, ctx);
248         postPipeline->append(SkRasterPipeline::apply_vector_mask, &ctx->fMask);
249     }
250 
251     return true;
252 }
253