blob: 479d85ffd852eacf9e5777d786daeb8fb608f8cf [file] [log] [blame]
rileya@google.com589708b2012-07-26 20:04:23 +00001/*
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"
commit-bot@chromium.orgaa64fbf2014-04-03 14:59:19 +00009
Florin Malitaa66ef2d2017-06-28 10:02:40 -040010#include "SkRasterPipeline.h"
11#include "../../jumper/SkJumper.h"
12
Florin Malita9c2212f2017-07-29 18:23:10 -040013sk_sp<SkShader> SkTwoPointConicalGradient::Create(const SkPoint& c0, SkScalar r0,
14 const SkPoint& c1, SkScalar r1,
15 bool flipped, const Descriptor& desc) {
16 SkMatrix gradientMatrix;
17 Type gradientType;
18
19 if (SkScalarNearlyZero((c0 - c1).length())) {
20 // Concentric case: we can pretend we're radial (with a tiny twist).
21 gradientMatrix = SkMatrix::MakeTrans(-c1.x(), -c1.y());
22 gradientMatrix.postScale(1 / r1, 1 / r1);
23
24 gradientType = Type::kRadial;
25 } else {
26 const SkPoint centers[2] = { c0 , c1 };
27 const SkPoint unitvec[2] = { {0, 0}, {1, 0} };
28
29 if (!gradientMatrix.setPolyToPoly(centers, unitvec, 2)) {
30 // Degenerate case.
31 return nullptr;
32 }
33
34 // General two-point case.
35 gradientType = Type::kTwoPoint;
36 }
37
38 return sk_sp<SkShader>(new SkTwoPointConicalGradient(c0, r0, c1, r1, flipped, desc,
39 gradientType, gradientMatrix));
40}
41
rileya@google.com589708b2012-07-26 20:04:23 +000042SkTwoPointConicalGradient::SkTwoPointConicalGradient(
reed@google.com3d3a8602013-05-24 14:58:44 +000043 const SkPoint& start, SkScalar startRadius,
44 const SkPoint& end, SkScalar endRadius,
Florin Malita9c2212f2017-07-29 18:23:10 -040045 bool flippedGrad, const Descriptor& desc,
46 Type type, const SkMatrix& gradientMatrix)
47 : SkGradientShaderBase(desc, gradientMatrix)
reedaddf2ed2014-08-11 08:28:24 -070048 , fCenter1(start)
49 , fCenter2(end)
50 , fRadius1(startRadius)
51 , fRadius2(endRadius)
52 , fFlippedGrad(flippedGrad)
Florin Malita9c2212f2017-07-29 18:23:10 -040053 , fType(type)
reedaddf2ed2014-08-11 08:28:24 -070054{
rileya@google.com589708b2012-07-26 20:04:23 +000055 // this is degenerate, and should be caught by our caller
56 SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2);
rileya@google.com589708b2012-07-26 20:04:23 +000057}
58
commit-bot@chromium.org3fbab822013-03-20 00:49:57 +000059bool SkTwoPointConicalGradient::isOpaque() const {
robertphillips@google.comcb6d97c2013-07-09 13:50:09 +000060 // Because areas outside the cone are left untouched, we cannot treat the
61 // shader as opaque even if the gradient itself is opaque.
62 // TODO(junov): Compute whether the cone fills the plane crbug.com/222380
63 return false;
commit-bot@chromium.org3fbab822013-03-20 00:49:57 +000064}
65
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +000066// Returns the original non-sorted version of the gradient
rileya@google.com589708b2012-07-26 20:04:23 +000067SkShader::GradientType SkTwoPointConicalGradient::asAGradient(
68 GradientInfo* info) const {
69 if (info) {
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +000070 commonAsAGradient(info, fFlippedGrad);
rileya@google.com589708b2012-07-26 20:04:23 +000071 info->fPoint[0] = fCenter1;
72 info->fPoint[1] = fCenter2;
73 info->fRadius[0] = fRadius1;
74 info->fRadius[1] = fRadius2;
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +000075 if (fFlippedGrad) {
76 SkTSwap(info->fPoint[0], info->fPoint[1]);
77 SkTSwap(info->fRadius[0], info->fRadius[1]);
78 }
rileya@google.com589708b2012-07-26 20:04:23 +000079 }
80 return kConical_GradientType;
81}
82
reed60c9b582016-04-03 09:11:13 -070083sk_sp<SkFlattenable> SkTwoPointConicalGradient::CreateProc(SkReadBuffer& buffer) {
reed9fa60da2014-08-21 07:59:51 -070084 DescriptorScope desc;
85 if (!desc.unflatten(buffer)) {
halcanary96fcdcc2015-08-27 07:41:13 -070086 return nullptr;
reed9fa60da2014-08-21 07:59:51 -070087 }
88 SkPoint c1 = buffer.readPoint();
89 SkPoint c2 = buffer.readPoint();
90 SkScalar r1 = buffer.readScalar();
91 SkScalar r2 = buffer.readScalar();
92
93 if (buffer.readBool()) { // flipped
94 SkTSwap(c1, c2);
95 SkTSwap(r1, r2);
96
brianosmane25d71c2016-09-28 11:27:28 -070097 SkColor4f* colors = desc.mutableColors();
reed9fa60da2014-08-21 07:59:51 -070098 SkScalar* pos = desc.mutablePos();
99 const int last = desc.fCount - 1;
100 const int half = desc.fCount >> 1;
101 for (int i = 0; i < half; ++i) {
102 SkTSwap(colors[i], colors[last - i]);
103 if (pos) {
104 SkScalar tmp = pos[i];
105 pos[i] = SK_Scalar1 - pos[last - i];
106 pos[last - i] = SK_Scalar1 - tmp;
107 }
108 }
109 if (pos) {
110 if (desc.fCount & 1) {
111 pos[half] = SK_Scalar1 - pos[half];
112 }
113 }
114 }
115
brianosmane25d71c2016-09-28 11:27:28 -0700116 return SkGradientShader::MakeTwoPointConical(c1, r1, c2, r2, desc.fColors,
117 std::move(desc.fColorSpace), desc.fPos,
reed8a21c9f2016-03-08 18:50:00 -0800118 desc.fCount, desc.fTileMode, desc.fGradFlags,
reed60c9b582016-04-03 09:11:13 -0700119 desc.fLocalMatrix);
reed9fa60da2014-08-21 07:59:51 -0700120}
121
122void SkTwoPointConicalGradient::flatten(SkWriteBuffer& buffer) const {
rileya@google.com589708b2012-07-26 20:04:23 +0000123 this->INHERITED::flatten(buffer);
124 buffer.writePoint(fCenter1);
125 buffer.writePoint(fCenter2);
126 buffer.writeScalar(fRadius1);
127 buffer.writeScalar(fRadius2);
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +0000128 buffer.writeBool(fFlippedGrad);
rileya@google.com589708b2012-07-26 20:04:23 +0000129}
130
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000131#if SK_SUPPORT_GPU
132
dandov9de5b512014-06-10 14:38:28 -0700133#include "SkGr.h"
brianosman9557c272016-09-15 06:59:15 -0700134#include "SkTwoPointConicalGradient_gpu.h"
dandov9de5b512014-06-10 14:38:28 -0700135
bungeman06ca8ec2016-06-09 08:01:03 -0700136sk_sp<GrFragmentProcessor> SkTwoPointConicalGradient::asFragmentProcessor(
brianosman1638c0d2016-07-25 05:12:53 -0700137 const AsFPArgs& args) const {
brianosman839345d2016-07-22 11:04:53 -0700138 SkASSERT(args.fContext);
bsalomon@google.comf94b3a42012-10-31 18:09:01 +0000139 SkASSERT(fPtsToUnit.isIdentity());
brianosmanb9c51372016-09-15 11:09:45 -0700140 sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(),
141 args.fDstColorSpace);
brianosman9557c272016-09-15 06:59:15 -0700142 sk_sp<GrFragmentProcessor> inner(Gr2PtConicalGradientEffect::Make(
brianosmanb9c51372016-09-15 11:09:45 -0700143 GrGradientEffect::CreateArgs(args.fContext, this, args.fLocalMatrix, fTileMode,
144 std::move(colorSpaceXform), SkToBool(args.fDstColorSpace))));
Brian Salomon6af27012017-06-09 08:21:42 -0400145 if (!inner) {
146 return nullptr;
147 }
bungeman06ca8ec2016-06-09 08:01:03 -0700148 return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner));
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000149}
150
twiz@google.coma5e65ec2012-08-02 15:15:16 +0000151#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000152
Matt Sarett6cc6ae752017-04-18 18:29:12 -0400153sk_sp<SkShader> SkTwoPointConicalGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
154 SkSTArray<8, SkColor> origColorsStorage(fColorCount);
155 SkSTArray<8, SkScalar> origPosStorage(fColorCount);
156 SkSTArray<8, SkColor> xformedColorsStorage(fColorCount);
157 SkColor* origColors = origColorsStorage.begin();
158 SkScalar* origPos = fOrigPos ? origPosStorage.begin() : nullptr;
159 SkColor* xformedColors = xformedColorsStorage.begin();
160
161 // Flip if necessary
162 SkPoint center1 = fFlippedGrad ? fCenter2 : fCenter1;
163 SkPoint center2 = fFlippedGrad ? fCenter1 : fCenter2;
164 SkScalar radius1 = fFlippedGrad ? fRadius2 : fRadius1;
165 SkScalar radius2 = fFlippedGrad ? fRadius1 : fRadius2;
166 for (int i = 0; i < fColorCount; i++) {
167 origColors[i] = fFlippedGrad ? fOrigColors[fColorCount - i - 1] : fOrigColors[i];
168 if (origPos) {
169 origPos[i] = fFlippedGrad ? 1.0f - fOrigPos[fColorCount - i - 1] : fOrigPos[i];
170 }
171 }
172
173 xformer->apply(xformedColors, origColors, fColorCount);
174 return SkGradientShader::MakeTwoPointConical(center1, radius1, center2, radius2, xformedColors,
175 origPos, fColorCount, fTileMode, fGradFlags,
176 &this->getLocalMatrix());
177}
178
179
commit-bot@chromium.org0f10f7b2014-03-13 18:02:17 +0000180#ifndef SK_IGNORE_TO_STRING
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000181void SkTwoPointConicalGradient::toString(SkString* str) const {
182 str->append("SkTwoPointConicalGradient: (");
183
184 str->append("center1: (");
185 str->appendScalar(fCenter1.fX);
186 str->append(", ");
187 str->appendScalar(fCenter1.fY);
188 str->append(") radius1: ");
189 str->appendScalar(fRadius1);
190 str->append(" ");
191
192 str->append("center2: (");
193 str->appendScalar(fCenter2.fX);
194 str->append(", ");
195 str->appendScalar(fCenter2.fY);
196 str->append(") radius2: ");
197 str->appendScalar(fRadius2);
198 str->append(" ");
199
200 this->INHERITED::toString(str);
201
202 str->append(")");
203}
204#endif
Florin Malita0bb04112017-06-27 14:35:50 -0400205
206bool SkTwoPointConicalGradient::adjustMatrixAndAppendStages(SkArenaAlloc* alloc,
207 SkMatrix* matrix,
Florin Malita2e409002017-06-28 14:46:54 -0400208 SkRasterPipeline* p,
209 SkRasterPipeline* postPipeline) const {
Florin Malita9c2212f2017-07-29 18:23:10 -0400210 matrix->postConcat(fPtsToUnit);
211
Florin Malitaa66ef2d2017-06-28 10:02:40 -0400212 const auto dRadius = fRadius2 - fRadius1;
213 SkASSERT(dRadius >= 0);
214
Florin Malita9c2212f2017-07-29 18:23:10 -0400215 if (fType == Type::kRadial) {
Florin Malita0bb04112017-06-27 14:35:50 -0400216 p->append(SkRasterPipeline::xy_to_radius);
217
218 // Tiny twist: radial computes a t for [0, r2], but we want a t for [r1, r2].
Florin Malitaa66ef2d2017-06-28 10:02:40 -0400219 auto scale = fRadius2 / dRadius;
220 auto bias = -fRadius1 / dRadius;
Florin Malita0bb04112017-06-27 14:35:50 -0400221
Mike Reed6b59bf42017-07-03 21:26:44 -0400222 p->append_matrix(alloc, SkMatrix::Concat(SkMatrix::MakeTrans(bias, 0),
223 SkMatrix::MakeScale(scale, 1)));
Florin Malita0bb04112017-06-27 14:35:50 -0400224
225 return true;
226 }
227
Florin Malita9c2212f2017-07-29 18:23:10 -0400228 const auto dCenter = (fCenter1 - fCenter2).length();
Florin Malitaa66ef2d2017-06-28 10:02:40 -0400229
230 // Since we've squashed the centers into a unit vector, we must also scale
231 // all the coefficient variables by (1 / dCenter).
232 const auto coeffA = 1 - dRadius * dRadius / (dCenter * dCenter);
Florin Malitaa66ef2d2017-06-28 10:02:40 -0400233 auto* ctx = alloc->make<SkJumper_2PtConicalCtx>();
234 ctx->fCoeffA = coeffA;
235 ctx->fInvCoeffA = 1 / coeffA;
236 ctx->fR0 = fRadius1 / dCenter;
237 ctx->fDR = dRadius / dCenter;
238
Florin Malita9026fe12017-06-29 11:03:45 -0400239 // Is the solver guaranteed to not produce degenerates?
240 bool isWellBehaved = true;
241
Florin Malita2e409002017-06-28 14:46:54 -0400242 if (SkScalarNearlyZero(coeffA)) {
243 // The focal point is on the edge of the end circle.
244 p->append(SkRasterPipeline::xy_to_2pt_conical_linear, ctx);
Florin Malita9026fe12017-06-29 11:03:45 -0400245 isWellBehaved = false;
Florin Malita2e409002017-06-28 14:46:54 -0400246 } else {
Florin Malita9026fe12017-06-29 11:03:45 -0400247 if (dCenter + fRadius1 > fRadius2) {
248 // The focal point is outside the end circle.
249
250 // We want the larger root, per spec:
251 // "For all values of ω where r(ω) > 0, starting with the value of ω nearest
252 // to positive infinity and ending with the value of ω nearest to negative
253 // infinity, draw the circumference of the circle with radius r(ω) at position
254 // (x(ω), y(ω)), with the color at ω, but only painting on the parts of the
255 // bitmap that have not yet been painted on by earlier circles in this step for
256 // this rendering of the gradient."
257 // (https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-createradialgradient)
258 p->append(fFlippedGrad ? SkRasterPipeline::xy_to_2pt_conical_quadratic_min
259 : SkRasterPipeline::xy_to_2pt_conical_quadratic_max, ctx);
260 isWellBehaved = false;
261 } else {
262 // The focal point is inside (well-behaved case).
263 p->append(SkRasterPipeline::xy_to_2pt_conical_quadratic_max, ctx);
264 }
265 }
266
267 if (!isWellBehaved) {
268 p->append(SkRasterPipeline::mask_2pt_conical_degenerates, ctx);
269 postPipeline->append(SkRasterPipeline::apply_vector_mask, &ctx->fMask);
Florin Malita2e409002017-06-28 14:46:54 -0400270 }
271
Florin Malitaa66ef2d2017-06-28 10:02:40 -0400272 return true;
Florin Malita0bb04112017-06-27 14:35:50 -0400273}