blob: e9c32c8601450ffc5534127ed3e0786406136370 [file] [log] [blame]
rileya@google.com589708b2012-07-26 20:04:23 +00001/*
2 * Copyright 2006 The Android Open Source Project
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
Herb Derby4de13042017-05-15 10:49:39 -04008#include <algorithm>
fmalitabc590c02016-02-22 09:12:33 -08009#include "Sk4fLinearGradient.h"
Mike Kleine28a6b52018-07-25 13:05:17 -040010#include "SkColorSpacePriv.h"
Brian Salomon5dfcf132018-10-12 14:39:32 +000011#include "SkConvertPixels.h"
Florin Malitacad3b8c2017-10-28 21:42:50 -040012#include "SkFloatBits.h"
rileya@google.com589708b2012-07-26 20:04:23 +000013#include "SkGradientShaderPriv.h"
brianosmand4546092016-09-22 12:31:58 -070014#include "SkHalf.h"
rileya@google.com589708b2012-07-26 20:04:23 +000015#include "SkLinearGradient.h"
Mike Reed6b3155c2017-04-03 14:41:44 -040016#include "SkMallocPixelRef.h"
rileya@google.com589708b2012-07-26 20:04:23 +000017#include "SkRadialGradient.h"
Florin Malitad4e9ec82017-10-25 18:00:26 -040018#include "SkReadBuffer.h"
Mike Klein02ab8cc2017-05-04 22:41:05 +000019#include "SkSweepGradient.h"
Mike Kleina3771842017-05-04 19:38:48 -040020#include "SkTwoPointConicalGradient.h"
Florin Malitad4e9ec82017-10-25 18:00:26 -040021#include "SkWriteBuffer.h"
rileya@google.com589708b2012-07-26 20:04:23 +000022
brianosmane25d71c2016-09-28 11:27:28 -070023enum GradientSerializationFlags {
24 // Bits 29:31 used for various boolean flags
25 kHasPosition_GSF = 0x80000000,
26 kHasLocalMatrix_GSF = 0x40000000,
27 kHasColorSpace_GSF = 0x20000000,
28
29 // Bits 12:28 unused
30
31 // Bits 8:11 for fTileMode
32 kTileModeShift_GSF = 8,
33 kTileModeMask_GSF = 0xF,
34
35 // Bits 0:7 for fGradFlags (note that kForce4fContext_PrivateFlag is 0x80)
36 kGradFlagsShift_GSF = 0,
37 kGradFlagsMask_GSF = 0xFF,
38};
39
reed9fa60da2014-08-21 07:59:51 -070040void SkGradientShaderBase::Descriptor::flatten(SkWriteBuffer& buffer) const {
brianosmane25d71c2016-09-28 11:27:28 -070041 uint32_t flags = 0;
reed9fa60da2014-08-21 07:59:51 -070042 if (fPos) {
brianosmane25d71c2016-09-28 11:27:28 -070043 flags |= kHasPosition_GSF;
reed9fa60da2014-08-21 07:59:51 -070044 }
reed9fa60da2014-08-21 07:59:51 -070045 if (fLocalMatrix) {
brianosmane25d71c2016-09-28 11:27:28 -070046 flags |= kHasLocalMatrix_GSF;
47 }
48 sk_sp<SkData> colorSpaceData = fColorSpace ? fColorSpace->serialize() : nullptr;
49 if (colorSpaceData) {
50 flags |= kHasColorSpace_GSF;
51 }
52 SkASSERT(static_cast<uint32_t>(fTileMode) <= kTileModeMask_GSF);
Mike Reedfae8fce2019-04-03 10:27:45 -040053 flags |= ((unsigned)fTileMode << kTileModeShift_GSF);
brianosmane25d71c2016-09-28 11:27:28 -070054 SkASSERT(fGradFlags <= kGradFlagsMask_GSF);
55 flags |= (fGradFlags << kGradFlagsShift_GSF);
56
57 buffer.writeUInt(flags);
58
59 buffer.writeColor4fArray(fColors, fCount);
60 if (colorSpaceData) {
61 buffer.writeDataAsByteArray(colorSpaceData.get());
62 }
63 if (fPos) {
64 buffer.writeScalarArray(fPos, fCount);
65 }
66 if (fLocalMatrix) {
reed9fa60da2014-08-21 07:59:51 -070067 buffer.writeMatrix(*fLocalMatrix);
reed9fa60da2014-08-21 07:59:51 -070068 }
69}
70
Florin Malitaf77db112018-05-10 09:52:27 -040071template <int N, typename T, bool MEM_MOVE>
72static bool validate_array(SkReadBuffer& buffer, size_t count, SkSTArray<N, T, MEM_MOVE>* array) {
Kevin Lubickdaebae92018-05-17 11:29:10 -040073 if (!buffer.validateCanReadN<T>(count)) {
Florin Malitaf77db112018-05-10 09:52:27 -040074 return false;
75 }
76
77 array->resize_back(count);
78 return true;
79}
80
reed9fa60da2014-08-21 07:59:51 -070081bool SkGradientShaderBase::DescriptorScope::unflatten(SkReadBuffer& buffer) {
Mike Reed70bc94f2017-06-08 12:45:52 -040082 // New gradient format. Includes floating point color, color space, densely packed flags
83 uint32_t flags = buffer.readUInt();
reed9fa60da2014-08-21 07:59:51 -070084
Mike Reedfae8fce2019-04-03 10:27:45 -040085 fTileMode = (SkTileMode)((flags >> kTileModeShift_GSF) & kTileModeMask_GSF);
Mike Reed70bc94f2017-06-08 12:45:52 -040086 fGradFlags = (flags >> kGradFlagsShift_GSF) & kGradFlagsMask_GSF;
reed9fa60da2014-08-21 07:59:51 -070087
Mike Reed70bc94f2017-06-08 12:45:52 -040088 fCount = buffer.getArrayCount();
Florin Malitaf77db112018-05-10 09:52:27 -040089
90 if (!(validate_array(buffer, fCount, &fColorStorage) &&
91 buffer.readColor4fArray(fColorStorage.begin(), fCount))) {
Mike Reed70bc94f2017-06-08 12:45:52 -040092 return false;
93 }
Florin Malitaf77db112018-05-10 09:52:27 -040094 fColors = fColorStorage.begin();
95
Mike Reed70bc94f2017-06-08 12:45:52 -040096 if (SkToBool(flags & kHasColorSpace_GSF)) {
97 sk_sp<SkData> data = buffer.readByteArrayAsData();
Florin Malitac2ea3272018-05-10 09:41:38 -040098 fColorSpace = data ? SkColorSpace::Deserialize(data->data(), data->size()) : nullptr;
Mike Reed70bc94f2017-06-08 12:45:52 -040099 } else {
brianosmane25d71c2016-09-28 11:27:28 -0700100 fColorSpace = nullptr;
Mike Reed70bc94f2017-06-08 12:45:52 -0400101 }
102 if (SkToBool(flags & kHasPosition_GSF)) {
Florin Malitaf77db112018-05-10 09:52:27 -0400103 if (!(validate_array(buffer, fCount, &fPosStorage) &&
104 buffer.readScalarArray(fPosStorage.begin(), fCount))) {
Mike Reed70bc94f2017-06-08 12:45:52 -0400105 return false;
brianosmane25d71c2016-09-28 11:27:28 -0700106 }
Florin Malitaf77db112018-05-10 09:52:27 -0400107 fPos = fPosStorage.begin();
reed9fa60da2014-08-21 07:59:51 -0700108 } else {
Mike Reed70bc94f2017-06-08 12:45:52 -0400109 fPos = nullptr;
110 }
111 if (SkToBool(flags & kHasLocalMatrix_GSF)) {
112 fLocalMatrix = &fLocalMatrixStorage;
113 buffer.readMatrix(&fLocalMatrixStorage);
114 } else {
115 fLocalMatrix = nullptr;
reed9fa60da2014-08-21 07:59:51 -0700116 }
117 return buffer.isValid();
118}
119
120////////////////////////////////////////////////////////////////////////////////////////////
121
mtkleincc695fe2014-12-10 10:29:19 -0800122SkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc, const SkMatrix& ptsToUnit)
reedaddf2ed2014-08-11 08:28:24 -0700123 : INHERITED(desc.fLocalMatrix)
mtkleincc695fe2014-12-10 10:29:19 -0800124 , fPtsToUnit(ptsToUnit)
Brian Osman6667fb12018-07-03 16:44:02 -0400125 , fColorSpace(desc.fColorSpace ? desc.fColorSpace : SkColorSpace::MakeSRGB())
Florin Malita39d71de2017-10-31 11:33:49 -0400126 , fColorsAreOpaque(true)
commit-bot@chromium.org9c9005a2014-04-28 14:55:39 +0000127{
mtkleincc695fe2014-12-10 10:29:19 -0800128 fPtsToUnit.getType(); // Precache so reads are threadsafe.
reed@google.com437d6eb2013-05-23 19:03:05 +0000129 SkASSERT(desc.fCount > 1);
rileya@google.com589708b2012-07-26 20:04:23 +0000130
fmalita6d7e4e82016-09-20 06:55:16 -0700131 fGradFlags = static_cast<uint8_t>(desc.fGradFlags);
rileya@google.com589708b2012-07-26 20:04:23 +0000132
Mike Reedfae8fce2019-04-03 10:27:45 -0400133 SkASSERT((unsigned)desc.fTileMode < kSkTileModeCount);
reed@google.com437d6eb2013-05-23 19:03:05 +0000134 fTileMode = desc.fTileMode;
rileya@google.com589708b2012-07-26 20:04:23 +0000135
rileya@google.com589708b2012-07-26 20:04:23 +0000136 /* Note: we let the caller skip the first and/or last position.
137 i.e. pos[0] = 0.3, pos[1] = 0.7
138 In these cases, we insert dummy entries to ensure that the final data
139 will be bracketed by [0, 1].
140 i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
141
142 Thus colorCount (the caller's value, and fColorCount (our value) may
143 differ by up to 2. In the above example:
144 colorCount = 2
145 fColorCount = 4
146 */
reed@google.com437d6eb2013-05-23 19:03:05 +0000147 fColorCount = desc.fCount;
rileya@google.com589708b2012-07-26 20:04:23 +0000148 // check if we need to add in dummy start and/or end position/colors
149 bool dummyFirst = false;
150 bool dummyLast = false;
reed@google.com437d6eb2013-05-23 19:03:05 +0000151 if (desc.fPos) {
152 dummyFirst = desc.fPos[0] != 0;
153 dummyLast = desc.fPos[desc.fCount - 1] != SK_Scalar1;
rileya@google.com589708b2012-07-26 20:04:23 +0000154 fColorCount += dummyFirst + dummyLast;
155 }
156
Mike Reed62ce2ca2018-02-19 14:20:15 -0500157 size_t storageSize = fColorCount * (sizeof(SkColor4f) + (desc.fPos ? sizeof(SkScalar) : 0));
Florin Malita89ab2402017-11-01 10:14:57 -0400158 fOrigColors4f = reinterpret_cast<SkColor4f*>(fStorage.reset(storageSize));
Mike Reed62ce2ca2018-02-19 14:20:15 -0500159 fOrigPos = desc.fPos ? reinterpret_cast<SkScalar*>(fOrigColors4f + fColorCount)
160 : nullptr;
rileya@google.com589708b2012-07-26 20:04:23 +0000161
brianosmane25d71c2016-09-28 11:27:28 -0700162 // Now copy over the colors, adding the dummies as needed
163 SkColor4f* origColors = fOrigColors4f;
164 if (dummyFirst) {
165 *origColors++ = desc.fColors[0];
166 }
Florin Malita39d71de2017-10-31 11:33:49 -0400167 for (int i = 0; i < desc.fCount; ++i) {
Mike Reed62ce2ca2018-02-19 14:20:15 -0500168 origColors[i] = desc.fColors[i];
Florin Malita39d71de2017-10-31 11:33:49 -0400169 fColorsAreOpaque = fColorsAreOpaque && (desc.fColors[i].fA == 1);
170 }
brianosmane25d71c2016-09-28 11:27:28 -0700171 if (dummyLast) {
Mike Reed62ce2ca2018-02-19 14:20:15 -0500172 origColors += desc.fCount;
173 *origColors = desc.fColors[desc.fCount - 1];
brianosmane25d71c2016-09-28 11:27:28 -0700174 }
brianosmanb9c51372016-09-15 11:09:45 -0700175
Florin Malita89ab2402017-11-01 10:14:57 -0400176 if (desc.fPos) {
Florin Malita64bb78e2017-11-03 12:54:07 -0400177 SkScalar prev = 0;
Mike Reed62ce2ca2018-02-19 14:20:15 -0500178 SkScalar* origPosPtr = fOrigPos;
Florin Malita64bb78e2017-11-03 12:54:07 -0400179 *origPosPtr++ = prev; // force the first pos to 0
reed9fa60da2014-08-21 07:59:51 -0700180
Florin Malita89ab2402017-11-01 10:14:57 -0400181 int startIndex = dummyFirst ? 0 : 1;
182 int count = desc.fCount + dummyLast;
Florin Malita64bb78e2017-11-03 12:54:07 -0400183
184 bool uniformStops = true;
185 const SkScalar uniformStep = desc.fPos[startIndex] - prev;
Florin Malita89ab2402017-11-01 10:14:57 -0400186 for (int i = startIndex; i < count; i++) {
Florin Malita3e20d022017-11-03 12:11:38 -0400187 // Pin the last value to 1.0, and make sure pos is monotonic.
Florin Malita64bb78e2017-11-03 12:54:07 -0400188 auto curr = (i == desc.fCount) ? 1 : SkScalarPin(desc.fPos[i], prev, 1);
189 uniformStops &= SkScalarNearlyEqual(uniformStep, curr - prev);
190
191 *origPosPtr++ = prev = curr;
reed9fa60da2014-08-21 07:59:51 -0700192 }
Florin Malita64bb78e2017-11-03 12:54:07 -0400193
Florin Malita64bb78e2017-11-03 12:54:07 -0400194 // If the stops are uniform, treat them as implicit.
Mike Reed62ce2ca2018-02-19 14:20:15 -0500195 if (uniformStops) {
Florin Malita64bb78e2017-11-03 12:54:07 -0400196 fOrigPos = nullptr;
197 }
rileya@google.com589708b2012-07-26 20:04:23 +0000198 }
rileya@google.com589708b2012-07-26 20:04:23 +0000199}
200
Florin Malita89ab2402017-11-01 10:14:57 -0400201SkGradientShaderBase::~SkGradientShaderBase() {}
rileya@google.com589708b2012-07-26 20:04:23 +0000202
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +0000203void SkGradientShaderBase::flatten(SkWriteBuffer& buffer) const {
reed9fa60da2014-08-21 07:59:51 -0700204 Descriptor desc;
brianosmane25d71c2016-09-28 11:27:28 -0700205 desc.fColors = fOrigColors4f;
brianosmanb9c51372016-09-15 11:09:45 -0700206 desc.fColorSpace = fColorSpace;
reed9fa60da2014-08-21 07:59:51 -0700207 desc.fPos = fOrigPos;
208 desc.fCount = fColorCount;
209 desc.fTileMode = fTileMode;
210 desc.fGradFlags = fGradFlags;
211
212 const SkMatrix& m = this->getLocalMatrix();
halcanary96fcdcc2015-08-27 07:41:13 -0700213 desc.fLocalMatrix = m.isIdentity() ? nullptr : &m;
reed9fa60da2014-08-21 07:59:51 -0700214 desc.flatten(buffer);
rileya@google.com589708b2012-07-26 20:04:23 +0000215}
216
Mike Kleinb11ab572018-10-24 06:42:14 -0400217static void add_stop_color(SkRasterPipeline_GradientCtx* ctx, size_t stop, SkPMColor4f Fs, SkPMColor4f Bs) {
Brian Osman781e3502018-10-03 15:42:47 -0400218 (ctx->fs[0])[stop] = Fs.fR;
219 (ctx->fs[1])[stop] = Fs.fG;
220 (ctx->fs[2])[stop] = Fs.fB;
221 (ctx->fs[3])[stop] = Fs.fA;
222 (ctx->bs[0])[stop] = Bs.fR;
223 (ctx->bs[1])[stop] = Bs.fG;
224 (ctx->bs[2])[stop] = Bs.fB;
225 (ctx->bs[3])[stop] = Bs.fA;
Mike Kleinf945cbb2017-05-17 09:30:58 -0400226}
Herb Derbyeb99bfd2017-05-16 10:51:26 -0400227
Mike Kleinb11ab572018-10-24 06:42:14 -0400228static void add_const_color(SkRasterPipeline_GradientCtx* ctx, size_t stop, SkPMColor4f color) {
Brian Osman781e3502018-10-03 15:42:47 -0400229 add_stop_color(ctx, stop, { 0, 0, 0, 0 }, color);
Mike Kleinf945cbb2017-05-17 09:30:58 -0400230}
Herb Derbyeb99bfd2017-05-16 10:51:26 -0400231
232// Calculate a factor F and a bias B so that color = F*t + B when t is in range of
233// the stop. Assume that the distance between stops is 1/gapCount.
234static void init_stop_evenly(
Mike Kleinb11ab572018-10-24 06:42:14 -0400235 SkRasterPipeline_GradientCtx* ctx, float gapCount, size_t stop, SkPMColor4f c_l, SkPMColor4f c_r) {
Mike Klein68768172017-05-17 09:54:36 -0400236 // Clankium's GCC 4.9 targeting ARMv7 is barfing when we use Sk4f math here, so go scalar...
Brian Osman781e3502018-10-03 15:42:47 -0400237 SkPMColor4f Fs = {
238 (c_r.fR - c_l.fR) * gapCount,
239 (c_r.fG - c_l.fG) * gapCount,
240 (c_r.fB - c_l.fB) * gapCount,
241 (c_r.fA - c_l.fA) * gapCount,
242 };
243 SkPMColor4f Bs = {
244 c_l.fR - Fs.fR*(stop/gapCount),
245 c_l.fG - Fs.fG*(stop/gapCount),
246 c_l.fB - Fs.fB*(stop/gapCount),
247 c_l.fA - Fs.fA*(stop/gapCount),
248 };
Herb Derbyeb99bfd2017-05-16 10:51:26 -0400249 add_stop_color(ctx, stop, Fs, Bs);
Mike Kleinf945cbb2017-05-17 09:30:58 -0400250}
Herb Derbyeb99bfd2017-05-16 10:51:26 -0400251
252// For each stop we calculate a bias B and a scale factor F, such that
253// for any t between stops n and n+1, the color we want is B[n] + F[n]*t.
254static void init_stop_pos(
Mike Kleinb11ab572018-10-24 06:42:14 -0400255 SkRasterPipeline_GradientCtx* ctx, size_t stop, float t_l, float t_r, SkPMColor4f c_l, SkPMColor4f c_r) {
Mike Klein68768172017-05-17 09:54:36 -0400256 // See note about Clankium's old compiler in init_stop_evenly().
Brian Osman781e3502018-10-03 15:42:47 -0400257 SkPMColor4f Fs = {
258 (c_r.fR - c_l.fR) / (t_r - t_l),
259 (c_r.fG - c_l.fG) / (t_r - t_l),
260 (c_r.fB - c_l.fB) / (t_r - t_l),
261 (c_r.fA - c_l.fA) / (t_r - t_l),
262 };
263 SkPMColor4f Bs = {
264 c_l.fR - Fs.fR*t_l,
265 c_l.fG - Fs.fG*t_l,
266 c_l.fB - Fs.fB*t_l,
267 c_l.fA - Fs.fA*t_l,
268 };
Herb Derbyeb99bfd2017-05-16 10:51:26 -0400269 ctx->ts[stop] = t_l;
270 add_stop_color(ctx, stop, Fs, Bs);
Mike Kleinf945cbb2017-05-17 09:30:58 -0400271}
Herb Derbyeb99bfd2017-05-16 10:51:26 -0400272
Mike Reed1386b2d2019-03-13 21:15:05 -0400273bool SkGradientShaderBase::onAppendStages(const SkStageRec& rec) const {
Mike Reed1d8c42e2017-08-29 14:58:19 -0400274 SkRasterPipeline* p = rec.fPipeline;
275 SkArenaAlloc* alloc = rec.fAlloc;
Mike Kleinb11ab572018-10-24 06:42:14 -0400276 SkRasterPipeline_DecalTileCtx* decal_ctx = nullptr;
Mike Reed1d8c42e2017-08-29 14:58:19 -0400277
Mike Kleina3771842017-05-04 19:38:48 -0400278 SkMatrix matrix;
Mike Reed1d8c42e2017-08-29 14:58:19 -0400279 if (!this->computeTotalInverse(rec.fCTM, rec.fLocalM, &matrix)) {
Mike Kleina3771842017-05-04 19:38:48 -0400280 return false;
281 }
Florin Malita50b20842017-07-29 19:08:28 -0400282 matrix.postConcat(fPtsToUnit);
Mike Kleina3771842017-05-04 19:38:48 -0400283
Florin Malita2e409002017-06-28 14:46:54 -0400284 SkRasterPipeline_<256> postPipeline;
Mike Kleina3771842017-05-04 19:38:48 -0400285
Mike Kleine8de0242018-03-10 12:37:11 -0500286 p->append(SkRasterPipeline::seed_shader);
Mike Reed6b59bf42017-07-03 21:26:44 -0400287 p->append_matrix(alloc, matrix);
Florin Malita50b20842017-07-29 19:08:28 -0400288 this->appendGradientStages(alloc, p, &postPipeline);
Mike Kleine7598532017-05-11 11:29:29 -0400289
Mike Reed62ce2ca2018-02-19 14:20:15 -0500290 switch(fTileMode) {
Mike Reedfae8fce2019-04-03 10:27:45 -0400291 case SkTileMode::kMirror: p->append(SkRasterPipeline::mirror_x_1); break;
292 case SkTileMode::kRepeat: p->append(SkRasterPipeline::repeat_x_1); break;
293 case SkTileMode::kDecal:
Mike Kleinb11ab572018-10-24 06:42:14 -0400294 decal_ctx = alloc->make<SkRasterPipeline_DecalTileCtx>();
Mike Reed62ce2ca2018-02-19 14:20:15 -0500295 decal_ctx->limit_x = SkBits2Float(SkFloat2Bits(1.0f) + 1);
296 // reuse mask + limit_x stage, or create a custom decal_1 that just stores the mask
297 p->append(SkRasterPipeline::decal_x, decal_ctx);
298 // fall-through to clamp
Mike Reedfae8fce2019-04-03 10:27:45 -0400299 case SkTileMode::kClamp:
Mike Kleine7598532017-05-11 11:29:29 -0400300 if (!fOrigPos) {
301 // We clamp only when the stops are evenly spaced.
302 // If not, there may be hard stops, and clamping ruins hard stops at 0 and/or 1.
Mike Klein5c7960b2017-05-11 10:59:22 -0400303 // In that case, we must make sure we're using the general "gradient" stage,
Mike Kleine7598532017-05-11 11:29:29 -0400304 // which is the only stage that will correctly handle unclamped t.
Mike Klein9f85d682017-05-23 07:52:01 -0400305 p->append(SkRasterPipeline::clamp_x_1);
Mike Kleine7598532017-05-11 11:29:29 -0400306 }
Mike Reed62ce2ca2018-02-19 14:20:15 -0500307 break;
Mike Kleine7598532017-05-11 11:29:29 -0400308 }
Mike Kleina3771842017-05-04 19:38:48 -0400309
310 const bool premulGrad = fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag;
Brian Osman6667fb12018-07-03 16:44:02 -0400311
312 // Transform all of the colors to destination color space
313 SkColor4fXformer xformedColors(fOrigColors4f, fColorCount, fColorSpace.get(), rec.fDstCS);
314
315 auto prepareColor = [premulGrad, &xformedColors](int i) {
316 SkColor4f c = xformedColors.fColors[i];
Brian Osman781e3502018-10-03 15:42:47 -0400317 return premulGrad ? c.premul()
318 : SkPMColor4f{ c.fR, c.fG, c.fB, c.fA };
Mike Kleina3771842017-05-04 19:38:48 -0400319 };
320
321 // The two-stop case with stops at 0 and 1.
322 if (fColorCount == 2 && fOrigPos == nullptr) {
Brian Osman781e3502018-10-03 15:42:47 -0400323 const SkPMColor4f c_l = prepareColor(0),
324 c_r = prepareColor(1);
Mike Kleina3771842017-05-04 19:38:48 -0400325
326 // See F and B below.
Mike Kleinb11ab572018-10-24 06:42:14 -0400327 auto ctx = alloc->make<SkRasterPipeline_EvenlySpaced2StopGradientCtx>();
Brian Osman781e3502018-10-03 15:42:47 -0400328 (Sk4f::Load(c_r.vec()) - Sk4f::Load(c_l.vec())).store(ctx->f);
329 ( Sk4f::Load(c_l.vec())).store(ctx->b);
Mike Klein24de6482018-09-07 12:05:29 -0400330 ctx->interpolatedInPremul = premulGrad;
Mike Kleina3771842017-05-04 19:38:48 -0400331
Mike Klein24de6482018-09-07 12:05:29 -0400332 p->append(SkRasterPipeline::evenly_spaced_2_stop_gradient, ctx);
Mike Kleina3771842017-05-04 19:38:48 -0400333 } else {
Mike Kleinb11ab572018-10-24 06:42:14 -0400334 auto* ctx = alloc->make<SkRasterPipeline_GradientCtx>();
Mike Klein24de6482018-09-07 12:05:29 -0400335 ctx->interpolatedInPremul = premulGrad;
Herb Derby4de13042017-05-15 10:49:39 -0400336
337 // Note: In order to handle clamps in search, the search assumes a stop conceptully placed
338 // at -inf. Therefore, the max number of stops is fColorCount+1.
339 for (int i = 0; i < 4; i++) {
340 // Allocate at least at for the AVX2 gather from a YMM register.
341 ctx->fs[i] = alloc->makeArray<float>(std::max(fColorCount+1, 8));
342 ctx->bs[i] = alloc->makeArray<float>(std::max(fColorCount+1, 8));
343 }
344
Mike Kleina3771842017-05-04 19:38:48 -0400345 if (fOrigPos == nullptr) {
346 // Handle evenly distributed stops.
347
Herb Derby4de13042017-05-15 10:49:39 -0400348 size_t stopCount = fColorCount;
349 float gapCount = stopCount - 1;
Mike Kleina3771842017-05-04 19:38:48 -0400350
Brian Osman781e3502018-10-03 15:42:47 -0400351 SkPMColor4f c_l = prepareColor(0);
Herb Derby4de13042017-05-15 10:49:39 -0400352 for (size_t i = 0; i < stopCount - 1; i++) {
Brian Osman781e3502018-10-03 15:42:47 -0400353 SkPMColor4f c_r = prepareColor(i + 1);
Herb Derbyeb99bfd2017-05-16 10:51:26 -0400354 init_stop_evenly(ctx, gapCount, i, c_l, c_r);
Mike Kleina3771842017-05-04 19:38:48 -0400355 c_l = c_r;
356 }
Herb Derbyeb99bfd2017-05-16 10:51:26 -0400357 add_const_color(ctx, stopCount - 1, c_l);
Mike Kleina3771842017-05-04 19:38:48 -0400358
Herb Derby4de13042017-05-15 10:49:39 -0400359 ctx->stopCount = stopCount;
360 p->append(SkRasterPipeline::evenly_spaced_gradient, ctx);
Mike Kleina3771842017-05-04 19:38:48 -0400361 } else {
362 // Handle arbitrary stops.
363
Herb Derby4de13042017-05-15 10:49:39 -0400364 ctx->ts = alloc->makeArray<float>(fColorCount+1);
365
Mike Kleina3771842017-05-04 19:38:48 -0400366 // Remove the dummy stops inserted by SkGradientShaderBase::SkGradientShaderBase
367 // because they are naturally handled by the search method.
368 int firstStop;
369 int lastStop;
370 if (fColorCount > 2) {
371 firstStop = fOrigColors4f[0] != fOrigColors4f[1] ? 0 : 1;
372 lastStop = fOrigColors4f[fColorCount - 2] != fOrigColors4f[fColorCount - 1]
373 ? fColorCount - 1 : fColorCount - 2;
374 } else {
375 firstStop = 0;
376 lastStop = 1;
377 }
Mike Kleina3771842017-05-04 19:38:48 -0400378
Mike Kleina3771842017-05-04 19:38:48 -0400379 size_t stopCount = 0;
380 float t_l = fOrigPos[firstStop];
Brian Osman781e3502018-10-03 15:42:47 -0400381 SkPMColor4f c_l = prepareColor(firstStop);
Herb Derbyeb99bfd2017-05-16 10:51:26 -0400382 add_const_color(ctx, stopCount++, c_l);
Mike Kleina3771842017-05-04 19:38:48 -0400383 // N.B. lastStop is the index of the last stop, not one after.
384 for (int i = firstStop; i < lastStop; i++) {
385 float t_r = fOrigPos[i + 1];
Brian Osman781e3502018-10-03 15:42:47 -0400386 SkPMColor4f c_r = prepareColor(i + 1);
Florin Malita3e20d022017-11-03 12:11:38 -0400387 SkASSERT(t_l <= t_r);
Mike Kleina3771842017-05-04 19:38:48 -0400388 if (t_l < t_r) {
Herb Derbyeb99bfd2017-05-16 10:51:26 -0400389 init_stop_pos(ctx, stopCount, t_l, t_r, c_l, c_r);
Mike Kleina3771842017-05-04 19:38:48 -0400390 stopCount += 1;
391 }
392 t_l = t_r;
393 c_l = c_r;
394 }
395
Herb Derby4de13042017-05-15 10:49:39 -0400396 ctx->ts[stopCount] = t_l;
Herb Derbyeb99bfd2017-05-16 10:51:26 -0400397 add_const_color(ctx, stopCount++, c_l);
Mike Kleina3771842017-05-04 19:38:48 -0400398
Herb Derby4de13042017-05-15 10:49:39 -0400399 ctx->stopCount = stopCount;
400 p->append(SkRasterPipeline::gradient, ctx);
Mike Kleina3771842017-05-04 19:38:48 -0400401 }
Mike Kleina3771842017-05-04 19:38:48 -0400402 }
403
Mike Reed62ce2ca2018-02-19 14:20:15 -0500404 if (decal_ctx) {
405 p->append(SkRasterPipeline::check_decal_mask, decal_ctx);
406 }
407
Mike Kleina3771842017-05-04 19:38:48 -0400408 if (!premulGrad && !this->colorsAreOpaque()) {
Mike Kleine7598532017-05-11 11:29:29 -0400409 p->append(SkRasterPipeline::premul);
Mike Kleina3771842017-05-04 19:38:48 -0400410 }
411
Florin Malita2e409002017-06-28 14:46:54 -0400412 p->extend(postPipeline);
413
Mike Kleina3771842017-05-04 19:38:48 -0400414 return true;
415}
416
417
rileya@google.com589708b2012-07-26 20:04:23 +0000418bool SkGradientShaderBase::isOpaque() const {
Mike Reedfae8fce2019-04-03 10:27:45 -0400419 return fColorsAreOpaque && (this->getTileMode() != SkTileMode::kDecal);
Mike Reed62ce2ca2018-02-19 14:20:15 -0500420}
421
reed8367b8c2014-08-22 08:30:20 -0700422static unsigned rounded_divide(unsigned numer, unsigned denom) {
423 return (numer + (denom >> 1)) / denom;
424}
425
426bool SkGradientShaderBase::onAsLuminanceColor(SkColor* lum) const {
427 // we just compute an average color.
428 // possibly we could weight this based on the proportional width for each color
429 // assuming they are not evenly distributed in the fPos array.
430 int r = 0;
431 int g = 0;
432 int b = 0;
433 const int n = fColorCount;
Florin Malita39d71de2017-10-31 11:33:49 -0400434 // TODO: use linear colors?
reed8367b8c2014-08-22 08:30:20 -0700435 for (int i = 0; i < n; ++i) {
Florin Malita39d71de2017-10-31 11:33:49 -0400436 SkColor c = this->getLegacyColor(i);
reed8367b8c2014-08-22 08:30:20 -0700437 r += SkColorGetR(c);
438 g += SkColorGetG(c);
439 b += SkColorGetB(c);
440 }
441 *lum = SkColorSetRGB(rounded_divide(r, n), rounded_divide(g, n), rounded_divide(b, n));
442 return true;
443}
444
Brian Osman6667fb12018-07-03 16:44:02 -0400445SkColor4fXformer::SkColor4fXformer(const SkColor4f* colors, int colorCount,
446 SkColorSpace* src, SkColorSpace* dst) {
Brian Osman6667fb12018-07-03 16:44:02 -0400447 fColors = colors;
Brian Osmanccd39952018-07-06 16:16:43 -0400448
Mike Kleinf9f68ff2018-10-12 14:23:06 -0400449 if (dst && !SkColorSpace::Equals(src, dst)) {
Brian Osman6667fb12018-07-03 16:44:02 -0400450 fStorage.reset(colorCount);
Brian Salomon5dfcf132018-10-12 14:39:32 +0000451
452 auto info = SkImageInfo::Make(colorCount,1, kRGBA_F32_SkColorType, kUnpremul_SkAlphaType);
453
454 SkConvertPixels(info.makeColorSpace(sk_ref_sp(dst)), fStorage.begin(), info.minRowBytes(),
455 info.makeColorSpace(sk_ref_sp(src)), fColors , info.minRowBytes());
456
Brian Osman6667fb12018-07-03 16:44:02 -0400457 fColors = fStorage.begin();
458 }
459}
460
Florin Malita5f379a82017-10-18 16:22:35 -0400461void SkGradientShaderBase::commonAsAGradient(GradientInfo* info) const {
rileya@google.com589708b2012-07-26 20:04:23 +0000462 if (info) {
463 if (info->fColorCount >= fColorCount) {
464 if (info->fColors) {
Florin Malita39d71de2017-10-31 11:33:49 -0400465 for (int i = 0; i < fColorCount; ++i) {
466 info->fColors[i] = this->getLegacyColor(i);
467 }
rileya@google.com589708b2012-07-26 20:04:23 +0000468 }
469 if (info->fColorOffsets) {
Florin Malitaed6ae562017-10-28 11:06:48 -0400470 for (int i = 0; i < fColorCount; ++i) {
471 info->fColorOffsets[i] = this->getPos(i);
rileya@google.com589708b2012-07-26 20:04:23 +0000472 }
473 }
474 }
475 info->fColorCount = fColorCount;
Mike Reedfae8fce2019-04-03 10:27:45 -0400476#ifdef SK_SUPPORT_LEGACY_TILEMODE_ENUM
477 info->fTileMode = (SkShader::TileMode)fTileMode;
478#else
rileya@google.com589708b2012-07-26 20:04:23 +0000479 info->fTileMode = fTileMode;
Mike Reedfae8fce2019-04-03 10:27:45 -0400480#endif
reed@google.com3d3a8602013-05-24 14:58:44 +0000481 info->fGradientFlags = fGradFlags;
rileya@google.com589708b2012-07-26 20:04:23 +0000482 }
483}
484
485///////////////////////////////////////////////////////////////////////////////
486///////////////////////////////////////////////////////////////////////////////
487
reed1b747302015-01-06 07:13:19 -0800488// Return true if these parameters are valid/legal/safe to construct a gradient
489//
brianosmane25d71c2016-09-28 11:27:28 -0700490static bool valid_grad(const SkColor4f colors[], const SkScalar pos[], int count,
Mike Reedfae8fce2019-04-03 10:27:45 -0400491 SkTileMode tileMode) {
492 return nullptr != colors && count >= 1 && (unsigned)tileMode < kSkTileModeCount;
reed1b747302015-01-06 07:13:19 -0800493}
494
reed@google.com437d6eb2013-05-23 19:03:05 +0000495static void desc_init(SkGradientShaderBase::Descriptor* desc,
brianosmane25d71c2016-09-28 11:27:28 -0700496 const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace,
497 const SkScalar pos[], int colorCount,
Mike Reedfae8fce2019-04-03 10:27:45 -0400498 SkTileMode mode, uint32_t flags, const SkMatrix* localMatrix) {
fmalita748d6202016-05-11 11:39:58 -0700499 SkASSERT(colorCount > 1);
500
commit-bot@chromium.org6c5aea22014-04-22 16:25:15 +0000501 desc->fColors = colors;
brianosmane25d71c2016-09-28 11:27:28 -0700502 desc->fColorSpace = std::move(colorSpace);
commit-bot@chromium.org6c5aea22014-04-22 16:25:15 +0000503 desc->fPos = pos;
504 desc->fCount = colorCount;
505 desc->fTileMode = mode;
commit-bot@chromium.org6c5aea22014-04-22 16:25:15 +0000506 desc->fGradFlags = flags;
reedaddf2ed2014-08-11 08:28:24 -0700507 desc->fLocalMatrix = localMatrix;
reed@google.com437d6eb2013-05-23 19:03:05 +0000508}
509
Mike Klein024072a2018-11-11 00:26:30 +0000510static SkColor4f average_gradient_color(const SkColor4f colors[], const SkScalar pos[],
511 int colorCount) {
512 // The gradient is a piecewise linear interpolation between colors. For a given interval,
513 // the integral between the two endpoints is 0.5 * (ci + cj) * (pj - pi), which provides that
514 // intervals average color. The overall average color is thus the sum of each piece. The thing
515 // to keep in mind is that the provided gradient definition may implicitly use p=0 and p=1.
516 Sk4f blend(0.0);
517 // Bake 1/(colorCount - 1) uniform stop difference into this scale factor
518 SkScalar wScale = pos ? 0.5 : 0.5 / (colorCount - 1);
519 for (int i = 0; i < colorCount - 1; ++i) {
520 // Calculate the average color for the interval between pos(i) and pos(i+1)
521 Sk4f c0 = Sk4f::Load(&colors[i]);
522 Sk4f c1 = Sk4f::Load(&colors[i + 1]);
523 // when pos == null, there are colorCount uniformly distributed stops, going from 0 to 1,
524 // so pos[i + 1] - pos[i] = 1/(colorCount-1)
525 SkScalar w = pos ? (pos[i + 1] - pos[i]) : SK_Scalar1;
526 blend += wScale * w * (c1 + c0);
527 }
528
529 // Now account for any implicit intervals at the start or end of the stop definitions
530 if (pos) {
531 if (pos[0] > 0.0) {
532 // The first color is fixed between p = 0 to pos[0], so 0.5 * (ci + cj) * (pj - pi)
533 // becomes 0.5 * (c + c) * (pj - 0) = c * pj
534 Sk4f c = Sk4f::Load(&colors[0]);
535 blend += pos[0] * c;
536 }
537 if (pos[colorCount - 1] < SK_Scalar1) {
538 // The last color is fixed between pos[n-1] to p = 1, so 0.5 * (ci + cj) * (pj - pi)
539 // becomes 0.5 * (c + c) * (1 - pi) = c * (1 - pi)
540 Sk4f c = Sk4f::Load(&colors[colorCount - 1]);
541 blend += (1 - pos[colorCount - 1]) * c;
542 }
543 }
544
545 SkColor4f avg;
546 blend.store(&avg);
547 return avg;
548}
549
Michael Ludwigd431c722018-11-16 10:00:24 -0500550// The default SkScalarNearlyZero threshold of .0024 is too big and causes regressions for svg
551// gradients defined in the wild.
552static constexpr SkScalar kDegenerateThreshold = SK_Scalar1 / (1 << 15);
553
Mike Klein024072a2018-11-11 00:26:30 +0000554// Except for special circumstances of clamped gradients, every gradient shape--when degenerate--
555// can be mapped to the same fallbacks. The specific shape factories must account for special
556// clamped conditions separately, this will always return the last color for clamped gradients.
557static sk_sp<SkShader> make_degenerate_gradient(const SkColor4f colors[], const SkScalar pos[],
558 int colorCount, sk_sp<SkColorSpace> colorSpace,
Mike Reedfae8fce2019-04-03 10:27:45 -0400559 SkTileMode mode) {
Mike Klein024072a2018-11-11 00:26:30 +0000560 switch(mode) {
Mike Reedfae8fce2019-04-03 10:27:45 -0400561 case SkTileMode::kDecal:
Mike Klein024072a2018-11-11 00:26:30 +0000562 // normally this would reject the area outside of the interpolation region, so since
563 // inside region is empty when the radii are equal, the entire draw region is empty
Mike Reedc8bea7d2019-04-09 13:55:36 -0400564 return SkShaders::Empty();
Mike Reedfae8fce2019-04-03 10:27:45 -0400565 case SkTileMode::kRepeat:
566 case SkTileMode::kMirror:
Mike Klein024072a2018-11-11 00:26:30 +0000567 // repeat and mirror are treated the same: the border colors are never visible,
568 // but approximate the final color as infinite repetitions of the colors, so
569 // it can be represented as the average color of the gradient.
Mike Reedc8bea7d2019-04-09 13:55:36 -0400570 return SkShaders::Color(
Mike Klein024072a2018-11-11 00:26:30 +0000571 average_gradient_color(colors, pos, colorCount), std::move(colorSpace));
Mike Reedfae8fce2019-04-03 10:27:45 -0400572 case SkTileMode::kClamp:
Mike Klein024072a2018-11-11 00:26:30 +0000573 // Depending on how the gradient shape degenerates, there may be a more specialized
574 // fallback representation for the factories to use, but this is a reasonable default.
Mike Reedc8bea7d2019-04-09 13:55:36 -0400575 return SkShaders::Color(colors[colorCount - 1], std::move(colorSpace));
Mike Klein024072a2018-11-11 00:26:30 +0000576 }
Mike Reedfae8fce2019-04-03 10:27:45 -0400577 SkDEBUGFAIL("Should not be reached");
578 return nullptr;
Mike Klein024072a2018-11-11 00:26:30 +0000579}
580
brianosmane25d71c2016-09-28 11:27:28 -0700581// assumes colors is SkColor4f* and pos is SkScalar*
fmenozzie9fd0f82016-08-19 07:50:57 -0700582#define EXPAND_1_COLOR(count) \
brianosmane25d71c2016-09-28 11:27:28 -0700583 SkColor4f tmp[2]; \
fmenozzie9fd0f82016-08-19 07:50:57 -0700584 do { \
585 if (1 == count) { \
586 tmp[0] = tmp[1] = colors[0]; \
587 colors = tmp; \
588 pos = nullptr; \
589 count = 2; \
590 } \
591 } while (0)
592
fmenozzi68d952c2016-08-19 08:56:56 -0700593struct ColorStopOptimizer {
Mike Reedfae8fce2019-04-03 10:27:45 -0400594 ColorStopOptimizer(const SkColor4f* colors, const SkScalar* pos, int count, SkTileMode mode)
fmenozzi68d952c2016-08-19 08:56:56 -0700595 : fColors(colors)
596 , fPos(pos)
597 , fCount(count) {
598
599 if (!pos || count != 3) {
600 return;
601 }
602
603 if (SkScalarNearlyEqual(pos[0], 0.0f) &&
604 SkScalarNearlyEqual(pos[1], 0.0f) &&
605 SkScalarNearlyEqual(pos[2], 1.0f)) {
606
Mike Reedfae8fce2019-04-03 10:27:45 -0400607 if (SkTileMode::kRepeat == mode || SkTileMode::kMirror == mode ||
fmenozzi68d952c2016-08-19 08:56:56 -0700608 colors[0] == colors[1]) {
609
fmalita582a6562016-08-22 06:28:57 -0700610 // Ignore the leftmost color/pos.
611 fColors += 1;
612 fPos += 1;
613 fCount = 2;
fmenozzi68d952c2016-08-19 08:56:56 -0700614 }
615 } else if (SkScalarNearlyEqual(pos[0], 0.0f) &&
616 SkScalarNearlyEqual(pos[1], 1.0f) &&
617 SkScalarNearlyEqual(pos[2], 1.0f)) {
618
Mike Reedfae8fce2019-04-03 10:27:45 -0400619 if (SkTileMode::kRepeat == mode || SkTileMode::kMirror == mode ||
fmenozzi68d952c2016-08-19 08:56:56 -0700620 colors[1] == colors[2]) {
621
fmalita582a6562016-08-22 06:28:57 -0700622 // Ignore the rightmost color/pos.
fmenozzi68d952c2016-08-19 08:56:56 -0700623 fCount = 2;
624 }
625 }
626 }
627
brianosmane25d71c2016-09-28 11:27:28 -0700628 const SkColor4f* fColors;
629 const SkScalar* fPos;
630 int fCount;
631};
632
633struct ColorConverter {
634 ColorConverter(const SkColor* colors, int count) {
Brian Osman6667fb12018-07-03 16:44:02 -0400635 const float ONE_OVER_255 = 1.f / 255;
brianosmane25d71c2016-09-28 11:27:28 -0700636 for (int i = 0; i < count; ++i) {
Brian Osman6667fb12018-07-03 16:44:02 -0400637 fColors4f.push_back({
638 SkColorGetR(colors[i]) * ONE_OVER_255,
639 SkColorGetG(colors[i]) * ONE_OVER_255,
640 SkColorGetB(colors[i]) * ONE_OVER_255,
641 SkColorGetA(colors[i]) * ONE_OVER_255 });
brianosmane25d71c2016-09-28 11:27:28 -0700642 }
643 }
644
645 SkSTArray<2, SkColor4f, true> fColors4f;
fmenozzi68d952c2016-08-19 08:56:56 -0700646};
647
reed8a21c9f2016-03-08 18:50:00 -0800648sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2],
fmenozzi68d952c2016-08-19 08:56:56 -0700649 const SkColor colors[],
650 const SkScalar pos[], int colorCount,
Mike Reedfae8fce2019-04-03 10:27:45 -0400651 SkTileMode mode,
fmenozzi68d952c2016-08-19 08:56:56 -0700652 uint32_t flags,
653 const SkMatrix* localMatrix) {
brianosmane25d71c2016-09-28 11:27:28 -0700654 ColorConverter converter(colors, colorCount);
655 return MakeLinear(pts, converter.fColors4f.begin(), nullptr, pos, colorCount, mode, flags,
656 localMatrix);
657}
658
659sk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2],
660 const SkColor4f colors[],
661 sk_sp<SkColorSpace> colorSpace,
662 const SkScalar pos[], int colorCount,
Mike Reedfae8fce2019-04-03 10:27:45 -0400663 SkTileMode mode,
brianosmane25d71c2016-09-28 11:27:28 -0700664 uint32_t flags,
665 const SkMatrix* localMatrix) {
fmalitac5231042016-08-10 05:45:50 -0700666 if (!pts || !SkScalarIsFinite((pts[1] - pts[0]).length())) {
halcanary96fcdcc2015-08-27 07:41:13 -0700667 return nullptr;
reed1b747302015-01-06 07:13:19 -0800668 }
669 if (!valid_grad(colors, pos, colorCount, mode)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700670 return nullptr;
rileya@google.com589708b2012-07-26 20:04:23 +0000671 }
fmenozzie9fd0f82016-08-19 07:50:57 -0700672 if (1 == colorCount) {
Mike Reedc8bea7d2019-04-09 13:55:36 -0400673 return SkShaders::Color(colors[0], std::move(colorSpace));
fmenozzie9fd0f82016-08-19 07:50:57 -0700674 }
Florin Malita8d3ffad2017-02-03 18:21:17 +0000675 if (localMatrix && !localMatrix->invert(nullptr)) {
676 return nullptr;
677 }
rileya@google.com589708b2012-07-26 20:04:23 +0000678
Michael Ludwigd431c722018-11-16 10:00:24 -0500679 if (SkScalarNearlyZero((pts[1] - pts[0]).length(), kDegenerateThreshold)) {
Mike Klein024072a2018-11-11 00:26:30 +0000680 // Degenerate gradient, the only tricky complication is when in clamp mode, the limit of
681 // the gradient approaches two half planes of solid color (first and last). However, they
682 // are divided by the line perpendicular to the start and end point, which becomes undefined
683 // once start and end are exactly the same, so just use the end color for a stable solution.
684 return make_degenerate_gradient(colors, pos, colorCount, std::move(colorSpace), mode);
685 }
686
fmenozzi68d952c2016-08-19 08:56:56 -0700687 ColorStopOptimizer opt(colors, pos, colorCount, mode);
688
reed@google.com437d6eb2013-05-23 19:03:05 +0000689 SkGradientShaderBase::Descriptor desc;
brianosmane25d71c2016-09-28 11:27:28 -0700690 desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
691 localMatrix);
reed8a21c9f2016-03-08 18:50:00 -0800692 return sk_make_sp<SkLinearGradient>(pts, desc);
rileya@google.com589708b2012-07-26 20:04:23 +0000693}
694
reed8a21c9f2016-03-08 18:50:00 -0800695sk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius,
brianosmane25d71c2016-09-28 11:27:28 -0700696 const SkColor colors[],
697 const SkScalar pos[], int colorCount,
Mike Reedfae8fce2019-04-03 10:27:45 -0400698 SkTileMode mode,
brianosmane25d71c2016-09-28 11:27:28 -0700699 uint32_t flags,
700 const SkMatrix* localMatrix) {
701 ColorConverter converter(colors, colorCount);
702 return MakeRadial(center, radius, converter.fColors4f.begin(), nullptr, pos, colorCount, mode,
703 flags, localMatrix);
704}
705
706sk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius,
707 const SkColor4f colors[],
708 sk_sp<SkColorSpace> colorSpace,
709 const SkScalar pos[], int colorCount,
Mike Reedfae8fce2019-04-03 10:27:45 -0400710 SkTileMode mode,
brianosmane25d71c2016-09-28 11:27:28 -0700711 uint32_t flags,
712 const SkMatrix* localMatrix) {
Mike Klein024072a2018-11-11 00:26:30 +0000713 if (radius < 0) {
halcanary96fcdcc2015-08-27 07:41:13 -0700714 return nullptr;
reed1b747302015-01-06 07:13:19 -0800715 }
716 if (!valid_grad(colors, pos, colorCount, mode)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700717 return nullptr;
rileya@google.com589708b2012-07-26 20:04:23 +0000718 }
fmenozzie9fd0f82016-08-19 07:50:57 -0700719 if (1 == colorCount) {
Mike Reedc8bea7d2019-04-09 13:55:36 -0400720 return SkShaders::Color(colors[0], std::move(colorSpace));
fmenozzie9fd0f82016-08-19 07:50:57 -0700721 }
Florin Malita8d3ffad2017-02-03 18:21:17 +0000722 if (localMatrix && !localMatrix->invert(nullptr)) {
723 return nullptr;
724 }
rileya@google.com589708b2012-07-26 20:04:23 +0000725
Michael Ludwigd431c722018-11-16 10:00:24 -0500726 if (SkScalarNearlyZero(radius, kDegenerateThreshold)) {
Mike Klein024072a2018-11-11 00:26:30 +0000727 // Degenerate gradient optimization, and no special logic needed for clamped radial gradient
728 return make_degenerate_gradient(colors, pos, colorCount, std::move(colorSpace), mode);
729 }
730
fmenozzi68d952c2016-08-19 08:56:56 -0700731 ColorStopOptimizer opt(colors, pos, colorCount, mode);
732
reed@google.com437d6eb2013-05-23 19:03:05 +0000733 SkGradientShaderBase::Descriptor desc;
brianosmane25d71c2016-09-28 11:27:28 -0700734 desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
735 localMatrix);
reed8a21c9f2016-03-08 18:50:00 -0800736 return sk_make_sp<SkRadialGradient>(center, radius, desc);
rileya@google.com589708b2012-07-26 20:04:23 +0000737}
738
reed8a21c9f2016-03-08 18:50:00 -0800739sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start,
brianosmane25d71c2016-09-28 11:27:28 -0700740 SkScalar startRadius,
741 const SkPoint& end,
742 SkScalar endRadius,
743 const SkColor colors[],
744 const SkScalar pos[],
745 int colorCount,
Mike Reedfae8fce2019-04-03 10:27:45 -0400746 SkTileMode mode,
brianosmane25d71c2016-09-28 11:27:28 -0700747 uint32_t flags,
748 const SkMatrix* localMatrix) {
749 ColorConverter converter(colors, colorCount);
750 return MakeTwoPointConical(start, startRadius, end, endRadius, converter.fColors4f.begin(),
751 nullptr, pos, colorCount, mode, flags, localMatrix);
752}
753
754sk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start,
755 SkScalar startRadius,
756 const SkPoint& end,
757 SkScalar endRadius,
758 const SkColor4f colors[],
759 sk_sp<SkColorSpace> colorSpace,
760 const SkScalar pos[],
761 int colorCount,
Mike Reedfae8fce2019-04-03 10:27:45 -0400762 SkTileMode mode,
brianosmane25d71c2016-09-28 11:27:28 -0700763 uint32_t flags,
764 const SkMatrix* localMatrix) {
reed1b747302015-01-06 07:13:19 -0800765 if (startRadius < 0 || endRadius < 0) {
halcanary96fcdcc2015-08-27 07:41:13 -0700766 return nullptr;
reed1b747302015-01-06 07:13:19 -0800767 }
768 if (!valid_grad(colors, pos, colorCount, mode)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700769 return nullptr;
rileya@google.com589708b2012-07-26 20:04:23 +0000770 }
Michael Ludwigd431c722018-11-16 10:00:24 -0500771 if (SkScalarNearlyZero((start - end).length(), kDegenerateThreshold)) {
Mike Klein024072a2018-11-11 00:26:30 +0000772 // If the center positions are the same, then the gradient is the radial variant of a 2 pt
773 // conical gradient, an actual radial gradient (startRadius == 0), or it is fully degenerate
774 // (startRadius == endRadius).
Michael Ludwigd431c722018-11-16 10:00:24 -0500775 if (SkScalarNearlyEqual(startRadius, endRadius, kDegenerateThreshold)) {
Mike Klein024072a2018-11-11 00:26:30 +0000776 // Degenerate case, where the interpolation region area approaches zero. The proper
777 // behavior depends on the tile mode, which is consistent with the default degenerate
778 // gradient behavior, except when mode = clamp and the radii > 0.
Mike Reedfae8fce2019-04-03 10:27:45 -0400779 if (mode == SkTileMode::kClamp && endRadius > kDegenerateThreshold) {
Mike Klein024072a2018-11-11 00:26:30 +0000780 // The interpolation region becomes an infinitely thin ring at the radius, so the
781 // final gradient will be the first color repeated from p=0 to 1, and then a hard
782 // stop switching to the last color at p=1.
783 static constexpr SkScalar circlePos[3] = {0, 1, 1};
784 SkColor4f reColors[3] = {colors[0], colors[0], colors[colorCount - 1]};
785 return MakeRadial(start, endRadius, reColors, std::move(colorSpace),
786 circlePos, 3, mode, flags, localMatrix);
787 } else {
788 // Otherwise use the default degenerate case
789 return make_degenerate_gradient(
790 colors, pos, colorCount, std::move(colorSpace), mode);
791 }
Michael Ludwigd431c722018-11-16 10:00:24 -0500792 } else if (SkScalarNearlyZero(startRadius, kDegenerateThreshold)) {
Mike Klein024072a2018-11-11 00:26:30 +0000793 // We can treat this gradient as radial, which is faster. If we got here, we know
794 // that endRadius is not equal to 0, so this produces a meaningful gradient
795 return MakeRadial(start, endRadius, colors, std::move(colorSpace), pos, colorCount,
796 mode, flags, localMatrix);
Brian Osman2dfab272018-11-06 00:41:40 +0000797 }
Mike Klein024072a2018-11-11 00:26:30 +0000798 // Else it's the 2pt conical radial variant with no degenerate radii, so fall through to the
799 // regular 2pt constructor.
Brian Osman2dfab272018-11-06 00:41:40 +0000800 }
Mike Klein024072a2018-11-11 00:26:30 +0000801
Florin Malita8d3ffad2017-02-03 18:21:17 +0000802 if (localMatrix && !localMatrix->invert(nullptr)) {
803 return nullptr;
804 }
reed6b7a6c72016-08-18 16:13:50 -0700805 EXPAND_1_COLOR(colorCount);
rileya@google.com589708b2012-07-26 20:04:23 +0000806
fmenozzi68d952c2016-08-19 08:56:56 -0700807 ColorStopOptimizer opt(colors, pos, colorCount, mode);
808
reed@google.com437d6eb2013-05-23 19:03:05 +0000809 SkGradientShaderBase::Descriptor desc;
Florin Malita5f379a82017-10-18 16:22:35 -0400810 desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
811 localMatrix);
812 return SkTwoPointConicalGradient::Create(start, startRadius, end, endRadius, desc);
rileya@google.com589708b2012-07-26 20:04:23 +0000813}
814
reed8a21c9f2016-03-08 18:50:00 -0800815sk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy,
brianosmane25d71c2016-09-28 11:27:28 -0700816 const SkColor colors[],
817 const SkScalar pos[],
818 int colorCount,
Mike Reedfae8fce2019-04-03 10:27:45 -0400819 SkTileMode mode,
Florin Malita5a9a9812017-08-01 16:38:08 -0400820 SkScalar startAngle,
821 SkScalar endAngle,
brianosmane25d71c2016-09-28 11:27:28 -0700822 uint32_t flags,
823 const SkMatrix* localMatrix) {
824 ColorConverter converter(colors, colorCount);
Florin Malita5a9a9812017-08-01 16:38:08 -0400825 return MakeSweep(cx, cy, converter.fColors4f.begin(), nullptr, pos, colorCount,
826 mode, startAngle, endAngle, flags, localMatrix);
brianosmane25d71c2016-09-28 11:27:28 -0700827}
828
829sk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy,
830 const SkColor4f colors[],
831 sk_sp<SkColorSpace> colorSpace,
832 const SkScalar pos[],
833 int colorCount,
Mike Reedfae8fce2019-04-03 10:27:45 -0400834 SkTileMode mode,
Florin Malita5a9a9812017-08-01 16:38:08 -0400835 SkScalar startAngle,
836 SkScalar endAngle,
brianosmane25d71c2016-09-28 11:27:28 -0700837 uint32_t flags,
838 const SkMatrix* localMatrix) {
Florin Malita5a9a9812017-08-01 16:38:08 -0400839 if (!valid_grad(colors, pos, colorCount, mode)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700840 return nullptr;
rileya@google.com589708b2012-07-26 20:04:23 +0000841 }
fmenozzie9fd0f82016-08-19 07:50:57 -0700842 if (1 == colorCount) {
Mike Reedc8bea7d2019-04-09 13:55:36 -0400843 return SkShaders::Color(colors[0], std::move(colorSpace));
fmenozzie9fd0f82016-08-19 07:50:57 -0700844 }
Mike Klein024072a2018-11-11 00:26:30 +0000845 if (!SkScalarIsFinite(startAngle) || !SkScalarIsFinite(endAngle) || startAngle > endAngle) {
Florin Malita5a9a9812017-08-01 16:38:08 -0400846 return nullptr;
847 }
Florin Malita8d3ffad2017-02-03 18:21:17 +0000848 if (localMatrix && !localMatrix->invert(nullptr)) {
849 return nullptr;
850 }
rileya@google.com589708b2012-07-26 20:04:23 +0000851
Michael Ludwigd431c722018-11-16 10:00:24 -0500852 if (SkScalarNearlyEqual(startAngle, endAngle, kDegenerateThreshold)) {
Mike Klein024072a2018-11-11 00:26:30 +0000853 // Degenerate gradient, which should follow default degenerate behavior unless it is
854 // clamped and the angle is greater than 0.
Mike Reedfae8fce2019-04-03 10:27:45 -0400855 if (mode == SkTileMode::kClamp && endAngle > kDegenerateThreshold) {
Mike Klein024072a2018-11-11 00:26:30 +0000856 // In this case, the first color is repeated from 0 to the angle, then a hardstop
857 // switches to the last color (all other colors are compressed to the infinitely thin
858 // interpolation region).
859 static constexpr SkScalar clampPos[3] = {0, 1, 1};
860 SkColor4f reColors[3] = {colors[0], colors[0], colors[colorCount - 1]};
861 return MakeSweep(cx, cy, reColors, std::move(colorSpace), clampPos, 3, mode, 0,
862 endAngle, flags, localMatrix);
863 } else {
864 return make_degenerate_gradient(colors, pos, colorCount, std::move(colorSpace), mode);
865 }
866 }
867
Florin Malita5a9a9812017-08-01 16:38:08 -0400868 if (startAngle <= 0 && endAngle >= 360) {
869 // If the t-range includes [0,1], then we can always use clamping (presumably faster).
Mike Reedfae8fce2019-04-03 10:27:45 -0400870 mode = SkTileMode::kClamp;
Florin Malita5a9a9812017-08-01 16:38:08 -0400871 }
fmenozzi68d952c2016-08-19 08:56:56 -0700872
873 ColorStopOptimizer opt(colors, pos, colorCount, mode);
874
reed@google.com437d6eb2013-05-23 19:03:05 +0000875 SkGradientShaderBase::Descriptor desc;
brianosmane25d71c2016-09-28 11:27:28 -0700876 desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags,
877 localMatrix);
Florin Malita5a9a9812017-08-01 16:38:08 -0400878
879 const SkScalar t0 = startAngle / 360,
880 t1 = endAngle / 360;
881
882 return sk_make_sp<SkSweepGradient>(SkPoint::Make(cx, cy), t0, t1, desc);
rileya@google.com589708b2012-07-26 20:04:23 +0000883}
884
Mike Kleinfa5f6ce2018-10-20 08:21:31 -0400885void SkGradientShader::RegisterFlattenables() {
Brian Salomon23356442018-11-30 15:33:19 -0500886 SK_REGISTER_FLATTENABLE(SkLinearGradient);
887 SK_REGISTER_FLATTENABLE(SkRadialGradient);
888 SK_REGISTER_FLATTENABLE(SkSweepGradient);
889 SK_REGISTER_FLATTENABLE(SkTwoPointConicalGradient);
Mike Klein12956722018-10-19 10:00:21 -0400890}