blob: d98f4bce20f745ff1d3ebb8dec8df509c0e89052 [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
Mike Reed0b510fb2018-05-03 12:50:38 -04008#include "SkFloatingPoint.h"
Florin Malitaa66ef2d2017-06-28 10:02:40 -04009#include "SkRasterPipeline.h"
Florin Malitad4e9ec82017-10-25 18:00:26 -040010#include "SkReadBuffer.h"
Mike Reed0b510fb2018-05-03 12:50:38 -040011#include "SkTwoPointConicalGradient.h"
Florin Malitad4e9ec82017-10-25 18:00:26 -040012#include "SkWriteBuffer.h"
Florin Malitaa66ef2d2017-06-28 10:02:40 -040013#include "../../jumper/SkJumper.h"
14
Yuqian Lid208a882018-01-04 10:08:42 -050015// Please see https://skia.org/dev/design/conical for how our shader works.
16
Mike Reeda2f14de2018-05-09 14:01:00 -040017bool SkTwoPointConicalGradient::FocalData::set(SkScalar r0, SkScalar r1, SkMatrix* matrix) {
Yuqian Lid208a882018-01-04 10:08:42 -050018 fIsSwapped = false;
Mike Reeda2f14de2018-05-09 14:01:00 -040019 fFocalX = sk_ieee_float_divide(r0, (r0 - r1));
Yuqian Lid208a882018-01-04 10:08:42 -050020 if (SkScalarNearlyZero(fFocalX - 1)) {
21 // swap r0, r1
Mike Reeda2f14de2018-05-09 14:01:00 -040022 matrix->postTranslate(-1, 0);
23 matrix->postScale(-1, 1);
Yuqian Lid208a882018-01-04 10:08:42 -050024 std::swap(r0, r1);
25 fFocalX = 0; // because r0 is now 0
26 fIsSwapped = true;
27 }
28
29 // Map {focal point, (1, 0)} to {(0, 0), (1, 0)}
30 const SkPoint from[2] = { {fFocalX, 0}, {1, 0} };
31 const SkPoint to[2] = { {0, 0}, {1, 0} };
32 SkMatrix focalMatrix;
33 if (!focalMatrix.setPolyToPoly(from, to, 2)) {
Mike Reeda2f14de2018-05-09 14:01:00 -040034 return false;
Yuqian Lid208a882018-01-04 10:08:42 -050035 }
Mike Reeda2f14de2018-05-09 14:01:00 -040036 matrix->postConcat(focalMatrix);
Yuqian Lid208a882018-01-04 10:08:42 -050037 fR1 = r1 / SkScalarAbs(1 - fFocalX); // focalMatrix has a scale of 1/(1-f)
38
39 // The following transformations are just to accelerate the shader computation by saving
40 // some arithmatic operations.
41 if (this->isFocalOnCircle()) {
Mike Reeda2f14de2018-05-09 14:01:00 -040042 matrix->postScale(0.5, 0.5);
Yuqian Lid208a882018-01-04 10:08:42 -050043 } else {
Mike Reeda2f14de2018-05-09 14:01:00 -040044 matrix->postScale(fR1 / (fR1 * fR1 - 1), 1 / sqrt(SkScalarAbs(fR1 * fR1 - 1)));
Yuqian Lid208a882018-01-04 10:08:42 -050045 }
Mike Reeda2f14de2018-05-09 14:01:00 -040046 matrix->postScale(SkScalarAbs(1 - fFocalX), SkScalarAbs(1 - fFocalX)); // scale |1 - f|
47 return true;
Yuqian Lid208a882018-01-04 10:08:42 -050048}
49
Florin Malita9c2212f2017-07-29 18:23:10 -040050sk_sp<SkShader> SkTwoPointConicalGradient::Create(const SkPoint& c0, SkScalar r0,
51 const SkPoint& c1, SkScalar r1,
Florin Malita5f379a82017-10-18 16:22:35 -040052 const Descriptor& desc) {
Florin Malita9c2212f2017-07-29 18:23:10 -040053 SkMatrix gradientMatrix;
54 Type gradientType;
55
56 if (SkScalarNearlyZero((c0 - c1).length())) {
Yuqian Li45a6d712018-05-14 09:39:24 -070057 if (SkScalarNearlyZero(SkTMax(r0, r1))) {
58 return nullptr; // Degenerate case; avoid dividing by zero.
59 }
Florin Malita9c2212f2017-07-29 18:23:10 -040060 // Concentric case: we can pretend we're radial (with a tiny twist).
Mike Reed0b510fb2018-05-03 12:50:38 -040061 const SkScalar scale = sk_ieee_float_divide(1, SkTMax(r0, r1));
Florin Malita9c2212f2017-07-29 18:23:10 -040062 gradientMatrix = SkMatrix::MakeTrans(-c1.x(), -c1.y());
Florin Malita5f379a82017-10-18 16:22:35 -040063 gradientMatrix.postScale(scale, scale);
Florin Malita9c2212f2017-07-29 18:23:10 -040064
65 gradientType = Type::kRadial;
66 } else {
67 const SkPoint centers[2] = { c0 , c1 };
68 const SkPoint unitvec[2] = { {0, 0}, {1, 0} };
69
70 if (!gradientMatrix.setPolyToPoly(centers, unitvec, 2)) {
71 // Degenerate case.
72 return nullptr;
73 }
74
Yuqian Lid208a882018-01-04 10:08:42 -050075 gradientType = SkScalarNearlyZero(r1 - r0) ? Type::kStrip : Type::kFocal;
Florin Malita9c2212f2017-07-29 18:23:10 -040076 }
77
Yuqian Lid208a882018-01-04 10:08:42 -050078 FocalData focalData;
79 if (gradientType == Type::kFocal) {
80 const auto dCenter = (c0 - c1).length();
Mike Reeda2f14de2018-05-09 14:01:00 -040081 if (!focalData.set(r0 / dCenter, r1 / dCenter, &gradientMatrix)) {
82 return nullptr;
83 }
Yuqian Lid208a882018-01-04 10:08:42 -050084 }
Florin Malita5f379a82017-10-18 16:22:35 -040085 return sk_sp<SkShader>(new SkTwoPointConicalGradient(c0, r0, c1, r1, desc,
Yuqian Lid208a882018-01-04 10:08:42 -050086 gradientType, gradientMatrix, focalData));
Florin Malita9c2212f2017-07-29 18:23:10 -040087}
88
rileya@google.com589708b2012-07-26 20:04:23 +000089SkTwoPointConicalGradient::SkTwoPointConicalGradient(
reed@google.com3d3a8602013-05-24 14:58:44 +000090 const SkPoint& start, SkScalar startRadius,
91 const SkPoint& end, SkScalar endRadius,
Yuqian Lid208a882018-01-04 10:08:42 -050092 const Descriptor& desc, Type type, const SkMatrix& gradientMatrix, const FocalData& data)
Florin Malita9c2212f2017-07-29 18:23:10 -040093 : SkGradientShaderBase(desc, gradientMatrix)
reedaddf2ed2014-08-11 08:28:24 -070094 , fCenter1(start)
95 , fCenter2(end)
96 , fRadius1(startRadius)
97 , fRadius2(endRadius)
Florin Malita9c2212f2017-07-29 18:23:10 -040098 , fType(type)
reedaddf2ed2014-08-11 08:28:24 -070099{
rileya@google.com589708b2012-07-26 20:04:23 +0000100 // this is degenerate, and should be caught by our caller
101 SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2);
Yuqian Lid208a882018-01-04 10:08:42 -0500102 if (type == Type::kFocal) {
103 fFocalData = data;
104 }
rileya@google.com589708b2012-07-26 20:04:23 +0000105}
106
commit-bot@chromium.org3fbab822013-03-20 00:49:57 +0000107bool SkTwoPointConicalGradient::isOpaque() const {
robertphillips@google.comcb6d97c2013-07-09 13:50:09 +0000108 // Because areas outside the cone are left untouched, we cannot treat the
109 // shader as opaque even if the gradient itself is opaque.
110 // TODO(junov): Compute whether the cone fills the plane crbug.com/222380
111 return false;
commit-bot@chromium.org3fbab822013-03-20 00:49:57 +0000112}
113
commit-bot@chromium.org44d83c12014-04-21 13:10:25 +0000114// Returns the original non-sorted version of the gradient
Florin Malita5f379a82017-10-18 16:22:35 -0400115SkShader::GradientType SkTwoPointConicalGradient::asAGradient(GradientInfo* info) const {
rileya@google.com589708b2012-07-26 20:04:23 +0000116 if (info) {
Florin Malita5f379a82017-10-18 16:22:35 -0400117 commonAsAGradient(info);
rileya@google.com589708b2012-07-26 20:04:23 +0000118 info->fPoint[0] = fCenter1;
119 info->fPoint[1] = fCenter2;
120 info->fRadius[0] = fRadius1;
121 info->fRadius[1] = fRadius2;
122 }
123 return kConical_GradientType;
124}
125
reed60c9b582016-04-03 09:11:13 -0700126sk_sp<SkFlattenable> SkTwoPointConicalGradient::CreateProc(SkReadBuffer& buffer) {
reed9fa60da2014-08-21 07:59:51 -0700127 DescriptorScope desc;
128 if (!desc.unflatten(buffer)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700129 return nullptr;
reed9fa60da2014-08-21 07:59:51 -0700130 }
131 SkPoint c1 = buffer.readPoint();
132 SkPoint c2 = buffer.readPoint();
133 SkScalar r1 = buffer.readScalar();
134 SkScalar r2 = buffer.readScalar();
135
Florin Malita5f379a82017-10-18 16:22:35 -0400136 if (buffer.isVersionLT(SkReadBuffer::k2PtConicalNoFlip_Version) && buffer.readBool()) {
137 // legacy flipped gradient
reed9fa60da2014-08-21 07:59:51 -0700138 SkTSwap(c1, c2);
139 SkTSwap(r1, r2);
140
brianosmane25d71c2016-09-28 11:27:28 -0700141 SkColor4f* colors = desc.mutableColors();
reed9fa60da2014-08-21 07:59:51 -0700142 SkScalar* pos = desc.mutablePos();
143 const int last = desc.fCount - 1;
144 const int half = desc.fCount >> 1;
145 for (int i = 0; i < half; ++i) {
146 SkTSwap(colors[i], colors[last - i]);
147 if (pos) {
148 SkScalar tmp = pos[i];
149 pos[i] = SK_Scalar1 - pos[last - i];
150 pos[last - i] = SK_Scalar1 - tmp;
151 }
152 }
153 if (pos) {
154 if (desc.fCount & 1) {
155 pos[half] = SK_Scalar1 - pos[half];
156 }
157 }
158 }
159
brianosmane25d71c2016-09-28 11:27:28 -0700160 return SkGradientShader::MakeTwoPointConical(c1, r1, c2, r2, desc.fColors,
161 std::move(desc.fColorSpace), desc.fPos,
reed8a21c9f2016-03-08 18:50:00 -0800162 desc.fCount, desc.fTileMode, desc.fGradFlags,
reed60c9b582016-04-03 09:11:13 -0700163 desc.fLocalMatrix);
reed9fa60da2014-08-21 07:59:51 -0700164}
165
166void SkTwoPointConicalGradient::flatten(SkWriteBuffer& buffer) const {
rileya@google.com589708b2012-07-26 20:04:23 +0000167 this->INHERITED::flatten(buffer);
168 buffer.writePoint(fCenter1);
169 buffer.writePoint(fCenter2);
170 buffer.writeScalar(fRadius1);
171 buffer.writeScalar(fRadius2);
172}
173
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000174#if SK_SUPPORT_GPU
175
dandov9de5b512014-06-10 14:38:28 -0700176#include "SkGr.h"
brianosman9557c272016-09-15 06:59:15 -0700177#include "SkTwoPointConicalGradient_gpu.h"
dandov9de5b512014-06-10 14:38:28 -0700178
Brian Salomonaff329b2017-08-11 09:40:37 -0400179std::unique_ptr<GrFragmentProcessor> SkTwoPointConicalGradient::asFragmentProcessor(
Mike Reede3429e62018-01-19 11:43:34 -0500180 const GrFPArgs& args) const {
Florin Malitac6c5ead2018-04-11 15:33:40 -0400181 SkMatrix matrix;
182 if (!this->totalLocalMatrix(args.fPreLocalMatrix, args.fPostLocalMatrix)->invert(&matrix)) {
183 return nullptr;
184 }
185
Brian Salomon4cbb6e62017-10-25 15:12:19 -0400186 return Gr2PtConicalGradientEffect::Make(
Florin Malitac6c5ead2018-04-11 15:33:40 -0400187 GrGradientEffect::CreateArgs(args.fContext, this, &matrix, fTileMode,
Brian Salomon4cbb6e62017-10-25 15:12:19 -0400188 args.fDstColorSpaceInfo->colorSpace()));
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000189}
190
twiz@google.coma5e65ec2012-08-02 15:15:16 +0000191#endif
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000192
Matt Sarett6cc6ae752017-04-18 18:29:12 -0400193sk_sp<SkShader> SkTwoPointConicalGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
Florin Malita39d71de2017-10-31 11:33:49 -0400194 const AutoXformColors xformedColors(*this, xformer);
Florin Malita5f379a82017-10-18 16:22:35 -0400195 return SkGradientShader::MakeTwoPointConical(fCenter1, fRadius1, fCenter2, fRadius2,
Florin Malita39d71de2017-10-31 11:33:49 -0400196 xformedColors.fColors.get(), fOrigPos, fColorCount,
Florin Malita5f379a82017-10-18 16:22:35 -0400197 fTileMode, fGradFlags, &this->getLocalMatrix());
Matt Sarett6cc6ae752017-04-18 18:29:12 -0400198}
199
200
robertphillips@google.com76f9e932013-01-15 20:17:47 +0000201void SkTwoPointConicalGradient::toString(SkString* str) const {
202 str->append("SkTwoPointConicalGradient: (");
203
204 str->append("center1: (");
205 str->appendScalar(fCenter1.fX);
206 str->append(", ");
207 str->appendScalar(fCenter1.fY);
208 str->append(") radius1: ");
209 str->appendScalar(fRadius1);
210 str->append(" ");
211
212 str->append("center2: (");
213 str->appendScalar(fCenter2.fX);
214 str->append(", ");
215 str->appendScalar(fCenter2.fY);
216 str->append(") radius2: ");
217 str->appendScalar(fRadius2);
218 str->append(" ");
219
220 this->INHERITED::toString(str);
221
222 str->append(")");
223}
Florin Malita0bb04112017-06-27 14:35:50 -0400224
Florin Malita50b20842017-07-29 19:08:28 -0400225void SkTwoPointConicalGradient::appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline* p,
226 SkRasterPipeline* postPipeline) const {
Florin Malitaa66ef2d2017-06-28 10:02:40 -0400227 const auto dRadius = fRadius2 - fRadius1;
Florin Malitaa66ef2d2017-06-28 10:02:40 -0400228
Florin Malita9c2212f2017-07-29 18:23:10 -0400229 if (fType == Type::kRadial) {
Florin Malita0bb04112017-06-27 14:35:50 -0400230 p->append(SkRasterPipeline::xy_to_radius);
231
232 // Tiny twist: radial computes a t for [0, r2], but we want a t for [r1, r2].
Florin Malita5f379a82017-10-18 16:22:35 -0400233 auto scale = SkTMax(fRadius1, fRadius2) / dRadius;
Florin Malitaa66ef2d2017-06-28 10:02:40 -0400234 auto bias = -fRadius1 / dRadius;
Florin Malita0bb04112017-06-27 14:35:50 -0400235
Mike Reed6b59bf42017-07-03 21:26:44 -0400236 p->append_matrix(alloc, SkMatrix::Concat(SkMatrix::MakeTrans(bias, 0),
237 SkMatrix::MakeScale(scale, 1)));
Florin Malita50b20842017-07-29 19:08:28 -0400238 return;
Florin Malita0bb04112017-06-27 14:35:50 -0400239 }
240
Yuqian Lid208a882018-01-04 10:08:42 -0500241 if (fType == Type::kStrip) {
242 auto* ctx = alloc->make<SkJumper_2PtConicalCtx>();
243 SkScalar scaledR0 = fRadius1 / this->getCenterX1();
244 ctx->fP0 = scaledR0 * scaledR0;
245 p->append(SkRasterPipeline::xy_to_2pt_conical_strip, ctx);
246 p->append(SkRasterPipeline::mask_2pt_conical_nan, ctx);
247 postPipeline->append(SkRasterPipeline::apply_vector_mask, &ctx->fMask);
248 return;
249 }
250
251 auto* ctx = alloc->make<SkJumper_2PtConicalCtx>();
252 ctx->fP0 = 1/fFocalData.fR1;
253 ctx->fP1 = fFocalData.fFocalX;
254
255 if (fFocalData.isFocalOnCircle()) {
256 p->append(SkRasterPipeline::xy_to_2pt_conical_focal_on_circle);
257 } else if (fFocalData.isWellBehaved()) {
258 p->append(SkRasterPipeline::xy_to_2pt_conical_well_behaved, ctx);
259 } else if (fFocalData.isSwapped() || 1 - fFocalData.fFocalX < 0) {
260 p->append(SkRasterPipeline::xy_to_2pt_conical_smaller, ctx);
261 } else {
262 p->append(SkRasterPipeline::xy_to_2pt_conical_greater, ctx);
263 }
264
265 if (!fFocalData.isWellBehaved()) {
266 p->append(SkRasterPipeline::mask_2pt_conical_degenerates, ctx);
267 }
268 if (1 - fFocalData.fFocalX < 0) {
269 p->append(SkRasterPipeline::negate_x);
270 }
271 if (!fFocalData.isNativelyFocal()) {
272 p->append(SkRasterPipeline::alter_2pt_conical_compensate_focal, ctx);
273 }
274 if (fFocalData.isSwapped()) {
275 p->append(SkRasterPipeline::alter_2pt_conical_unswap);
276 }
277 if (!fFocalData.isWellBehaved()) {
278 postPipeline->append(SkRasterPipeline::apply_vector_mask, &ctx->fMask);
279 }
Florin Malita0bb04112017-06-27 14:35:50 -0400280}