xref: /aosp_15_r20/external/skia/src/shaders/gradients/SkLinearGradient.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 "src/shaders/gradients/SkLinearGradient.h"
9 
10 #include "include/core/SkColor.h"
11 #include "include/core/SkColorSpace.h"
12 #include "include/core/SkMatrix.h"
13 #include "include/core/SkRefCnt.h"
14 #include "include/core/SkScalar.h"
15 #include "include/core/SkShader.h"
16 #include "include/effects/SkGradientShader.h"
17 #include "include/private/base/SkFloatingPoint.h"
18 #include "include/private/base/SkTArray.h"
19 #include "src/core/SkReadBuffer.h"
20 #include "src/core/SkWriteBuffer.h"
21 #include "src/shaders/SkShaderBase.h"
22 
23 #include <cstdint>
24 #include <utility>
25 
26 class SkArenaAlloc;
27 class SkRasterPipeline;
28 enum class SkTileMode;
29 
pts_to_unit_matrix(const SkPoint pts[2])30 static SkMatrix pts_to_unit_matrix(const SkPoint pts[2]) {
31     SkVector    vec = pts[1] - pts[0];
32     SkScalar    mag = vec.length();
33     SkScalar    inv = mag ? SkScalarInvert(mag) : 0;
34 
35     vec.scale(inv);
36     SkMatrix matrix;
37     matrix.setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
38     matrix.postTranslate(-pts[0].fX, -pts[0].fY);
39     matrix.postScale(inv, inv);
40     return matrix;
41 }
42 
43 ///////////////////////////////////////////////////////////////////////////////
44 
SkLinearGradient(const SkPoint pts[2],const Descriptor & desc)45 SkLinearGradient::SkLinearGradient(const SkPoint pts[2], const Descriptor& desc)
46         : SkGradientBaseShader(desc, pts_to_unit_matrix(pts)), fStart(pts[0]), fEnd(pts[1]) {}
47 
CreateProc(SkReadBuffer & buffer)48 sk_sp<SkFlattenable> SkLinearGradient::CreateProc(SkReadBuffer& buffer) {
49     DescriptorScope desc;
50     SkMatrix legacyLocalMatrix, *lmPtr = nullptr;
51     if (!desc.unflatten(buffer, &legacyLocalMatrix)) {
52         return nullptr;
53     }
54     if (!legacyLocalMatrix.isIdentity()) {
55         lmPtr = &legacyLocalMatrix;
56     }
57     SkPoint pts[2];
58     pts[0] = buffer.readPoint();
59     pts[1] = buffer.readPoint();
60     return SkGradientShader::MakeLinear(pts,
61                                         desc.fColors,
62                                         std::move(desc.fColorSpace),
63                                         desc.fPositions,
64                                         desc.fColorCount,
65                                         desc.fTileMode,
66                                         desc.fInterpolation,
67                                         lmPtr);
68 }
69 
flatten(SkWriteBuffer & buffer) const70 void SkLinearGradient::flatten(SkWriteBuffer& buffer) const {
71     this->INHERITED::flatten(buffer);
72     buffer.writePoint(fStart);
73     buffer.writePoint(fEnd);
74 }
75 
appendGradientStages(SkArenaAlloc *,SkRasterPipeline *,SkRasterPipeline *) const76 void SkLinearGradient::appendGradientStages(SkArenaAlloc*, SkRasterPipeline*,
77                                             SkRasterPipeline*) const {
78     // No extra stage needed for linear gradients.
79 }
80 
asGradient(GradientInfo * info,SkMatrix * localMatrix) const81 SkShaderBase::GradientType SkLinearGradient::asGradient(GradientInfo* info,
82                                                         SkMatrix* localMatrix) const {
83     if (info) {
84         commonAsAGradient(info);
85         info->fPoint[0] = fStart;
86         info->fPoint[1] = fEnd;
87     }
88     if (localMatrix) {
89         *localMatrix = SkMatrix::I();
90     }
91     return GradientType::kLinear;
92 }
93 
MakeLinear(const SkPoint pts[2],const SkColor4f colors[],sk_sp<SkColorSpace> colorSpace,const SkScalar pos[],int colorCount,SkTileMode mode,const Interpolation & interpolation,const SkMatrix * localMatrix)94 sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2],
95                                              const SkColor4f colors[],
96                                              sk_sp<SkColorSpace> colorSpace,
97                                              const SkScalar pos[],
98                                              int colorCount,
99                                              SkTileMode mode,
100                                              const Interpolation& interpolation,
101                                              const SkMatrix* localMatrix) {
102     if (!pts || !SkIsFinite((pts[1] - pts[0]).length())) {
103         return nullptr;
104     }
105     if (!SkGradientBaseShader::ValidGradient(colors, colorCount, mode, interpolation)) {
106         return nullptr;
107     }
108     if (1 == colorCount) {
109         return SkShaders::Color(colors[0], std::move(colorSpace));
110     }
111     if (localMatrix && !localMatrix->invert(nullptr)) {
112         return nullptr;
113     }
114 
115     if (SkScalarNearlyZero((pts[1] - pts[0]).length(),
116                            SkGradientBaseShader::kDegenerateThreshold)) {
117         // Degenerate gradient, the only tricky complication is when in clamp mode, the limit of
118         // the gradient approaches two half planes of solid color (first and last). However, they
119         // are divided by the line perpendicular to the start and end point, which becomes undefined
120         // once start and end are exactly the same, so just use the end color for a stable solution.
121         return SkGradientBaseShader::MakeDegenerateGradient(
122                 colors, pos, colorCount, std::move(colorSpace), mode);
123     }
124 
125     SkGradientBaseShader::Descriptor desc(
126             colors, std::move(colorSpace), pos, colorCount, mode, interpolation);
127 
128     sk_sp<SkShader> s = sk_make_sp<SkLinearGradient>(pts, desc);
129     return s->makeWithLocalMatrix(localMatrix ? *localMatrix : SkMatrix::I());
130 }
131 
MakeLinear(const SkPoint pts[2],const SkColor colors[],const SkScalar pos[],int colorCount,SkTileMode mode,uint32_t flags,const SkMatrix * localMatrix)132 sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2],
133                                              const SkColor colors[],
134                                              const SkScalar pos[],
135                                              int colorCount,
136                                              SkTileMode mode,
137                                              uint32_t flags,
138                                              const SkMatrix* localMatrix) {
139     SkColorConverter converter(colors, colorCount);
140     return MakeLinear(pts, converter.fColors4f.begin(), nullptr, pos, colorCount, mode, flags,
141                       localMatrix);
142 }
143 
SkRegisterLinearGradientShaderFlattenable()144 void SkRegisterLinearGradientShaderFlattenable() {
145     SK_REGISTER_FLATTENABLE(SkLinearGradient);
146 }
147