1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2012 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "include/private/SkFloatingPoint.h"
9cb93a386Sopenharmony_ci#include "src/core/SkRasterPipeline.h"
10cb93a386Sopenharmony_ci#include "src/core/SkReadBuffer.h"
11cb93a386Sopenharmony_ci#include "src/core/SkWriteBuffer.h"
12cb93a386Sopenharmony_ci#include "src/shaders/gradients/SkTwoPointConicalGradient.h"
13cb93a386Sopenharmony_ci
14cb93a386Sopenharmony_ci#include <utility>
15cb93a386Sopenharmony_ci
16cb93a386Sopenharmony_ci// Please see https://skia.org/dev/design/conical for how our shader works.
17cb93a386Sopenharmony_ci
18cb93a386Sopenharmony_cibool SkTwoPointConicalGradient::FocalData::set(SkScalar r0, SkScalar r1, SkMatrix* matrix) {
19cb93a386Sopenharmony_ci    fIsSwapped = false;
20cb93a386Sopenharmony_ci    fFocalX = sk_ieee_float_divide(r0, (r0 - r1));
21cb93a386Sopenharmony_ci    if (SkScalarNearlyZero(fFocalX - 1)) {
22cb93a386Sopenharmony_ci        // swap r0, r1
23cb93a386Sopenharmony_ci        matrix->postTranslate(-1, 0);
24cb93a386Sopenharmony_ci        matrix->postScale(-1, 1);
25cb93a386Sopenharmony_ci        std::swap(r0, r1);
26cb93a386Sopenharmony_ci        fFocalX = 0; // because r0 is now 0
27cb93a386Sopenharmony_ci        fIsSwapped = true;
28cb93a386Sopenharmony_ci    }
29cb93a386Sopenharmony_ci
30cb93a386Sopenharmony_ci    // Map {focal point, (1, 0)} to {(0, 0), (1, 0)}
31cb93a386Sopenharmony_ci    const SkPoint from[2]   = { {fFocalX, 0}, {1, 0} };
32cb93a386Sopenharmony_ci    const SkPoint to[2]     = { {0, 0}, {1, 0} };
33cb93a386Sopenharmony_ci    SkMatrix focalMatrix;
34cb93a386Sopenharmony_ci    if (!focalMatrix.setPolyToPoly(from, to, 2)) {
35cb93a386Sopenharmony_ci        return false;
36cb93a386Sopenharmony_ci    }
37cb93a386Sopenharmony_ci    matrix->postConcat(focalMatrix);
38cb93a386Sopenharmony_ci    fR1 = r1 / SkScalarAbs(1 - fFocalX); // focalMatrix has a scale of 1/(1-f)
39cb93a386Sopenharmony_ci
40cb93a386Sopenharmony_ci    // The following transformations are just to accelerate the shader computation by saving
41cb93a386Sopenharmony_ci    // some arithmatic operations.
42cb93a386Sopenharmony_ci    if (this->isFocalOnCircle()) {
43cb93a386Sopenharmony_ci        matrix->postScale(0.5, 0.5);
44cb93a386Sopenharmony_ci    } else {
45cb93a386Sopenharmony_ci        matrix->postScale(fR1 / (fR1 * fR1 - 1), 1 / sqrt(SkScalarAbs(fR1 * fR1 - 1)));
46cb93a386Sopenharmony_ci    }
47cb93a386Sopenharmony_ci    matrix->postScale(SkScalarAbs(1 - fFocalX), SkScalarAbs(1 - fFocalX)); // scale |1 - f|
48cb93a386Sopenharmony_ci    return true;
49cb93a386Sopenharmony_ci}
50cb93a386Sopenharmony_ci
51cb93a386Sopenharmony_cisk_sp<SkShader> SkTwoPointConicalGradient::Create(const SkPoint& c0, SkScalar r0,
52cb93a386Sopenharmony_ci                                                  const SkPoint& c1, SkScalar r1,
53cb93a386Sopenharmony_ci                                                  const Descriptor& desc) {
54cb93a386Sopenharmony_ci    SkMatrix gradientMatrix;
55cb93a386Sopenharmony_ci    Type     gradientType;
56cb93a386Sopenharmony_ci
57cb93a386Sopenharmony_ci    if (SkScalarNearlyZero((c0 - c1).length())) {
58cb93a386Sopenharmony_ci        if (SkScalarNearlyZero(std::max(r0, r1)) || SkScalarNearlyEqual(r0, r1)) {
59cb93a386Sopenharmony_ci            // Degenerate case; avoid dividing by zero. Should have been caught by caller but
60cb93a386Sopenharmony_ci            // just in case, recheck here.
61cb93a386Sopenharmony_ci            return nullptr;
62cb93a386Sopenharmony_ci        }
63cb93a386Sopenharmony_ci        // Concentric case: we can pretend we're radial (with a tiny twist).
64cb93a386Sopenharmony_ci        const SkScalar scale = sk_ieee_float_divide(1, std::max(r0, r1));
65cb93a386Sopenharmony_ci        gradientMatrix = SkMatrix::Translate(-c1.x(), -c1.y());
66cb93a386Sopenharmony_ci        gradientMatrix.postScale(scale, scale);
67cb93a386Sopenharmony_ci
68cb93a386Sopenharmony_ci        gradientType = Type::kRadial;
69cb93a386Sopenharmony_ci    } else {
70cb93a386Sopenharmony_ci        const SkPoint centers[2] = { c0    , c1     };
71cb93a386Sopenharmony_ci        const SkPoint unitvec[2] = { {0, 0}, {1, 0} };
72cb93a386Sopenharmony_ci
73cb93a386Sopenharmony_ci        if (!gradientMatrix.setPolyToPoly(centers, unitvec, 2)) {
74cb93a386Sopenharmony_ci            // Degenerate case.
75cb93a386Sopenharmony_ci            return nullptr;
76cb93a386Sopenharmony_ci        }
77cb93a386Sopenharmony_ci
78cb93a386Sopenharmony_ci        gradientType = SkScalarNearlyZero(r1 - r0) ? Type::kStrip : Type::kFocal;
79cb93a386Sopenharmony_ci    }
80cb93a386Sopenharmony_ci
81cb93a386Sopenharmony_ci    FocalData focalData;
82cb93a386Sopenharmony_ci    if (gradientType == Type::kFocal) {
83cb93a386Sopenharmony_ci        const auto dCenter = (c0 - c1).length();
84cb93a386Sopenharmony_ci        if (!focalData.set(r0 / dCenter, r1 / dCenter, &gradientMatrix)) {
85cb93a386Sopenharmony_ci            return nullptr;
86cb93a386Sopenharmony_ci        }
87cb93a386Sopenharmony_ci    }
88cb93a386Sopenharmony_ci    return sk_sp<SkShader>(new SkTwoPointConicalGradient(c0, r0, c1, r1, desc,
89cb93a386Sopenharmony_ci                                                         gradientType, gradientMatrix, focalData));
90cb93a386Sopenharmony_ci}
91cb93a386Sopenharmony_ci
92cb93a386Sopenharmony_ciSkTwoPointConicalGradient::SkTwoPointConicalGradient(
93cb93a386Sopenharmony_ci        const SkPoint& start, SkScalar startRadius,
94cb93a386Sopenharmony_ci        const SkPoint& end, SkScalar endRadius,
95cb93a386Sopenharmony_ci        const Descriptor& desc, Type type, const SkMatrix& gradientMatrix, const FocalData& data)
96cb93a386Sopenharmony_ci    : SkGradientShaderBase(desc, gradientMatrix)
97cb93a386Sopenharmony_ci    , fCenter1(start)
98cb93a386Sopenharmony_ci    , fCenter2(end)
99cb93a386Sopenharmony_ci    , fRadius1(startRadius)
100cb93a386Sopenharmony_ci    , fRadius2(endRadius)
101cb93a386Sopenharmony_ci    , fType(type)
102cb93a386Sopenharmony_ci{
103cb93a386Sopenharmony_ci    // this is degenerate, and should be caught by our caller
104cb93a386Sopenharmony_ci    SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2);
105cb93a386Sopenharmony_ci    if (type == Type::kFocal) {
106cb93a386Sopenharmony_ci        fFocalData = data;
107cb93a386Sopenharmony_ci    }
108cb93a386Sopenharmony_ci}
109cb93a386Sopenharmony_ci
110cb93a386Sopenharmony_cibool SkTwoPointConicalGradient::isOpaque() const {
111cb93a386Sopenharmony_ci    // Because areas outside the cone are left untouched, we cannot treat the
112cb93a386Sopenharmony_ci    // shader as opaque even if the gradient itself is opaque.
113cb93a386Sopenharmony_ci    // TODO(junov): Compute whether the cone fills the plane crbug.com/222380
114cb93a386Sopenharmony_ci    return false;
115cb93a386Sopenharmony_ci}
116cb93a386Sopenharmony_ci
117cb93a386Sopenharmony_ci// Returns the original non-sorted version of the gradient
118cb93a386Sopenharmony_ciSkShader::GradientType SkTwoPointConicalGradient::asAGradient(GradientInfo* info) const {
119cb93a386Sopenharmony_ci    if (info) {
120cb93a386Sopenharmony_ci        commonAsAGradient(info);
121cb93a386Sopenharmony_ci        info->fPoint[0] = fCenter1;
122cb93a386Sopenharmony_ci        info->fPoint[1] = fCenter2;
123cb93a386Sopenharmony_ci        info->fRadius[0] = fRadius1;
124cb93a386Sopenharmony_ci        info->fRadius[1] = fRadius2;
125cb93a386Sopenharmony_ci    }
126cb93a386Sopenharmony_ci    return kConical_GradientType;
127cb93a386Sopenharmony_ci}
128cb93a386Sopenharmony_ci
129cb93a386Sopenharmony_cisk_sp<SkFlattenable> SkTwoPointConicalGradient::CreateProc(SkReadBuffer& buffer) {
130cb93a386Sopenharmony_ci    DescriptorScope desc;
131cb93a386Sopenharmony_ci    if (!desc.unflatten(buffer)) {
132cb93a386Sopenharmony_ci        return nullptr;
133cb93a386Sopenharmony_ci    }
134cb93a386Sopenharmony_ci    SkPoint c1 = buffer.readPoint();
135cb93a386Sopenharmony_ci    SkPoint c2 = buffer.readPoint();
136cb93a386Sopenharmony_ci    SkScalar r1 = buffer.readScalar();
137cb93a386Sopenharmony_ci    SkScalar r2 = buffer.readScalar();
138cb93a386Sopenharmony_ci
139cb93a386Sopenharmony_ci    if (!buffer.isValid()) {
140cb93a386Sopenharmony_ci        return nullptr;
141cb93a386Sopenharmony_ci    }
142cb93a386Sopenharmony_ci    return SkGradientShader::MakeTwoPointConical(c1, r1, c2, r2, desc.fColors,
143cb93a386Sopenharmony_ci                                                 std::move(desc.fColorSpace), desc.fPos,
144cb93a386Sopenharmony_ci                                                 desc.fCount, desc.fTileMode, desc.fGradFlags,
145cb93a386Sopenharmony_ci                                                 desc.fLocalMatrix);
146cb93a386Sopenharmony_ci}
147cb93a386Sopenharmony_ci
148cb93a386Sopenharmony_civoid SkTwoPointConicalGradient::flatten(SkWriteBuffer& buffer) const {
149cb93a386Sopenharmony_ci    this->INHERITED::flatten(buffer);
150cb93a386Sopenharmony_ci    buffer.writePoint(fCenter1);
151cb93a386Sopenharmony_ci    buffer.writePoint(fCenter2);
152cb93a386Sopenharmony_ci    buffer.writeScalar(fRadius1);
153cb93a386Sopenharmony_ci    buffer.writeScalar(fRadius2);
154cb93a386Sopenharmony_ci}
155cb93a386Sopenharmony_ci
156cb93a386Sopenharmony_civoid SkTwoPointConicalGradient::appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline* p,
157cb93a386Sopenharmony_ci                                                     SkRasterPipeline* postPipeline) const {
158cb93a386Sopenharmony_ci    const auto dRadius = fRadius2 - fRadius1;
159cb93a386Sopenharmony_ci
160cb93a386Sopenharmony_ci    if (fType == Type::kRadial) {
161cb93a386Sopenharmony_ci        p->append(SkRasterPipeline::xy_to_radius);
162cb93a386Sopenharmony_ci
163cb93a386Sopenharmony_ci        // Tiny twist: radial computes a t for [0, r2], but we want a t for [r1, r2].
164cb93a386Sopenharmony_ci        auto scale =  std::max(fRadius1, fRadius2) / dRadius;
165cb93a386Sopenharmony_ci        auto bias  = -fRadius1 / dRadius;
166cb93a386Sopenharmony_ci
167cb93a386Sopenharmony_ci        p->append_matrix(alloc, SkMatrix::Translate(bias, 0) * SkMatrix::Scale(scale, 1));
168cb93a386Sopenharmony_ci        return;
169cb93a386Sopenharmony_ci    }
170cb93a386Sopenharmony_ci
171cb93a386Sopenharmony_ci    if (fType == Type::kStrip) {
172cb93a386Sopenharmony_ci        auto* ctx = alloc->make<SkRasterPipeline_2PtConicalCtx>();
173cb93a386Sopenharmony_ci        SkScalar scaledR0 = fRadius1 / this->getCenterX1();
174cb93a386Sopenharmony_ci        ctx->fP0 = scaledR0 * scaledR0;
175cb93a386Sopenharmony_ci        p->append(SkRasterPipeline::xy_to_2pt_conical_strip, ctx);
176cb93a386Sopenharmony_ci        p->append(SkRasterPipeline::mask_2pt_conical_nan, ctx);
177cb93a386Sopenharmony_ci        postPipeline->append(SkRasterPipeline::apply_vector_mask, &ctx->fMask);
178cb93a386Sopenharmony_ci        return;
179cb93a386Sopenharmony_ci    }
180cb93a386Sopenharmony_ci
181cb93a386Sopenharmony_ci    auto* ctx = alloc->make<SkRasterPipeline_2PtConicalCtx>();
182cb93a386Sopenharmony_ci    ctx->fP0 = 1/fFocalData.fR1;
183cb93a386Sopenharmony_ci    ctx->fP1 = fFocalData.fFocalX;
184cb93a386Sopenharmony_ci
185cb93a386Sopenharmony_ci    if (fFocalData.isFocalOnCircle()) {
186cb93a386Sopenharmony_ci        p->append(SkRasterPipeline::xy_to_2pt_conical_focal_on_circle);
187cb93a386Sopenharmony_ci    } else if (fFocalData.isWellBehaved()) {
188cb93a386Sopenharmony_ci        p->append(SkRasterPipeline::xy_to_2pt_conical_well_behaved, ctx);
189cb93a386Sopenharmony_ci    } else if (fFocalData.isSwapped() || 1 - fFocalData.fFocalX < 0) {
190cb93a386Sopenharmony_ci        p->append(SkRasterPipeline::xy_to_2pt_conical_smaller, ctx);
191cb93a386Sopenharmony_ci    } else {
192cb93a386Sopenharmony_ci        p->append(SkRasterPipeline::xy_to_2pt_conical_greater, ctx);
193cb93a386Sopenharmony_ci    }
194cb93a386Sopenharmony_ci
195cb93a386Sopenharmony_ci    if (!fFocalData.isWellBehaved()) {
196cb93a386Sopenharmony_ci        p->append(SkRasterPipeline::mask_2pt_conical_degenerates, ctx);
197cb93a386Sopenharmony_ci    }
198cb93a386Sopenharmony_ci    if (1 - fFocalData.fFocalX < 0) {
199cb93a386Sopenharmony_ci        p->append(SkRasterPipeline::negate_x);
200cb93a386Sopenharmony_ci    }
201cb93a386Sopenharmony_ci    if (!fFocalData.isNativelyFocal()) {
202cb93a386Sopenharmony_ci        p->append(SkRasterPipeline::alter_2pt_conical_compensate_focal, ctx);
203cb93a386Sopenharmony_ci    }
204cb93a386Sopenharmony_ci    if (fFocalData.isSwapped()) {
205cb93a386Sopenharmony_ci        p->append(SkRasterPipeline::alter_2pt_conical_unswap);
206cb93a386Sopenharmony_ci    }
207cb93a386Sopenharmony_ci    if (!fFocalData.isWellBehaved()) {
208cb93a386Sopenharmony_ci        postPipeline->append(SkRasterPipeline::apply_vector_mask, &ctx->fMask);
209cb93a386Sopenharmony_ci    }
210cb93a386Sopenharmony_ci}
211cb93a386Sopenharmony_ci
212cb93a386Sopenharmony_ciskvm::F32 SkTwoPointConicalGradient::transformT(skvm::Builder* p, skvm::Uniforms* uniforms,
213cb93a386Sopenharmony_ci                                                skvm::Coord coord, skvm::I32* mask) const {
214cb93a386Sopenharmony_ci    auto mag = [](skvm::F32 x, skvm::F32 y) { return sqrt(x*x + y*y); };
215cb93a386Sopenharmony_ci
216cb93a386Sopenharmony_ci    // See https://skia.org/dev/design/conical, and onAppendStages() above.
217cb93a386Sopenharmony_ci    // There's a lot going on here, and I'm not really sure what's independent
218cb93a386Sopenharmony_ci    // or disjoint, what can be reordered, simplified, etc.  Tweak carefully.
219cb93a386Sopenharmony_ci
220cb93a386Sopenharmony_ci    const skvm::F32 x = coord.x,
221cb93a386Sopenharmony_ci                    y = coord.y;
222cb93a386Sopenharmony_ci    if (fType == Type::kRadial) {
223cb93a386Sopenharmony_ci        float denom = 1.0f / (fRadius2 - fRadius1),
224cb93a386Sopenharmony_ci              scale = std::max(fRadius1, fRadius2) * denom,
225cb93a386Sopenharmony_ci               bias =                  -fRadius1 * denom;
226cb93a386Sopenharmony_ci        return mag(x,y) * p->uniformF(uniforms->pushF(scale))
227cb93a386Sopenharmony_ci                        + p->uniformF(uniforms->pushF(bias ));
228cb93a386Sopenharmony_ci    }
229cb93a386Sopenharmony_ci
230cb93a386Sopenharmony_ci    if (fType == Type::kStrip) {
231cb93a386Sopenharmony_ci        float r = fRadius1 / this->getCenterX1();
232cb93a386Sopenharmony_ci        skvm::F32 t = x + sqrt(p->splat(r*r) - y*y);
233cb93a386Sopenharmony_ci
234cb93a386Sopenharmony_ci        *mask = (t == t);   // t != NaN
235cb93a386Sopenharmony_ci        return t;
236cb93a386Sopenharmony_ci    }
237cb93a386Sopenharmony_ci
238cb93a386Sopenharmony_ci    const skvm::F32 invR1 = p->uniformF(uniforms->pushF(1 / fFocalData.fR1));
239cb93a386Sopenharmony_ci
240cb93a386Sopenharmony_ci    skvm::F32 t;
241cb93a386Sopenharmony_ci    if (fFocalData.isFocalOnCircle()) {
242cb93a386Sopenharmony_ci        t = (y/x) * y + x;       // (x^2 + y^2) / x  ~~>  x + y^2/x  ~~>  y/x * y + x
243cb93a386Sopenharmony_ci    } else if (fFocalData.isWellBehaved()) {
244cb93a386Sopenharmony_ci        t = mag(x,y) - x*invR1;
245cb93a386Sopenharmony_ci    } else {
246cb93a386Sopenharmony_ci        skvm::F32 k = sqrt(x*x - y*y);
247cb93a386Sopenharmony_ci        if (fFocalData.isSwapped() || 1 - fFocalData.fFocalX < 0) {
248cb93a386Sopenharmony_ci            k = -k;
249cb93a386Sopenharmony_ci        }
250cb93a386Sopenharmony_ci        t = k - x*invR1;
251cb93a386Sopenharmony_ci    }
252cb93a386Sopenharmony_ci
253cb93a386Sopenharmony_ci    if (!fFocalData.isWellBehaved()) {
254cb93a386Sopenharmony_ci        // TODO: not sure why we consider t == 0 degenerate
255cb93a386Sopenharmony_ci        *mask = (t > 0.0f);  // and implicitly, t != NaN
256cb93a386Sopenharmony_ci    }
257cb93a386Sopenharmony_ci
258cb93a386Sopenharmony_ci    const skvm::F32 focalX = p->uniformF(uniforms->pushF(fFocalData.fFocalX));
259cb93a386Sopenharmony_ci    if (1 - fFocalData.fFocalX < 0)    { t = -t; }
260cb93a386Sopenharmony_ci    if (!fFocalData.isNativelyFocal()) { t += focalX; }
261cb93a386Sopenharmony_ci    if ( fFocalData.isSwapped())       { t = 1.0f - t; }
262cb93a386Sopenharmony_ci    return t;
263cb93a386Sopenharmony_ci}
264cb93a386Sopenharmony_ci
265cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////
266cb93a386Sopenharmony_ci
267cb93a386Sopenharmony_ci#if SK_SUPPORT_GPU
268cb93a386Sopenharmony_ci
269cb93a386Sopenharmony_ci#include "src/gpu/gradients/GrGradientShader.h"
270cb93a386Sopenharmony_ci
271cb93a386Sopenharmony_cistd::unique_ptr<GrFragmentProcessor> SkTwoPointConicalGradient::asFragmentProcessor(
272cb93a386Sopenharmony_ci        const GrFPArgs& args) const {
273cb93a386Sopenharmony_ci    return GrGradientShader::MakeConical(*this, args);
274cb93a386Sopenharmony_ci}
275cb93a386Sopenharmony_ci
276cb93a386Sopenharmony_ci#endif
277