1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2006 The Android Open Source Project 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 <algorithm> 9cb93a386Sopenharmony_ci#include "include/core/SkMallocPixelRef.h" 10cb93a386Sopenharmony_ci#include "include/private/SkFloatBits.h" 11cb93a386Sopenharmony_ci#include "include/private/SkHalf.h" 12cb93a386Sopenharmony_ci#include "include/private/SkTPin.h" 13cb93a386Sopenharmony_ci#include "include/private/SkVx.h" 14cb93a386Sopenharmony_ci#include "src/core/SkColorSpacePriv.h" 15cb93a386Sopenharmony_ci#include "src/core/SkConvertPixels.h" 16cb93a386Sopenharmony_ci#include "src/core/SkMatrixProvider.h" 17cb93a386Sopenharmony_ci#include "src/core/SkReadBuffer.h" 18cb93a386Sopenharmony_ci#include "src/core/SkVM.h" 19cb93a386Sopenharmony_ci#include "src/core/SkWriteBuffer.h" 20cb93a386Sopenharmony_ci#include "src/shaders/gradients/Sk4fLinearGradient.h" 21cb93a386Sopenharmony_ci#include "src/shaders/gradients/SkGradientShaderPriv.h" 22cb93a386Sopenharmony_ci#include "src/shaders/gradients/SkLinearGradient.h" 23cb93a386Sopenharmony_ci#include "src/shaders/gradients/SkRadialGradient.h" 24cb93a386Sopenharmony_ci#include "src/shaders/gradients/SkSweepGradient.h" 25cb93a386Sopenharmony_ci#include "src/shaders/gradients/SkTwoPointConicalGradient.h" 26cb93a386Sopenharmony_ci 27cb93a386Sopenharmony_cienum GradientSerializationFlags { 28cb93a386Sopenharmony_ci // Bits 29:31 used for various boolean flags 29cb93a386Sopenharmony_ci kHasPosition_GSF = 0x80000000, 30cb93a386Sopenharmony_ci kHasLocalMatrix_GSF = 0x40000000, 31cb93a386Sopenharmony_ci kHasColorSpace_GSF = 0x20000000, 32cb93a386Sopenharmony_ci 33cb93a386Sopenharmony_ci // Bits 12:28 unused 34cb93a386Sopenharmony_ci 35cb93a386Sopenharmony_ci // Bits 8:11 for fTileMode 36cb93a386Sopenharmony_ci kTileModeShift_GSF = 8, 37cb93a386Sopenharmony_ci kTileModeMask_GSF = 0xF, 38cb93a386Sopenharmony_ci 39cb93a386Sopenharmony_ci // Bits 0:7 for fGradFlags (note that kForce4fContext_PrivateFlag is 0x80) 40cb93a386Sopenharmony_ci kGradFlagsShift_GSF = 0, 41cb93a386Sopenharmony_ci kGradFlagsMask_GSF = 0xFF, 42cb93a386Sopenharmony_ci}; 43cb93a386Sopenharmony_ci 44cb93a386Sopenharmony_civoid SkGradientShaderBase::Descriptor::flatten(SkWriteBuffer& buffer) const { 45cb93a386Sopenharmony_ci uint32_t flags = 0; 46cb93a386Sopenharmony_ci if (fPos) { 47cb93a386Sopenharmony_ci flags |= kHasPosition_GSF; 48cb93a386Sopenharmony_ci } 49cb93a386Sopenharmony_ci if (fLocalMatrix) { 50cb93a386Sopenharmony_ci flags |= kHasLocalMatrix_GSF; 51cb93a386Sopenharmony_ci } 52cb93a386Sopenharmony_ci sk_sp<SkData> colorSpaceData = fColorSpace ? fColorSpace->serialize() : nullptr; 53cb93a386Sopenharmony_ci if (colorSpaceData) { 54cb93a386Sopenharmony_ci flags |= kHasColorSpace_GSF; 55cb93a386Sopenharmony_ci } 56cb93a386Sopenharmony_ci SkASSERT(static_cast<uint32_t>(fTileMode) <= kTileModeMask_GSF); 57cb93a386Sopenharmony_ci flags |= ((unsigned)fTileMode << kTileModeShift_GSF); 58cb93a386Sopenharmony_ci SkASSERT(fGradFlags <= kGradFlagsMask_GSF); 59cb93a386Sopenharmony_ci flags |= (fGradFlags << kGradFlagsShift_GSF); 60cb93a386Sopenharmony_ci 61cb93a386Sopenharmony_ci buffer.writeUInt(flags); 62cb93a386Sopenharmony_ci 63cb93a386Sopenharmony_ci buffer.writeColor4fArray(fColors, fCount); 64cb93a386Sopenharmony_ci if (colorSpaceData) { 65cb93a386Sopenharmony_ci buffer.writeDataAsByteArray(colorSpaceData.get()); 66cb93a386Sopenharmony_ci } 67cb93a386Sopenharmony_ci if (fPos) { 68cb93a386Sopenharmony_ci buffer.writeScalarArray(fPos, fCount); 69cb93a386Sopenharmony_ci } 70cb93a386Sopenharmony_ci if (fLocalMatrix) { 71cb93a386Sopenharmony_ci buffer.writeMatrix(*fLocalMatrix); 72cb93a386Sopenharmony_ci } 73cb93a386Sopenharmony_ci} 74cb93a386Sopenharmony_ci 75cb93a386Sopenharmony_citemplate <int N, typename T, bool MEM_MOVE> 76cb93a386Sopenharmony_cistatic bool validate_array(SkReadBuffer& buffer, size_t count, SkSTArray<N, T, MEM_MOVE>* array) { 77cb93a386Sopenharmony_ci if (!buffer.validateCanReadN<T>(count)) { 78cb93a386Sopenharmony_ci return false; 79cb93a386Sopenharmony_ci } 80cb93a386Sopenharmony_ci 81cb93a386Sopenharmony_ci array->resize_back(count); 82cb93a386Sopenharmony_ci return true; 83cb93a386Sopenharmony_ci} 84cb93a386Sopenharmony_ci 85cb93a386Sopenharmony_cibool SkGradientShaderBase::DescriptorScope::unflatten(SkReadBuffer& buffer) { 86cb93a386Sopenharmony_ci // New gradient format. Includes floating point color, color space, densely packed flags 87cb93a386Sopenharmony_ci uint32_t flags = buffer.readUInt(); 88cb93a386Sopenharmony_ci 89cb93a386Sopenharmony_ci fTileMode = (SkTileMode)((flags >> kTileModeShift_GSF) & kTileModeMask_GSF); 90cb93a386Sopenharmony_ci fGradFlags = (flags >> kGradFlagsShift_GSF) & kGradFlagsMask_GSF; 91cb93a386Sopenharmony_ci 92cb93a386Sopenharmony_ci fCount = buffer.getArrayCount(); 93cb93a386Sopenharmony_ci 94cb93a386Sopenharmony_ci if (!(validate_array(buffer, fCount, &fColorStorage) && 95cb93a386Sopenharmony_ci buffer.readColor4fArray(fColorStorage.begin(), fCount))) { 96cb93a386Sopenharmony_ci return false; 97cb93a386Sopenharmony_ci } 98cb93a386Sopenharmony_ci fColors = fColorStorage.begin(); 99cb93a386Sopenharmony_ci 100cb93a386Sopenharmony_ci if (SkToBool(flags & kHasColorSpace_GSF)) { 101cb93a386Sopenharmony_ci sk_sp<SkData> data = buffer.readByteArrayAsData(); 102cb93a386Sopenharmony_ci fColorSpace = data ? SkColorSpace::Deserialize(data->data(), data->size()) : nullptr; 103cb93a386Sopenharmony_ci } else { 104cb93a386Sopenharmony_ci fColorSpace = nullptr; 105cb93a386Sopenharmony_ci } 106cb93a386Sopenharmony_ci if (SkToBool(flags & kHasPosition_GSF)) { 107cb93a386Sopenharmony_ci if (!(validate_array(buffer, fCount, &fPosStorage) && 108cb93a386Sopenharmony_ci buffer.readScalarArray(fPosStorage.begin(), fCount))) { 109cb93a386Sopenharmony_ci return false; 110cb93a386Sopenharmony_ci } 111cb93a386Sopenharmony_ci fPos = fPosStorage.begin(); 112cb93a386Sopenharmony_ci } else { 113cb93a386Sopenharmony_ci fPos = nullptr; 114cb93a386Sopenharmony_ci } 115cb93a386Sopenharmony_ci if (SkToBool(flags & kHasLocalMatrix_GSF)) { 116cb93a386Sopenharmony_ci fLocalMatrix = &fLocalMatrixStorage; 117cb93a386Sopenharmony_ci buffer.readMatrix(&fLocalMatrixStorage); 118cb93a386Sopenharmony_ci } else { 119cb93a386Sopenharmony_ci fLocalMatrix = nullptr; 120cb93a386Sopenharmony_ci } 121cb93a386Sopenharmony_ci return buffer.isValid(); 122cb93a386Sopenharmony_ci} 123cb93a386Sopenharmony_ci 124cb93a386Sopenharmony_ci//////////////////////////////////////////////////////////////////////////////////////////// 125cb93a386Sopenharmony_ci 126cb93a386Sopenharmony_ciSkGradientShaderBase::SkGradientShaderBase(const Descriptor& desc, const SkMatrix& ptsToUnit) 127cb93a386Sopenharmony_ci : INHERITED(desc.fLocalMatrix) 128cb93a386Sopenharmony_ci , fPtsToUnit(ptsToUnit) 129cb93a386Sopenharmony_ci , fColorSpace(desc.fColorSpace ? desc.fColorSpace : SkColorSpace::MakeSRGB()) 130cb93a386Sopenharmony_ci , fColorsAreOpaque(true) 131cb93a386Sopenharmony_ci{ 132cb93a386Sopenharmony_ci fPtsToUnit.getType(); // Precache so reads are threadsafe. 133cb93a386Sopenharmony_ci SkASSERT(desc.fCount > 1); 134cb93a386Sopenharmony_ci 135cb93a386Sopenharmony_ci fGradFlags = static_cast<uint8_t>(desc.fGradFlags); 136cb93a386Sopenharmony_ci 137cb93a386Sopenharmony_ci SkASSERT((unsigned)desc.fTileMode < kSkTileModeCount); 138cb93a386Sopenharmony_ci fTileMode = desc.fTileMode; 139cb93a386Sopenharmony_ci 140cb93a386Sopenharmony_ci /* Note: we let the caller skip the first and/or last position. 141cb93a386Sopenharmony_ci i.e. pos[0] = 0.3, pos[1] = 0.7 142cb93a386Sopenharmony_ci In these cases, we insert entries to ensure that the final data 143cb93a386Sopenharmony_ci will be bracketed by [0, 1]. 144cb93a386Sopenharmony_ci i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1 145cb93a386Sopenharmony_ci 146cb93a386Sopenharmony_ci Thus colorCount (the caller's value, and fColorCount (our value) may 147cb93a386Sopenharmony_ci differ by up to 2. In the above example: 148cb93a386Sopenharmony_ci colorCount = 2 149cb93a386Sopenharmony_ci fColorCount = 4 150cb93a386Sopenharmony_ci */ 151cb93a386Sopenharmony_ci fColorCount = desc.fCount; 152cb93a386Sopenharmony_ci // check if we need to add in start and/or end position/colors 153cb93a386Sopenharmony_ci bool needsFirst = false; 154cb93a386Sopenharmony_ci bool needsLast = false; 155cb93a386Sopenharmony_ci if (desc.fPos) { 156cb93a386Sopenharmony_ci needsFirst = desc.fPos[0] != 0; 157cb93a386Sopenharmony_ci needsLast = desc.fPos[desc.fCount - 1] != SK_Scalar1; 158cb93a386Sopenharmony_ci fColorCount += needsFirst + needsLast; 159cb93a386Sopenharmony_ci } 160cb93a386Sopenharmony_ci 161cb93a386Sopenharmony_ci size_t storageSize = fColorCount * (sizeof(SkColor4f) + (desc.fPos ? sizeof(SkScalar) : 0)); 162cb93a386Sopenharmony_ci fOrigColors4f = reinterpret_cast<SkColor4f*>(fStorage.reset(storageSize)); 163cb93a386Sopenharmony_ci fOrigPos = desc.fPos ? reinterpret_cast<SkScalar*>(fOrigColors4f + fColorCount) 164cb93a386Sopenharmony_ci : nullptr; 165cb93a386Sopenharmony_ci 166cb93a386Sopenharmony_ci // Now copy over the colors, adding the dummies as needed 167cb93a386Sopenharmony_ci SkColor4f* origColors = fOrigColors4f; 168cb93a386Sopenharmony_ci if (needsFirst) { 169cb93a386Sopenharmony_ci *origColors++ = desc.fColors[0]; 170cb93a386Sopenharmony_ci } 171cb93a386Sopenharmony_ci for (int i = 0; i < desc.fCount; ++i) { 172cb93a386Sopenharmony_ci origColors[i] = desc.fColors[i]; 173cb93a386Sopenharmony_ci fColorsAreOpaque = fColorsAreOpaque && (desc.fColors[i].fA == 1); 174cb93a386Sopenharmony_ci } 175cb93a386Sopenharmony_ci if (needsLast) { 176cb93a386Sopenharmony_ci origColors += desc.fCount; 177cb93a386Sopenharmony_ci *origColors = desc.fColors[desc.fCount - 1]; 178cb93a386Sopenharmony_ci } 179cb93a386Sopenharmony_ci 180cb93a386Sopenharmony_ci if (desc.fPos) { 181cb93a386Sopenharmony_ci SkScalar prev = 0; 182cb93a386Sopenharmony_ci SkScalar* origPosPtr = fOrigPos; 183cb93a386Sopenharmony_ci *origPosPtr++ = prev; // force the first pos to 0 184cb93a386Sopenharmony_ci 185cb93a386Sopenharmony_ci int startIndex = needsFirst ? 0 : 1; 186cb93a386Sopenharmony_ci int count = desc.fCount + needsLast; 187cb93a386Sopenharmony_ci 188cb93a386Sopenharmony_ci bool uniformStops = true; 189cb93a386Sopenharmony_ci const SkScalar uniformStep = desc.fPos[startIndex] - prev; 190cb93a386Sopenharmony_ci for (int i = startIndex; i < count; i++) { 191cb93a386Sopenharmony_ci // Pin the last value to 1.0, and make sure pos is monotonic. 192cb93a386Sopenharmony_ci auto curr = (i == desc.fCount) ? 1 : SkTPin(desc.fPos[i], prev, 1.0f); 193cb93a386Sopenharmony_ci uniformStops &= SkScalarNearlyEqual(uniformStep, curr - prev); 194cb93a386Sopenharmony_ci 195cb93a386Sopenharmony_ci *origPosPtr++ = prev = curr; 196cb93a386Sopenharmony_ci } 197cb93a386Sopenharmony_ci 198cb93a386Sopenharmony_ci // If the stops are uniform, treat them as implicit. 199cb93a386Sopenharmony_ci if (uniformStops) { 200cb93a386Sopenharmony_ci fOrigPos = nullptr; 201cb93a386Sopenharmony_ci } 202cb93a386Sopenharmony_ci } 203cb93a386Sopenharmony_ci} 204cb93a386Sopenharmony_ci 205cb93a386Sopenharmony_ciSkGradientShaderBase::~SkGradientShaderBase() {} 206cb93a386Sopenharmony_ci 207cb93a386Sopenharmony_civoid SkGradientShaderBase::flatten(SkWriteBuffer& buffer) const { 208cb93a386Sopenharmony_ci Descriptor desc; 209cb93a386Sopenharmony_ci desc.fColors = fOrigColors4f; 210cb93a386Sopenharmony_ci desc.fColorSpace = fColorSpace; 211cb93a386Sopenharmony_ci desc.fPos = fOrigPos; 212cb93a386Sopenharmony_ci desc.fCount = fColorCount; 213cb93a386Sopenharmony_ci desc.fTileMode = fTileMode; 214cb93a386Sopenharmony_ci desc.fGradFlags = fGradFlags; 215cb93a386Sopenharmony_ci 216cb93a386Sopenharmony_ci const SkMatrix& m = this->getLocalMatrix(); 217cb93a386Sopenharmony_ci desc.fLocalMatrix = m.isIdentity() ? nullptr : &m; 218cb93a386Sopenharmony_ci desc.flatten(buffer); 219cb93a386Sopenharmony_ci} 220cb93a386Sopenharmony_ci 221cb93a386Sopenharmony_cistatic void add_stop_color(SkRasterPipeline_GradientCtx* ctx, size_t stop, SkPMColor4f Fs, SkPMColor4f Bs) { 222cb93a386Sopenharmony_ci (ctx->fs[0])[stop] = Fs.fR; 223cb93a386Sopenharmony_ci (ctx->fs[1])[stop] = Fs.fG; 224cb93a386Sopenharmony_ci (ctx->fs[2])[stop] = Fs.fB; 225cb93a386Sopenharmony_ci (ctx->fs[3])[stop] = Fs.fA; 226cb93a386Sopenharmony_ci 227cb93a386Sopenharmony_ci (ctx->bs[0])[stop] = Bs.fR; 228cb93a386Sopenharmony_ci (ctx->bs[1])[stop] = Bs.fG; 229cb93a386Sopenharmony_ci (ctx->bs[2])[stop] = Bs.fB; 230cb93a386Sopenharmony_ci (ctx->bs[3])[stop] = Bs.fA; 231cb93a386Sopenharmony_ci} 232cb93a386Sopenharmony_ci 233cb93a386Sopenharmony_cistatic void add_const_color(SkRasterPipeline_GradientCtx* ctx, size_t stop, SkPMColor4f color) { 234cb93a386Sopenharmony_ci add_stop_color(ctx, stop, { 0, 0, 0, 0 }, color); 235cb93a386Sopenharmony_ci} 236cb93a386Sopenharmony_ci 237cb93a386Sopenharmony_ci// Calculate a factor F and a bias B so that color = F*t + B when t is in range of 238cb93a386Sopenharmony_ci// the stop. Assume that the distance between stops is 1/gapCount. 239cb93a386Sopenharmony_cistatic void init_stop_evenly( 240cb93a386Sopenharmony_ci SkRasterPipeline_GradientCtx* ctx, float gapCount, size_t stop, SkPMColor4f c_l, SkPMColor4f c_r) { 241cb93a386Sopenharmony_ci // Clankium's GCC 4.9 targeting ARMv7 is barfing when we use Sk4f math here, so go scalar... 242cb93a386Sopenharmony_ci SkPMColor4f Fs = { 243cb93a386Sopenharmony_ci (c_r.fR - c_l.fR) * gapCount, 244cb93a386Sopenharmony_ci (c_r.fG - c_l.fG) * gapCount, 245cb93a386Sopenharmony_ci (c_r.fB - c_l.fB) * gapCount, 246cb93a386Sopenharmony_ci (c_r.fA - c_l.fA) * gapCount, 247cb93a386Sopenharmony_ci }; 248cb93a386Sopenharmony_ci SkPMColor4f Bs = { 249cb93a386Sopenharmony_ci c_l.fR - Fs.fR*(stop/gapCount), 250cb93a386Sopenharmony_ci c_l.fG - Fs.fG*(stop/gapCount), 251cb93a386Sopenharmony_ci c_l.fB - Fs.fB*(stop/gapCount), 252cb93a386Sopenharmony_ci c_l.fA - Fs.fA*(stop/gapCount), 253cb93a386Sopenharmony_ci }; 254cb93a386Sopenharmony_ci add_stop_color(ctx, stop, Fs, Bs); 255cb93a386Sopenharmony_ci} 256cb93a386Sopenharmony_ci 257cb93a386Sopenharmony_ci// For each stop we calculate a bias B and a scale factor F, such that 258cb93a386Sopenharmony_ci// for any t between stops n and n+1, the color we want is B[n] + F[n]*t. 259cb93a386Sopenharmony_cistatic void init_stop_pos( 260cb93a386Sopenharmony_ci SkRasterPipeline_GradientCtx* ctx, size_t stop, float t_l, float t_r, SkPMColor4f c_l, SkPMColor4f c_r) { 261cb93a386Sopenharmony_ci // See note about Clankium's old compiler in init_stop_evenly(). 262cb93a386Sopenharmony_ci SkPMColor4f Fs = { 263cb93a386Sopenharmony_ci (c_r.fR - c_l.fR) / (t_r - t_l), 264cb93a386Sopenharmony_ci (c_r.fG - c_l.fG) / (t_r - t_l), 265cb93a386Sopenharmony_ci (c_r.fB - c_l.fB) / (t_r - t_l), 266cb93a386Sopenharmony_ci (c_r.fA - c_l.fA) / (t_r - t_l), 267cb93a386Sopenharmony_ci }; 268cb93a386Sopenharmony_ci SkPMColor4f Bs = { 269cb93a386Sopenharmony_ci c_l.fR - Fs.fR*t_l, 270cb93a386Sopenharmony_ci c_l.fG - Fs.fG*t_l, 271cb93a386Sopenharmony_ci c_l.fB - Fs.fB*t_l, 272cb93a386Sopenharmony_ci c_l.fA - Fs.fA*t_l, 273cb93a386Sopenharmony_ci }; 274cb93a386Sopenharmony_ci ctx->ts[stop] = t_l; 275cb93a386Sopenharmony_ci add_stop_color(ctx, stop, Fs, Bs); 276cb93a386Sopenharmony_ci} 277cb93a386Sopenharmony_ci 278cb93a386Sopenharmony_cibool SkGradientShaderBase::onAppendStages(const SkStageRec& rec) const { 279cb93a386Sopenharmony_ci SkRasterPipeline* p = rec.fPipeline; 280cb93a386Sopenharmony_ci SkArenaAlloc* alloc = rec.fAlloc; 281cb93a386Sopenharmony_ci SkRasterPipeline_DecalTileCtx* decal_ctx = nullptr; 282cb93a386Sopenharmony_ci 283cb93a386Sopenharmony_ci SkMatrix matrix; 284cb93a386Sopenharmony_ci if (!this->computeTotalInverse(rec.fMatrixProvider.localToDevice(), rec.fLocalM, &matrix)) { 285cb93a386Sopenharmony_ci return false; 286cb93a386Sopenharmony_ci } 287cb93a386Sopenharmony_ci matrix.postConcat(fPtsToUnit); 288cb93a386Sopenharmony_ci 289cb93a386Sopenharmony_ci SkRasterPipeline_<256> postPipeline; 290cb93a386Sopenharmony_ci 291cb93a386Sopenharmony_ci p->append(SkRasterPipeline::seed_shader); 292cb93a386Sopenharmony_ci p->append_matrix(alloc, matrix); 293cb93a386Sopenharmony_ci this->appendGradientStages(alloc, p, &postPipeline); 294cb93a386Sopenharmony_ci 295cb93a386Sopenharmony_ci switch(fTileMode) { 296cb93a386Sopenharmony_ci case SkTileMode::kMirror: p->append(SkRasterPipeline::mirror_x_1); break; 297cb93a386Sopenharmony_ci case SkTileMode::kRepeat: p->append(SkRasterPipeline::repeat_x_1); break; 298cb93a386Sopenharmony_ci case SkTileMode::kDecal: 299cb93a386Sopenharmony_ci decal_ctx = alloc->make<SkRasterPipeline_DecalTileCtx>(); 300cb93a386Sopenharmony_ci decal_ctx->limit_x = SkBits2Float(SkFloat2Bits(1.0f) + 1); 301cb93a386Sopenharmony_ci // reuse mask + limit_x stage, or create a custom decal_1 that just stores the mask 302cb93a386Sopenharmony_ci p->append(SkRasterPipeline::decal_x, decal_ctx); 303cb93a386Sopenharmony_ci [[fallthrough]]; 304cb93a386Sopenharmony_ci 305cb93a386Sopenharmony_ci case SkTileMode::kClamp: 306cb93a386Sopenharmony_ci if (!fOrigPos) { 307cb93a386Sopenharmony_ci // We clamp only when the stops are evenly spaced. 308cb93a386Sopenharmony_ci // If not, there may be hard stops, and clamping ruins hard stops at 0 and/or 1. 309cb93a386Sopenharmony_ci // In that case, we must make sure we're using the general "gradient" stage, 310cb93a386Sopenharmony_ci // which is the only stage that will correctly handle unclamped t. 311cb93a386Sopenharmony_ci p->append(SkRasterPipeline::clamp_x_1); 312cb93a386Sopenharmony_ci } 313cb93a386Sopenharmony_ci break; 314cb93a386Sopenharmony_ci } 315cb93a386Sopenharmony_ci 316cb93a386Sopenharmony_ci const bool premulGrad = fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag; 317cb93a386Sopenharmony_ci 318cb93a386Sopenharmony_ci // Transform all of the colors to destination color space 319cb93a386Sopenharmony_ci SkColor4fXformer xformedColors(fOrigColors4f, fColorCount, fColorSpace.get(), rec.fDstCS); 320cb93a386Sopenharmony_ci 321cb93a386Sopenharmony_ci auto prepareColor = [premulGrad, &xformedColors](int i) { 322cb93a386Sopenharmony_ci SkColor4f c = xformedColors.fColors[i]; 323cb93a386Sopenharmony_ci return premulGrad ? c.premul() 324cb93a386Sopenharmony_ci : SkPMColor4f{ c.fR, c.fG, c.fB, c.fA }; 325cb93a386Sopenharmony_ci }; 326cb93a386Sopenharmony_ci 327cb93a386Sopenharmony_ci // The two-stop case with stops at 0 and 1. 328cb93a386Sopenharmony_ci if (fColorCount == 2 && fOrigPos == nullptr) { 329cb93a386Sopenharmony_ci const SkPMColor4f c_l = prepareColor(0), 330cb93a386Sopenharmony_ci c_r = prepareColor(1); 331cb93a386Sopenharmony_ci 332cb93a386Sopenharmony_ci // See F and B below. 333cb93a386Sopenharmony_ci auto ctx = alloc->make<SkRasterPipeline_EvenlySpaced2StopGradientCtx>(); 334cb93a386Sopenharmony_ci (Sk4f::Load(c_r.vec()) - Sk4f::Load(c_l.vec())).store(ctx->f); 335cb93a386Sopenharmony_ci ( Sk4f::Load(c_l.vec())).store(ctx->b); 336cb93a386Sopenharmony_ci ctx->interpolatedInPremul = premulGrad; 337cb93a386Sopenharmony_ci 338cb93a386Sopenharmony_ci p->append(SkRasterPipeline::evenly_spaced_2_stop_gradient, ctx); 339cb93a386Sopenharmony_ci } else { 340cb93a386Sopenharmony_ci auto* ctx = alloc->make<SkRasterPipeline_GradientCtx>(); 341cb93a386Sopenharmony_ci ctx->interpolatedInPremul = premulGrad; 342cb93a386Sopenharmony_ci 343cb93a386Sopenharmony_ci // Note: In order to handle clamps in search, the search assumes a stop conceptully placed 344cb93a386Sopenharmony_ci // at -inf. Therefore, the max number of stops is fColorCount+1. 345cb93a386Sopenharmony_ci for (int i = 0; i < 4; i++) { 346cb93a386Sopenharmony_ci // Allocate at least at for the AVX2 gather from a YMM register. 347cb93a386Sopenharmony_ci ctx->fs[i] = alloc->makeArray<float>(std::max(fColorCount+1, 8)); 348cb93a386Sopenharmony_ci ctx->bs[i] = alloc->makeArray<float>(std::max(fColorCount+1, 8)); 349cb93a386Sopenharmony_ci } 350cb93a386Sopenharmony_ci 351cb93a386Sopenharmony_ci if (fOrigPos == nullptr) { 352cb93a386Sopenharmony_ci // Handle evenly distributed stops. 353cb93a386Sopenharmony_ci 354cb93a386Sopenharmony_ci size_t stopCount = fColorCount; 355cb93a386Sopenharmony_ci float gapCount = stopCount - 1; 356cb93a386Sopenharmony_ci 357cb93a386Sopenharmony_ci SkPMColor4f c_l = prepareColor(0); 358cb93a386Sopenharmony_ci for (size_t i = 0; i < stopCount - 1; i++) { 359cb93a386Sopenharmony_ci SkPMColor4f c_r = prepareColor(i + 1); 360cb93a386Sopenharmony_ci init_stop_evenly(ctx, gapCount, i, c_l, c_r); 361cb93a386Sopenharmony_ci c_l = c_r; 362cb93a386Sopenharmony_ci } 363cb93a386Sopenharmony_ci add_const_color(ctx, stopCount - 1, c_l); 364cb93a386Sopenharmony_ci 365cb93a386Sopenharmony_ci ctx->stopCount = stopCount; 366cb93a386Sopenharmony_ci p->append(SkRasterPipeline::evenly_spaced_gradient, ctx); 367cb93a386Sopenharmony_ci } else { 368cb93a386Sopenharmony_ci // Handle arbitrary stops. 369cb93a386Sopenharmony_ci 370cb93a386Sopenharmony_ci ctx->ts = alloc->makeArray<float>(fColorCount+1); 371cb93a386Sopenharmony_ci 372cb93a386Sopenharmony_ci // Remove the default stops inserted by SkGradientShaderBase::SkGradientShaderBase 373cb93a386Sopenharmony_ci // because they are naturally handled by the search method. 374cb93a386Sopenharmony_ci int firstStop; 375cb93a386Sopenharmony_ci int lastStop; 376cb93a386Sopenharmony_ci if (fColorCount > 2) { 377cb93a386Sopenharmony_ci firstStop = fOrigColors4f[0] != fOrigColors4f[1] ? 0 : 1; 378cb93a386Sopenharmony_ci lastStop = fOrigColors4f[fColorCount - 2] != fOrigColors4f[fColorCount - 1] 379cb93a386Sopenharmony_ci ? fColorCount - 1 : fColorCount - 2; 380cb93a386Sopenharmony_ci } else { 381cb93a386Sopenharmony_ci firstStop = 0; 382cb93a386Sopenharmony_ci lastStop = 1; 383cb93a386Sopenharmony_ci } 384cb93a386Sopenharmony_ci 385cb93a386Sopenharmony_ci size_t stopCount = 0; 386cb93a386Sopenharmony_ci float t_l = fOrigPos[firstStop]; 387cb93a386Sopenharmony_ci SkPMColor4f c_l = prepareColor(firstStop); 388cb93a386Sopenharmony_ci add_const_color(ctx, stopCount++, c_l); 389cb93a386Sopenharmony_ci // N.B. lastStop is the index of the last stop, not one after. 390cb93a386Sopenharmony_ci for (int i = firstStop; i < lastStop; i++) { 391cb93a386Sopenharmony_ci float t_r = fOrigPos[i + 1]; 392cb93a386Sopenharmony_ci SkPMColor4f c_r = prepareColor(i + 1); 393cb93a386Sopenharmony_ci SkASSERT(t_l <= t_r); 394cb93a386Sopenharmony_ci if (t_l < t_r) { 395cb93a386Sopenharmony_ci init_stop_pos(ctx, stopCount, t_l, t_r, c_l, c_r); 396cb93a386Sopenharmony_ci stopCount += 1; 397cb93a386Sopenharmony_ci } 398cb93a386Sopenharmony_ci t_l = t_r; 399cb93a386Sopenharmony_ci c_l = c_r; 400cb93a386Sopenharmony_ci } 401cb93a386Sopenharmony_ci 402cb93a386Sopenharmony_ci ctx->ts[stopCount] = t_l; 403cb93a386Sopenharmony_ci add_const_color(ctx, stopCount++, c_l); 404cb93a386Sopenharmony_ci 405cb93a386Sopenharmony_ci ctx->stopCount = stopCount; 406cb93a386Sopenharmony_ci p->append(SkRasterPipeline::gradient, ctx); 407cb93a386Sopenharmony_ci } 408cb93a386Sopenharmony_ci } 409cb93a386Sopenharmony_ci 410cb93a386Sopenharmony_ci if (decal_ctx) { 411cb93a386Sopenharmony_ci p->append(SkRasterPipeline::check_decal_mask, decal_ctx); 412cb93a386Sopenharmony_ci } 413cb93a386Sopenharmony_ci 414cb93a386Sopenharmony_ci if (!premulGrad && !this->colorsAreOpaque()) { 415cb93a386Sopenharmony_ci p->append(SkRasterPipeline::premul); 416cb93a386Sopenharmony_ci } 417cb93a386Sopenharmony_ci 418cb93a386Sopenharmony_ci p->extend(postPipeline); 419cb93a386Sopenharmony_ci 420cb93a386Sopenharmony_ci return true; 421cb93a386Sopenharmony_ci} 422cb93a386Sopenharmony_ci 423cb93a386Sopenharmony_ciskvm::Color SkGradientShaderBase::onProgram(skvm::Builder* p, 424cb93a386Sopenharmony_ci skvm::Coord device, skvm::Coord local, 425cb93a386Sopenharmony_ci skvm::Color /*paint*/, 426cb93a386Sopenharmony_ci const SkMatrixProvider& mats, const SkMatrix* localM, 427cb93a386Sopenharmony_ci const SkColorInfo& dstInfo, 428cb93a386Sopenharmony_ci skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const { 429cb93a386Sopenharmony_ci SkMatrix inv; 430cb93a386Sopenharmony_ci if (!this->computeTotalInverse(mats.localToDevice(), localM, &inv)) { 431cb93a386Sopenharmony_ci return {}; 432cb93a386Sopenharmony_ci } 433cb93a386Sopenharmony_ci inv.postConcat(fPtsToUnit); 434cb93a386Sopenharmony_ci inv.normalizePerspective(); 435cb93a386Sopenharmony_ci 436cb93a386Sopenharmony_ci local = SkShaderBase::ApplyMatrix(p, inv, local, uniforms); 437cb93a386Sopenharmony_ci 438cb93a386Sopenharmony_ci skvm::I32 mask = p->splat(~0); 439cb93a386Sopenharmony_ci skvm::F32 t = this->transformT(p,uniforms, local, &mask); 440cb93a386Sopenharmony_ci 441cb93a386Sopenharmony_ci // Perhaps unexpectedly, clamping is handled naturally by our search, so we 442cb93a386Sopenharmony_ci // don't explicitly clamp t to [0,1]. That clamp would break hard stops 443cb93a386Sopenharmony_ci // right at 0 or 1 boundaries in kClamp mode. (kRepeat and kMirror always 444cb93a386Sopenharmony_ci // produce values in [0,1].) 445cb93a386Sopenharmony_ci switch(fTileMode) { 446cb93a386Sopenharmony_ci case SkTileMode::kClamp: 447cb93a386Sopenharmony_ci break; 448cb93a386Sopenharmony_ci 449cb93a386Sopenharmony_ci case SkTileMode::kDecal: 450cb93a386Sopenharmony_ci mask &= (t == clamp01(t)); 451cb93a386Sopenharmony_ci break; 452cb93a386Sopenharmony_ci 453cb93a386Sopenharmony_ci case SkTileMode::kRepeat: 454cb93a386Sopenharmony_ci t = fract(t); 455cb93a386Sopenharmony_ci break; 456cb93a386Sopenharmony_ci 457cb93a386Sopenharmony_ci case SkTileMode::kMirror: { 458cb93a386Sopenharmony_ci // t = | (t-1) - 2*(floor( (t-1)*0.5 )) - 1 | 459cb93a386Sopenharmony_ci // {-A-} {--------B-------} 460cb93a386Sopenharmony_ci skvm::F32 A = t - 1.0f, 461cb93a386Sopenharmony_ci B = floor(A * 0.5f); 462cb93a386Sopenharmony_ci t = abs(A - (B + B) - 1.0f); 463cb93a386Sopenharmony_ci } break; 464cb93a386Sopenharmony_ci } 465cb93a386Sopenharmony_ci 466cb93a386Sopenharmony_ci // Transform our colors as we want them interpolated, in dst color space, possibly premul. 467cb93a386Sopenharmony_ci SkImageInfo common = SkImageInfo::Make(fColorCount,1, kRGBA_F32_SkColorType 468cb93a386Sopenharmony_ci , kUnpremul_SkAlphaType), 469cb93a386Sopenharmony_ci src = common.makeColorSpace(fColorSpace), 470cb93a386Sopenharmony_ci dst = common.makeColorSpace(dstInfo.refColorSpace()); 471cb93a386Sopenharmony_ci if (fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag) { 472cb93a386Sopenharmony_ci dst = dst.makeAlphaType(kPremul_SkAlphaType); 473cb93a386Sopenharmony_ci } 474cb93a386Sopenharmony_ci 475cb93a386Sopenharmony_ci std::vector<float> rgba(4*fColorCount); // TODO: SkSTArray? 476cb93a386Sopenharmony_ci SkAssertResult(SkConvertPixels(dst, rgba.data(), dst.minRowBytes(), 477cb93a386Sopenharmony_ci src, fOrigColors4f, src.minRowBytes())); 478cb93a386Sopenharmony_ci 479cb93a386Sopenharmony_ci // Transform our colors into a scale factor f and bias b such that for 480cb93a386Sopenharmony_ci // any t between stops i and i+1, the color we want is mad(t, f[i], b[i]). 481cb93a386Sopenharmony_ci using F4 = skvx::Vec<4,float>; 482cb93a386Sopenharmony_ci struct FB { F4 f,b; }; 483cb93a386Sopenharmony_ci skvm::Color color; 484cb93a386Sopenharmony_ci 485cb93a386Sopenharmony_ci auto uniformF = [&](float x) { return p->uniformF(uniforms->pushF(x)); }; 486cb93a386Sopenharmony_ci 487cb93a386Sopenharmony_ci if (fColorCount == 2) { 488cb93a386Sopenharmony_ci // 2-stop gradients have colors at 0 and 1, and so must be evenly spaced. 489cb93a386Sopenharmony_ci SkASSERT(fOrigPos == nullptr); 490cb93a386Sopenharmony_ci 491cb93a386Sopenharmony_ci // With 2 stops, we upload the single FB as uniforms and interpolate directly with t. 492cb93a386Sopenharmony_ci F4 lo = F4::Load(rgba.data() + 0), 493cb93a386Sopenharmony_ci hi = F4::Load(rgba.data() + 4); 494cb93a386Sopenharmony_ci F4 F = hi - lo, 495cb93a386Sopenharmony_ci B = lo; 496cb93a386Sopenharmony_ci 497cb93a386Sopenharmony_ci auto T = clamp01(t); 498cb93a386Sopenharmony_ci color = { 499cb93a386Sopenharmony_ci T * uniformF(F[0]) + uniformF(B[0]), 500cb93a386Sopenharmony_ci T * uniformF(F[1]) + uniformF(B[1]), 501cb93a386Sopenharmony_ci T * uniformF(F[2]) + uniformF(B[2]), 502cb93a386Sopenharmony_ci T * uniformF(F[3]) + uniformF(B[3]), 503cb93a386Sopenharmony_ci }; 504cb93a386Sopenharmony_ci } else { 505cb93a386Sopenharmony_ci // To handle clamps in search we add a conceptual stop at t=-inf, so we 506cb93a386Sopenharmony_ci // may need up to fColorCount+1 FBs and fColorCount t stops between them: 507cb93a386Sopenharmony_ci // 508cb93a386Sopenharmony_ci // FBs: [color 0] [color 0->1] [color 1->2] [color 2->3] ... 509cb93a386Sopenharmony_ci // stops: (-inf) t0 t1 t2 ... 510cb93a386Sopenharmony_ci // 511cb93a386Sopenharmony_ci // Both these arrays could end up shorter if any hard stops share the same t. 512cb93a386Sopenharmony_ci FB* fb = alloc->makeArrayDefault<FB>(fColorCount+1); 513cb93a386Sopenharmony_ci std::vector<float> stops; // TODO: SkSTArray? 514cb93a386Sopenharmony_ci stops.reserve(fColorCount); 515cb93a386Sopenharmony_ci 516cb93a386Sopenharmony_ci // Here's our conceptual stop at t=-inf covering all t<=0, clamping to our first color. 517cb93a386Sopenharmony_ci float t_lo = this->getPos(0); 518cb93a386Sopenharmony_ci F4 color_lo = F4::Load(rgba.data()); 519cb93a386Sopenharmony_ci fb[0] = { 0.0f, color_lo }; 520cb93a386Sopenharmony_ci // N.B. No stops[] entry for this implicit -inf. 521cb93a386Sopenharmony_ci 522cb93a386Sopenharmony_ci // Now the non-edge cases, calculating scale and bias between adjacent normal stops. 523cb93a386Sopenharmony_ci for (int i = 1; i < fColorCount; i++) { 524cb93a386Sopenharmony_ci float t_hi = this->getPos(i); 525cb93a386Sopenharmony_ci F4 color_hi = F4::Load(rgba.data() + 4*i); 526cb93a386Sopenharmony_ci 527cb93a386Sopenharmony_ci // If t_lo == t_hi, we're on a hard stop, and transition immediately to the next color. 528cb93a386Sopenharmony_ci SkASSERT(t_lo <= t_hi); 529cb93a386Sopenharmony_ci if (t_lo < t_hi) { 530cb93a386Sopenharmony_ci F4 f = (color_hi - color_lo) / (t_hi - t_lo), 531cb93a386Sopenharmony_ci b = color_lo - f*t_lo; 532cb93a386Sopenharmony_ci stops.push_back(t_lo); 533cb93a386Sopenharmony_ci fb[stops.size()] = {f,b}; 534cb93a386Sopenharmony_ci } 535cb93a386Sopenharmony_ci 536cb93a386Sopenharmony_ci t_lo = t_hi; 537cb93a386Sopenharmony_ci color_lo = color_hi; 538cb93a386Sopenharmony_ci } 539cb93a386Sopenharmony_ci // Anything >= our final t clamps to our final color. 540cb93a386Sopenharmony_ci stops.push_back(t_lo); 541cb93a386Sopenharmony_ci fb[stops.size()] = { 0.0f, color_lo }; 542cb93a386Sopenharmony_ci 543cb93a386Sopenharmony_ci // We'll gather FBs from that array we just created. 544cb93a386Sopenharmony_ci skvm::Uniform fbs = uniforms->pushPtr(fb); 545cb93a386Sopenharmony_ci 546cb93a386Sopenharmony_ci // Find the two stops we need to interpolate. 547cb93a386Sopenharmony_ci skvm::I32 ix; 548cb93a386Sopenharmony_ci if (fOrigPos == nullptr) { 549cb93a386Sopenharmony_ci // Evenly spaced stops... we can calculate ix directly. 550cb93a386Sopenharmony_ci // Of note: we need to clamp t and skip over that conceptual -inf stop we made up. 551cb93a386Sopenharmony_ci ix = trunc(clamp01(t) * uniformF(stops.size() - 1) + 1.0f); 552cb93a386Sopenharmony_ci } else { 553cb93a386Sopenharmony_ci // Starting ix at 0 bakes in our conceptual first stop at -inf. 554cb93a386Sopenharmony_ci // TODO: good place to experiment with a loop in skvm.... stops.size() can be huge. 555cb93a386Sopenharmony_ci ix = p->splat(0); 556cb93a386Sopenharmony_ci for (float stop : stops) { 557cb93a386Sopenharmony_ci // ix += (t >= stop) ? +1 : 0 ~~> 558cb93a386Sopenharmony_ci // ix -= (t >= stop) ? -1 : 0 559cb93a386Sopenharmony_ci ix -= (t >= uniformF(stop)); 560cb93a386Sopenharmony_ci } 561cb93a386Sopenharmony_ci // TODO: we could skip any of the default stops GradientShaderBase's ctor added 562cb93a386Sopenharmony_ci // to ensure the full [0,1] span is covered. This linear search doesn't need 563cb93a386Sopenharmony_ci // them for correctness, and it'd be up to two fewer stops to check. 564cb93a386Sopenharmony_ci // N.B. we do still need those stops for the fOrigPos == nullptr direct math path. 565cb93a386Sopenharmony_ci } 566cb93a386Sopenharmony_ci 567cb93a386Sopenharmony_ci // A scale factor and bias for each lane, 8 total. 568cb93a386Sopenharmony_ci // TODO: simpler, faster, tidier to push 8 uniform pointers, one for each struct lane? 569cb93a386Sopenharmony_ci ix = shl(ix, 3); 570cb93a386Sopenharmony_ci skvm::F32 Fr = gatherF(fbs, ix + 0); 571cb93a386Sopenharmony_ci skvm::F32 Fg = gatherF(fbs, ix + 1); 572cb93a386Sopenharmony_ci skvm::F32 Fb = gatherF(fbs, ix + 2); 573cb93a386Sopenharmony_ci skvm::F32 Fa = gatherF(fbs, ix + 3); 574cb93a386Sopenharmony_ci 575cb93a386Sopenharmony_ci skvm::F32 Br = gatherF(fbs, ix + 4); 576cb93a386Sopenharmony_ci skvm::F32 Bg = gatherF(fbs, ix + 5); 577cb93a386Sopenharmony_ci skvm::F32 Bb = gatherF(fbs, ix + 6); 578cb93a386Sopenharmony_ci skvm::F32 Ba = gatherF(fbs, ix + 7); 579cb93a386Sopenharmony_ci 580cb93a386Sopenharmony_ci // This is what we've been building towards! 581cb93a386Sopenharmony_ci color = { 582cb93a386Sopenharmony_ci t * Fr + Br, 583cb93a386Sopenharmony_ci t * Fg + Bg, 584cb93a386Sopenharmony_ci t * Fb + Bb, 585cb93a386Sopenharmony_ci t * Fa + Ba, 586cb93a386Sopenharmony_ci }; 587cb93a386Sopenharmony_ci } 588cb93a386Sopenharmony_ci 589cb93a386Sopenharmony_ci // If we interpolated unpremul, premul now to match our output convention. 590cb93a386Sopenharmony_ci if (0 == (fGradFlags & SkGradientShader::kInterpolateColorsInPremul_Flag) 591cb93a386Sopenharmony_ci && !fColorsAreOpaque) { 592cb93a386Sopenharmony_ci color = premul(color); 593cb93a386Sopenharmony_ci } 594cb93a386Sopenharmony_ci 595cb93a386Sopenharmony_ci return { 596cb93a386Sopenharmony_ci pun_to_F32(mask & pun_to_I32(color.r)), 597cb93a386Sopenharmony_ci pun_to_F32(mask & pun_to_I32(color.g)), 598cb93a386Sopenharmony_ci pun_to_F32(mask & pun_to_I32(color.b)), 599cb93a386Sopenharmony_ci pun_to_F32(mask & pun_to_I32(color.a)), 600cb93a386Sopenharmony_ci }; 601cb93a386Sopenharmony_ci} 602cb93a386Sopenharmony_ci 603cb93a386Sopenharmony_ci 604cb93a386Sopenharmony_cibool SkGradientShaderBase::isOpaque() const { 605cb93a386Sopenharmony_ci return fColorsAreOpaque && (this->getTileMode() != SkTileMode::kDecal); 606cb93a386Sopenharmony_ci} 607cb93a386Sopenharmony_ci 608cb93a386Sopenharmony_cistatic unsigned rounded_divide(unsigned numer, unsigned denom) { 609cb93a386Sopenharmony_ci return (numer + (denom >> 1)) / denom; 610cb93a386Sopenharmony_ci} 611cb93a386Sopenharmony_ci 612cb93a386Sopenharmony_cibool SkGradientShaderBase::onAsLuminanceColor(SkColor* lum) const { 613cb93a386Sopenharmony_ci // we just compute an average color. 614cb93a386Sopenharmony_ci // possibly we could weight this based on the proportional width for each color 615cb93a386Sopenharmony_ci // assuming they are not evenly distributed in the fPos array. 616cb93a386Sopenharmony_ci int r = 0; 617cb93a386Sopenharmony_ci int g = 0; 618cb93a386Sopenharmony_ci int b = 0; 619cb93a386Sopenharmony_ci const int n = fColorCount; 620cb93a386Sopenharmony_ci // TODO: use linear colors? 621cb93a386Sopenharmony_ci for (int i = 0; i < n; ++i) { 622cb93a386Sopenharmony_ci SkColor c = this->getLegacyColor(i); 623cb93a386Sopenharmony_ci r += SkColorGetR(c); 624cb93a386Sopenharmony_ci g += SkColorGetG(c); 625cb93a386Sopenharmony_ci b += SkColorGetB(c); 626cb93a386Sopenharmony_ci } 627cb93a386Sopenharmony_ci *lum = SkColorSetRGB(rounded_divide(r, n), rounded_divide(g, n), rounded_divide(b, n)); 628cb93a386Sopenharmony_ci return true; 629cb93a386Sopenharmony_ci} 630cb93a386Sopenharmony_ci 631cb93a386Sopenharmony_ciSkColor4fXformer::SkColor4fXformer(const SkColor4f* colors, int colorCount, 632cb93a386Sopenharmony_ci SkColorSpace* src, SkColorSpace* dst) { 633cb93a386Sopenharmony_ci fColors = colors; 634cb93a386Sopenharmony_ci 635cb93a386Sopenharmony_ci if (dst && !SkColorSpace::Equals(src, dst)) { 636cb93a386Sopenharmony_ci fStorage.reset(colorCount); 637cb93a386Sopenharmony_ci 638cb93a386Sopenharmony_ci auto info = SkImageInfo::Make(colorCount,1, kRGBA_F32_SkColorType, kUnpremul_SkAlphaType); 639cb93a386Sopenharmony_ci 640cb93a386Sopenharmony_ci auto dstInfo = info.makeColorSpace(sk_ref_sp(dst)); 641cb93a386Sopenharmony_ci auto srcInfo = info.makeColorSpace(sk_ref_sp(src)); 642cb93a386Sopenharmony_ci SkAssertResult(SkConvertPixels(dstInfo, fStorage.begin(), info.minRowBytes(), 643cb93a386Sopenharmony_ci srcInfo, fColors , info.minRowBytes())); 644cb93a386Sopenharmony_ci 645cb93a386Sopenharmony_ci fColors = fStorage.begin(); 646cb93a386Sopenharmony_ci } 647cb93a386Sopenharmony_ci} 648cb93a386Sopenharmony_ci 649cb93a386Sopenharmony_civoid SkGradientShaderBase::commonAsAGradient(GradientInfo* info) const { 650cb93a386Sopenharmony_ci if (info) { 651cb93a386Sopenharmony_ci if (info->fColorCount >= fColorCount) { 652cb93a386Sopenharmony_ci if (info->fColors) { 653cb93a386Sopenharmony_ci for (int i = 0; i < fColorCount; ++i) { 654cb93a386Sopenharmony_ci info->fColors[i] = this->getLegacyColor(i); 655cb93a386Sopenharmony_ci } 656cb93a386Sopenharmony_ci } 657cb93a386Sopenharmony_ci if (info->fColorOffsets) { 658cb93a386Sopenharmony_ci for (int i = 0; i < fColorCount; ++i) { 659cb93a386Sopenharmony_ci info->fColorOffsets[i] = this->getPos(i); 660cb93a386Sopenharmony_ci } 661cb93a386Sopenharmony_ci } 662cb93a386Sopenharmony_ci } 663cb93a386Sopenharmony_ci info->fColorCount = fColorCount; 664cb93a386Sopenharmony_ci info->fTileMode = fTileMode; 665cb93a386Sopenharmony_ci info->fGradientFlags = fGradFlags; 666cb93a386Sopenharmony_ci } 667cb93a386Sopenharmony_ci} 668cb93a386Sopenharmony_ci 669cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 670cb93a386Sopenharmony_ci/////////////////////////////////////////////////////////////////////////////// 671cb93a386Sopenharmony_ci 672cb93a386Sopenharmony_ci// Return true if these parameters are valid/legal/safe to construct a gradient 673cb93a386Sopenharmony_ci// 674cb93a386Sopenharmony_cistatic bool valid_grad(const SkColor4f colors[], const SkScalar pos[], int count, 675cb93a386Sopenharmony_ci SkTileMode tileMode) { 676cb93a386Sopenharmony_ci return nullptr != colors && count >= 1 && (unsigned)tileMode < kSkTileModeCount; 677cb93a386Sopenharmony_ci} 678cb93a386Sopenharmony_ci 679cb93a386Sopenharmony_cistatic void desc_init(SkGradientShaderBase::Descriptor* desc, 680cb93a386Sopenharmony_ci const SkColor4f colors[], sk_sp<SkColorSpace> colorSpace, 681cb93a386Sopenharmony_ci const SkScalar pos[], int colorCount, 682cb93a386Sopenharmony_ci SkTileMode mode, uint32_t flags, const SkMatrix* localMatrix) { 683cb93a386Sopenharmony_ci SkASSERT(colorCount > 1); 684cb93a386Sopenharmony_ci 685cb93a386Sopenharmony_ci desc->fColors = colors; 686cb93a386Sopenharmony_ci desc->fColorSpace = std::move(colorSpace); 687cb93a386Sopenharmony_ci desc->fPos = pos; 688cb93a386Sopenharmony_ci desc->fCount = colorCount; 689cb93a386Sopenharmony_ci desc->fTileMode = mode; 690cb93a386Sopenharmony_ci desc->fGradFlags = flags; 691cb93a386Sopenharmony_ci desc->fLocalMatrix = localMatrix; 692cb93a386Sopenharmony_ci} 693cb93a386Sopenharmony_ci 694cb93a386Sopenharmony_cistatic SkColor4f average_gradient_color(const SkColor4f colors[], const SkScalar pos[], 695cb93a386Sopenharmony_ci int colorCount) { 696cb93a386Sopenharmony_ci // The gradient is a piecewise linear interpolation between colors. For a given interval, 697cb93a386Sopenharmony_ci // the integral between the two endpoints is 0.5 * (ci + cj) * (pj - pi), which provides that 698cb93a386Sopenharmony_ci // intervals average color. The overall average color is thus the sum of each piece. The thing 699cb93a386Sopenharmony_ci // to keep in mind is that the provided gradient definition may implicitly use p=0 and p=1. 700cb93a386Sopenharmony_ci Sk4f blend(0.0f); 701cb93a386Sopenharmony_ci for (int i = 0; i < colorCount - 1; ++i) { 702cb93a386Sopenharmony_ci // Calculate the average color for the interval between pos(i) and pos(i+1) 703cb93a386Sopenharmony_ci Sk4f c0 = Sk4f::Load(&colors[i]); 704cb93a386Sopenharmony_ci Sk4f c1 = Sk4f::Load(&colors[i + 1]); 705cb93a386Sopenharmony_ci 706cb93a386Sopenharmony_ci // when pos == null, there are colorCount uniformly distributed stops, going from 0 to 1, 707cb93a386Sopenharmony_ci // so pos[i + 1] - pos[i] = 1/(colorCount-1) 708cb93a386Sopenharmony_ci SkScalar w; 709cb93a386Sopenharmony_ci if (pos) { 710cb93a386Sopenharmony_ci // Match position fixing in SkGradientShader's constructor, clamping positions outside 711cb93a386Sopenharmony_ci // [0, 1] and forcing the sequence to be monotonic 712cb93a386Sopenharmony_ci SkScalar p0 = SkTPin(pos[i], 0.f, 1.f); 713cb93a386Sopenharmony_ci SkScalar p1 = SkTPin(pos[i + 1], p0, 1.f); 714cb93a386Sopenharmony_ci w = p1 - p0; 715cb93a386Sopenharmony_ci 716cb93a386Sopenharmony_ci // And account for any implicit intervals at the start or end of the positions 717cb93a386Sopenharmony_ci if (i == 0) { 718cb93a386Sopenharmony_ci if (p0 > 0.0f) { 719cb93a386Sopenharmony_ci // The first color is fixed between p = 0 to pos[0], so 0.5*(ci + cj)*(pj - pi) 720cb93a386Sopenharmony_ci // becomes 0.5*(c + c)*(pj - 0) = c * pj 721cb93a386Sopenharmony_ci Sk4f c = Sk4f::Load(&colors[0]); 722cb93a386Sopenharmony_ci blend += p0 * c; 723cb93a386Sopenharmony_ci } 724cb93a386Sopenharmony_ci } 725cb93a386Sopenharmony_ci if (i == colorCount - 2) { 726cb93a386Sopenharmony_ci if (p1 < 1.f) { 727cb93a386Sopenharmony_ci // The last color is fixed between pos[n-1] to p = 1, so 0.5*(ci + cj)*(pj - pi) 728cb93a386Sopenharmony_ci // becomes 0.5*(c + c)*(1 - pi) = c * (1 - pi) 729cb93a386Sopenharmony_ci Sk4f c = Sk4f::Load(&colors[colorCount - 1]); 730cb93a386Sopenharmony_ci blend += (1.f - p1) * c; 731cb93a386Sopenharmony_ci } 732cb93a386Sopenharmony_ci } 733cb93a386Sopenharmony_ci } else { 734cb93a386Sopenharmony_ci w = 1.f / (colorCount - 1); 735cb93a386Sopenharmony_ci } 736cb93a386Sopenharmony_ci 737cb93a386Sopenharmony_ci blend += 0.5f * w * (c1 + c0); 738cb93a386Sopenharmony_ci } 739cb93a386Sopenharmony_ci 740cb93a386Sopenharmony_ci SkColor4f avg; 741cb93a386Sopenharmony_ci blend.store(&avg); 742cb93a386Sopenharmony_ci return avg; 743cb93a386Sopenharmony_ci} 744cb93a386Sopenharmony_ci 745cb93a386Sopenharmony_ci// The default SkScalarNearlyZero threshold of .0024 is too big and causes regressions for svg 746cb93a386Sopenharmony_ci// gradients defined in the wild. 747cb93a386Sopenharmony_cistatic constexpr SkScalar kDegenerateThreshold = SK_Scalar1 / (1 << 15); 748cb93a386Sopenharmony_ci 749cb93a386Sopenharmony_ci// Except for special circumstances of clamped gradients, every gradient shape--when degenerate-- 750cb93a386Sopenharmony_ci// can be mapped to the same fallbacks. The specific shape factories must account for special 751cb93a386Sopenharmony_ci// clamped conditions separately, this will always return the last color for clamped gradients. 752cb93a386Sopenharmony_cistatic sk_sp<SkShader> make_degenerate_gradient(const SkColor4f colors[], const SkScalar pos[], 753cb93a386Sopenharmony_ci int colorCount, sk_sp<SkColorSpace> colorSpace, 754cb93a386Sopenharmony_ci SkTileMode mode) { 755cb93a386Sopenharmony_ci switch(mode) { 756cb93a386Sopenharmony_ci case SkTileMode::kDecal: 757cb93a386Sopenharmony_ci // normally this would reject the area outside of the interpolation region, so since 758cb93a386Sopenharmony_ci // inside region is empty when the radii are equal, the entire draw region is empty 759cb93a386Sopenharmony_ci return SkShaders::Empty(); 760cb93a386Sopenharmony_ci case SkTileMode::kRepeat: 761cb93a386Sopenharmony_ci case SkTileMode::kMirror: 762cb93a386Sopenharmony_ci // repeat and mirror are treated the same: the border colors are never visible, 763cb93a386Sopenharmony_ci // but approximate the final color as infinite repetitions of the colors, so 764cb93a386Sopenharmony_ci // it can be represented as the average color of the gradient. 765cb93a386Sopenharmony_ci return SkShaders::Color( 766cb93a386Sopenharmony_ci average_gradient_color(colors, pos, colorCount), std::move(colorSpace)); 767cb93a386Sopenharmony_ci case SkTileMode::kClamp: 768cb93a386Sopenharmony_ci // Depending on how the gradient shape degenerates, there may be a more specialized 769cb93a386Sopenharmony_ci // fallback representation for the factories to use, but this is a reasonable default. 770cb93a386Sopenharmony_ci return SkShaders::Color(colors[colorCount - 1], std::move(colorSpace)); 771cb93a386Sopenharmony_ci } 772cb93a386Sopenharmony_ci SkDEBUGFAIL("Should not be reached"); 773cb93a386Sopenharmony_ci return nullptr; 774cb93a386Sopenharmony_ci} 775cb93a386Sopenharmony_ci 776cb93a386Sopenharmony_ci// assumes colors is SkColor4f* and pos is SkScalar* 777cb93a386Sopenharmony_ci#define EXPAND_1_COLOR(count) \ 778cb93a386Sopenharmony_ci SkColor4f tmp[2]; \ 779cb93a386Sopenharmony_ci do { \ 780cb93a386Sopenharmony_ci if (1 == count) { \ 781cb93a386Sopenharmony_ci tmp[0] = tmp[1] = colors[0]; \ 782cb93a386Sopenharmony_ci colors = tmp; \ 783cb93a386Sopenharmony_ci pos = nullptr; \ 784cb93a386Sopenharmony_ci count = 2; \ 785cb93a386Sopenharmony_ci } \ 786cb93a386Sopenharmony_ci } while (0) 787cb93a386Sopenharmony_ci 788cb93a386Sopenharmony_cistruct ColorStopOptimizer { 789cb93a386Sopenharmony_ci ColorStopOptimizer(const SkColor4f* colors, const SkScalar* pos, int count, SkTileMode mode) 790cb93a386Sopenharmony_ci : fColors(colors) 791cb93a386Sopenharmony_ci , fPos(pos) 792cb93a386Sopenharmony_ci , fCount(count) { 793cb93a386Sopenharmony_ci 794cb93a386Sopenharmony_ci if (!pos || count != 3) { 795cb93a386Sopenharmony_ci return; 796cb93a386Sopenharmony_ci } 797cb93a386Sopenharmony_ci 798cb93a386Sopenharmony_ci if (SkScalarNearlyEqual(pos[0], 0.0f) && 799cb93a386Sopenharmony_ci SkScalarNearlyEqual(pos[1], 0.0f) && 800cb93a386Sopenharmony_ci SkScalarNearlyEqual(pos[2], 1.0f)) { 801cb93a386Sopenharmony_ci 802cb93a386Sopenharmony_ci if (SkTileMode::kRepeat == mode || SkTileMode::kMirror == mode || 803cb93a386Sopenharmony_ci colors[0] == colors[1]) { 804cb93a386Sopenharmony_ci 805cb93a386Sopenharmony_ci // Ignore the leftmost color/pos. 806cb93a386Sopenharmony_ci fColors += 1; 807cb93a386Sopenharmony_ci fPos += 1; 808cb93a386Sopenharmony_ci fCount = 2; 809cb93a386Sopenharmony_ci } 810cb93a386Sopenharmony_ci } else if (SkScalarNearlyEqual(pos[0], 0.0f) && 811cb93a386Sopenharmony_ci SkScalarNearlyEqual(pos[1], 1.0f) && 812cb93a386Sopenharmony_ci SkScalarNearlyEqual(pos[2], 1.0f)) { 813cb93a386Sopenharmony_ci 814cb93a386Sopenharmony_ci if (SkTileMode::kRepeat == mode || SkTileMode::kMirror == mode || 815cb93a386Sopenharmony_ci colors[1] == colors[2]) { 816cb93a386Sopenharmony_ci 817cb93a386Sopenharmony_ci // Ignore the rightmost color/pos. 818cb93a386Sopenharmony_ci fCount = 2; 819cb93a386Sopenharmony_ci } 820cb93a386Sopenharmony_ci } 821cb93a386Sopenharmony_ci } 822cb93a386Sopenharmony_ci 823cb93a386Sopenharmony_ci const SkColor4f* fColors; 824cb93a386Sopenharmony_ci const SkScalar* fPos; 825cb93a386Sopenharmony_ci int fCount; 826cb93a386Sopenharmony_ci}; 827cb93a386Sopenharmony_ci 828cb93a386Sopenharmony_cistruct ColorConverter { 829cb93a386Sopenharmony_ci ColorConverter(const SkColor* colors, int count) { 830cb93a386Sopenharmony_ci const float ONE_OVER_255 = 1.f / 255; 831cb93a386Sopenharmony_ci for (int i = 0; i < count; ++i) { 832cb93a386Sopenharmony_ci fColors4f.push_back({ 833cb93a386Sopenharmony_ci SkColorGetR(colors[i]) * ONE_OVER_255, 834cb93a386Sopenharmony_ci SkColorGetG(colors[i]) * ONE_OVER_255, 835cb93a386Sopenharmony_ci SkColorGetB(colors[i]) * ONE_OVER_255, 836cb93a386Sopenharmony_ci SkColorGetA(colors[i]) * ONE_OVER_255 }); 837cb93a386Sopenharmony_ci } 838cb93a386Sopenharmony_ci } 839cb93a386Sopenharmony_ci 840cb93a386Sopenharmony_ci SkSTArray<2, SkColor4f, true> fColors4f; 841cb93a386Sopenharmony_ci}; 842cb93a386Sopenharmony_ci 843cb93a386Sopenharmony_cisk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2], 844cb93a386Sopenharmony_ci const SkColor colors[], 845cb93a386Sopenharmony_ci const SkScalar pos[], int colorCount, 846cb93a386Sopenharmony_ci SkTileMode mode, 847cb93a386Sopenharmony_ci uint32_t flags, 848cb93a386Sopenharmony_ci const SkMatrix* localMatrix) { 849cb93a386Sopenharmony_ci ColorConverter converter(colors, colorCount); 850cb93a386Sopenharmony_ci return MakeLinear(pts, converter.fColors4f.begin(), nullptr, pos, colorCount, mode, flags, 851cb93a386Sopenharmony_ci localMatrix); 852cb93a386Sopenharmony_ci} 853cb93a386Sopenharmony_ci 854cb93a386Sopenharmony_cisk_sp<SkShader> SkGradientShader::MakeLinear(const SkPoint pts[2], 855cb93a386Sopenharmony_ci const SkColor4f colors[], 856cb93a386Sopenharmony_ci sk_sp<SkColorSpace> colorSpace, 857cb93a386Sopenharmony_ci const SkScalar pos[], int colorCount, 858cb93a386Sopenharmony_ci SkTileMode mode, 859cb93a386Sopenharmony_ci uint32_t flags, 860cb93a386Sopenharmony_ci const SkMatrix* localMatrix) { 861cb93a386Sopenharmony_ci if (!pts || !SkScalarIsFinite((pts[1] - pts[0]).length())) { 862cb93a386Sopenharmony_ci return nullptr; 863cb93a386Sopenharmony_ci } 864cb93a386Sopenharmony_ci if (!valid_grad(colors, pos, colorCount, mode)) { 865cb93a386Sopenharmony_ci return nullptr; 866cb93a386Sopenharmony_ci } 867cb93a386Sopenharmony_ci if (1 == colorCount) { 868cb93a386Sopenharmony_ci return SkShaders::Color(colors[0], std::move(colorSpace)); 869cb93a386Sopenharmony_ci } 870cb93a386Sopenharmony_ci if (localMatrix && !localMatrix->invert(nullptr)) { 871cb93a386Sopenharmony_ci return nullptr; 872cb93a386Sopenharmony_ci } 873cb93a386Sopenharmony_ci 874cb93a386Sopenharmony_ci if (SkScalarNearlyZero((pts[1] - pts[0]).length(), kDegenerateThreshold)) { 875cb93a386Sopenharmony_ci // Degenerate gradient, the only tricky complication is when in clamp mode, the limit of 876cb93a386Sopenharmony_ci // the gradient approaches two half planes of solid color (first and last). However, they 877cb93a386Sopenharmony_ci // are divided by the line perpendicular to the start and end point, which becomes undefined 878cb93a386Sopenharmony_ci // once start and end are exactly the same, so just use the end color for a stable solution. 879cb93a386Sopenharmony_ci return make_degenerate_gradient(colors, pos, colorCount, std::move(colorSpace), mode); 880cb93a386Sopenharmony_ci } 881cb93a386Sopenharmony_ci 882cb93a386Sopenharmony_ci ColorStopOptimizer opt(colors, pos, colorCount, mode); 883cb93a386Sopenharmony_ci 884cb93a386Sopenharmony_ci SkGradientShaderBase::Descriptor desc; 885cb93a386Sopenharmony_ci desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags, 886cb93a386Sopenharmony_ci localMatrix); 887cb93a386Sopenharmony_ci return sk_make_sp<SkLinearGradient>(pts, desc); 888cb93a386Sopenharmony_ci} 889cb93a386Sopenharmony_ci 890cb93a386Sopenharmony_cisk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius, 891cb93a386Sopenharmony_ci const SkColor colors[], 892cb93a386Sopenharmony_ci const SkScalar pos[], int colorCount, 893cb93a386Sopenharmony_ci SkTileMode mode, 894cb93a386Sopenharmony_ci uint32_t flags, 895cb93a386Sopenharmony_ci const SkMatrix* localMatrix) { 896cb93a386Sopenharmony_ci ColorConverter converter(colors, colorCount); 897cb93a386Sopenharmony_ci return MakeRadial(center, radius, converter.fColors4f.begin(), nullptr, pos, colorCount, mode, 898cb93a386Sopenharmony_ci flags, localMatrix); 899cb93a386Sopenharmony_ci} 900cb93a386Sopenharmony_ci 901cb93a386Sopenharmony_cisk_sp<SkShader> SkGradientShader::MakeRadial(const SkPoint& center, SkScalar radius, 902cb93a386Sopenharmony_ci const SkColor4f colors[], 903cb93a386Sopenharmony_ci sk_sp<SkColorSpace> colorSpace, 904cb93a386Sopenharmony_ci const SkScalar pos[], int colorCount, 905cb93a386Sopenharmony_ci SkTileMode mode, 906cb93a386Sopenharmony_ci uint32_t flags, 907cb93a386Sopenharmony_ci const SkMatrix* localMatrix) { 908cb93a386Sopenharmony_ci if (radius < 0) { 909cb93a386Sopenharmony_ci return nullptr; 910cb93a386Sopenharmony_ci } 911cb93a386Sopenharmony_ci if (!valid_grad(colors, pos, colorCount, mode)) { 912cb93a386Sopenharmony_ci return nullptr; 913cb93a386Sopenharmony_ci } 914cb93a386Sopenharmony_ci if (1 == colorCount) { 915cb93a386Sopenharmony_ci return SkShaders::Color(colors[0], std::move(colorSpace)); 916cb93a386Sopenharmony_ci } 917cb93a386Sopenharmony_ci if (localMatrix && !localMatrix->invert(nullptr)) { 918cb93a386Sopenharmony_ci return nullptr; 919cb93a386Sopenharmony_ci } 920cb93a386Sopenharmony_ci 921cb93a386Sopenharmony_ci if (SkScalarNearlyZero(radius, kDegenerateThreshold)) { 922cb93a386Sopenharmony_ci // Degenerate gradient optimization, and no special logic needed for clamped radial gradient 923cb93a386Sopenharmony_ci return make_degenerate_gradient(colors, pos, colorCount, std::move(colorSpace), mode); 924cb93a386Sopenharmony_ci } 925cb93a386Sopenharmony_ci 926cb93a386Sopenharmony_ci ColorStopOptimizer opt(colors, pos, colorCount, mode); 927cb93a386Sopenharmony_ci 928cb93a386Sopenharmony_ci SkGradientShaderBase::Descriptor desc; 929cb93a386Sopenharmony_ci desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags, 930cb93a386Sopenharmony_ci localMatrix); 931cb93a386Sopenharmony_ci return sk_make_sp<SkRadialGradient>(center, radius, desc); 932cb93a386Sopenharmony_ci} 933cb93a386Sopenharmony_ci 934cb93a386Sopenharmony_cisk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start, 935cb93a386Sopenharmony_ci SkScalar startRadius, 936cb93a386Sopenharmony_ci const SkPoint& end, 937cb93a386Sopenharmony_ci SkScalar endRadius, 938cb93a386Sopenharmony_ci const SkColor colors[], 939cb93a386Sopenharmony_ci const SkScalar pos[], 940cb93a386Sopenharmony_ci int colorCount, 941cb93a386Sopenharmony_ci SkTileMode mode, 942cb93a386Sopenharmony_ci uint32_t flags, 943cb93a386Sopenharmony_ci const SkMatrix* localMatrix) { 944cb93a386Sopenharmony_ci ColorConverter converter(colors, colorCount); 945cb93a386Sopenharmony_ci return MakeTwoPointConical(start, startRadius, end, endRadius, converter.fColors4f.begin(), 946cb93a386Sopenharmony_ci nullptr, pos, colorCount, mode, flags, localMatrix); 947cb93a386Sopenharmony_ci} 948cb93a386Sopenharmony_ci 949cb93a386Sopenharmony_cisk_sp<SkShader> SkGradientShader::MakeTwoPointConical(const SkPoint& start, 950cb93a386Sopenharmony_ci SkScalar startRadius, 951cb93a386Sopenharmony_ci const SkPoint& end, 952cb93a386Sopenharmony_ci SkScalar endRadius, 953cb93a386Sopenharmony_ci const SkColor4f colors[], 954cb93a386Sopenharmony_ci sk_sp<SkColorSpace> colorSpace, 955cb93a386Sopenharmony_ci const SkScalar pos[], 956cb93a386Sopenharmony_ci int colorCount, 957cb93a386Sopenharmony_ci SkTileMode mode, 958cb93a386Sopenharmony_ci uint32_t flags, 959cb93a386Sopenharmony_ci const SkMatrix* localMatrix) { 960cb93a386Sopenharmony_ci if (startRadius < 0 || endRadius < 0) { 961cb93a386Sopenharmony_ci return nullptr; 962cb93a386Sopenharmony_ci } 963cb93a386Sopenharmony_ci if (!valid_grad(colors, pos, colorCount, mode)) { 964cb93a386Sopenharmony_ci return nullptr; 965cb93a386Sopenharmony_ci } 966cb93a386Sopenharmony_ci if (SkScalarNearlyZero((start - end).length(), kDegenerateThreshold)) { 967cb93a386Sopenharmony_ci // If the center positions are the same, then the gradient is the radial variant of a 2 pt 968cb93a386Sopenharmony_ci // conical gradient, an actual radial gradient (startRadius == 0), or it is fully degenerate 969cb93a386Sopenharmony_ci // (startRadius == endRadius). 970cb93a386Sopenharmony_ci if (SkScalarNearlyEqual(startRadius, endRadius, kDegenerateThreshold)) { 971cb93a386Sopenharmony_ci // Degenerate case, where the interpolation region area approaches zero. The proper 972cb93a386Sopenharmony_ci // behavior depends on the tile mode, which is consistent with the default degenerate 973cb93a386Sopenharmony_ci // gradient behavior, except when mode = clamp and the radii > 0. 974cb93a386Sopenharmony_ci if (mode == SkTileMode::kClamp && endRadius > kDegenerateThreshold) { 975cb93a386Sopenharmony_ci // The interpolation region becomes an infinitely thin ring at the radius, so the 976cb93a386Sopenharmony_ci // final gradient will be the first color repeated from p=0 to 1, and then a hard 977cb93a386Sopenharmony_ci // stop switching to the last color at p=1. 978cb93a386Sopenharmony_ci static constexpr SkScalar circlePos[3] = {0, 1, 1}; 979cb93a386Sopenharmony_ci SkColor4f reColors[3] = {colors[0], colors[0], colors[colorCount - 1]}; 980cb93a386Sopenharmony_ci return MakeRadial(start, endRadius, reColors, std::move(colorSpace), 981cb93a386Sopenharmony_ci circlePos, 3, mode, flags, localMatrix); 982cb93a386Sopenharmony_ci } else { 983cb93a386Sopenharmony_ci // Otherwise use the default degenerate case 984cb93a386Sopenharmony_ci return make_degenerate_gradient( 985cb93a386Sopenharmony_ci colors, pos, colorCount, std::move(colorSpace), mode); 986cb93a386Sopenharmony_ci } 987cb93a386Sopenharmony_ci } else if (SkScalarNearlyZero(startRadius, kDegenerateThreshold)) { 988cb93a386Sopenharmony_ci // We can treat this gradient as radial, which is faster. If we got here, we know 989cb93a386Sopenharmony_ci // that endRadius is not equal to 0, so this produces a meaningful gradient 990cb93a386Sopenharmony_ci return MakeRadial(start, endRadius, colors, std::move(colorSpace), pos, colorCount, 991cb93a386Sopenharmony_ci mode, flags, localMatrix); 992cb93a386Sopenharmony_ci } 993cb93a386Sopenharmony_ci // Else it's the 2pt conical radial variant with no degenerate radii, so fall through to the 994cb93a386Sopenharmony_ci // regular 2pt constructor. 995cb93a386Sopenharmony_ci } 996cb93a386Sopenharmony_ci 997cb93a386Sopenharmony_ci if (localMatrix && !localMatrix->invert(nullptr)) { 998cb93a386Sopenharmony_ci return nullptr; 999cb93a386Sopenharmony_ci } 1000cb93a386Sopenharmony_ci EXPAND_1_COLOR(colorCount); 1001cb93a386Sopenharmony_ci 1002cb93a386Sopenharmony_ci ColorStopOptimizer opt(colors, pos, colorCount, mode); 1003cb93a386Sopenharmony_ci 1004cb93a386Sopenharmony_ci SkGradientShaderBase::Descriptor desc; 1005cb93a386Sopenharmony_ci desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags, 1006cb93a386Sopenharmony_ci localMatrix); 1007cb93a386Sopenharmony_ci return SkTwoPointConicalGradient::Create(start, startRadius, end, endRadius, desc); 1008cb93a386Sopenharmony_ci} 1009cb93a386Sopenharmony_ci 1010cb93a386Sopenharmony_cisk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy, 1011cb93a386Sopenharmony_ci const SkColor colors[], 1012cb93a386Sopenharmony_ci const SkScalar pos[], 1013cb93a386Sopenharmony_ci int colorCount, 1014cb93a386Sopenharmony_ci SkTileMode mode, 1015cb93a386Sopenharmony_ci SkScalar startAngle, 1016cb93a386Sopenharmony_ci SkScalar endAngle, 1017cb93a386Sopenharmony_ci uint32_t flags, 1018cb93a386Sopenharmony_ci const SkMatrix* localMatrix) { 1019cb93a386Sopenharmony_ci ColorConverter converter(colors, colorCount); 1020cb93a386Sopenharmony_ci return MakeSweep(cx, cy, converter.fColors4f.begin(), nullptr, pos, colorCount, 1021cb93a386Sopenharmony_ci mode, startAngle, endAngle, flags, localMatrix); 1022cb93a386Sopenharmony_ci} 1023cb93a386Sopenharmony_ci 1024cb93a386Sopenharmony_cisk_sp<SkShader> SkGradientShader::MakeSweep(SkScalar cx, SkScalar cy, 1025cb93a386Sopenharmony_ci const SkColor4f colors[], 1026cb93a386Sopenharmony_ci sk_sp<SkColorSpace> colorSpace, 1027cb93a386Sopenharmony_ci const SkScalar pos[], 1028cb93a386Sopenharmony_ci int colorCount, 1029cb93a386Sopenharmony_ci SkTileMode mode, 1030cb93a386Sopenharmony_ci SkScalar startAngle, 1031cb93a386Sopenharmony_ci SkScalar endAngle, 1032cb93a386Sopenharmony_ci uint32_t flags, 1033cb93a386Sopenharmony_ci const SkMatrix* localMatrix) { 1034cb93a386Sopenharmony_ci if (!valid_grad(colors, pos, colorCount, mode)) { 1035cb93a386Sopenharmony_ci return nullptr; 1036cb93a386Sopenharmony_ci } 1037cb93a386Sopenharmony_ci if (1 == colorCount) { 1038cb93a386Sopenharmony_ci return SkShaders::Color(colors[0], std::move(colorSpace)); 1039cb93a386Sopenharmony_ci } 1040cb93a386Sopenharmony_ci if (!SkScalarIsFinite(startAngle) || !SkScalarIsFinite(endAngle) || startAngle > endAngle) { 1041cb93a386Sopenharmony_ci return nullptr; 1042cb93a386Sopenharmony_ci } 1043cb93a386Sopenharmony_ci if (localMatrix && !localMatrix->invert(nullptr)) { 1044cb93a386Sopenharmony_ci return nullptr; 1045cb93a386Sopenharmony_ci } 1046cb93a386Sopenharmony_ci 1047cb93a386Sopenharmony_ci if (SkScalarNearlyEqual(startAngle, endAngle, kDegenerateThreshold)) { 1048cb93a386Sopenharmony_ci // Degenerate gradient, which should follow default degenerate behavior unless it is 1049cb93a386Sopenharmony_ci // clamped and the angle is greater than 0. 1050cb93a386Sopenharmony_ci if (mode == SkTileMode::kClamp && endAngle > kDegenerateThreshold) { 1051cb93a386Sopenharmony_ci // In this case, the first color is repeated from 0 to the angle, then a hardstop 1052cb93a386Sopenharmony_ci // switches to the last color (all other colors are compressed to the infinitely thin 1053cb93a386Sopenharmony_ci // interpolation region). 1054cb93a386Sopenharmony_ci static constexpr SkScalar clampPos[3] = {0, 1, 1}; 1055cb93a386Sopenharmony_ci SkColor4f reColors[3] = {colors[0], colors[0], colors[colorCount - 1]}; 1056cb93a386Sopenharmony_ci return MakeSweep(cx, cy, reColors, std::move(colorSpace), clampPos, 3, mode, 0, 1057cb93a386Sopenharmony_ci endAngle, flags, localMatrix); 1058cb93a386Sopenharmony_ci } else { 1059cb93a386Sopenharmony_ci return make_degenerate_gradient(colors, pos, colorCount, std::move(colorSpace), mode); 1060cb93a386Sopenharmony_ci } 1061cb93a386Sopenharmony_ci } 1062cb93a386Sopenharmony_ci 1063cb93a386Sopenharmony_ci if (startAngle <= 0 && endAngle >= 360) { 1064cb93a386Sopenharmony_ci // If the t-range includes [0,1], then we can always use clamping (presumably faster). 1065cb93a386Sopenharmony_ci mode = SkTileMode::kClamp; 1066cb93a386Sopenharmony_ci } 1067cb93a386Sopenharmony_ci 1068cb93a386Sopenharmony_ci ColorStopOptimizer opt(colors, pos, colorCount, mode); 1069cb93a386Sopenharmony_ci 1070cb93a386Sopenharmony_ci SkGradientShaderBase::Descriptor desc; 1071cb93a386Sopenharmony_ci desc_init(&desc, opt.fColors, std::move(colorSpace), opt.fPos, opt.fCount, mode, flags, 1072cb93a386Sopenharmony_ci localMatrix); 1073cb93a386Sopenharmony_ci 1074cb93a386Sopenharmony_ci const SkScalar t0 = startAngle / 360, 1075cb93a386Sopenharmony_ci t1 = endAngle / 360; 1076cb93a386Sopenharmony_ci 1077cb93a386Sopenharmony_ci return sk_make_sp<SkSweepGradient>(SkPoint::Make(cx, cy), t0, t1, desc); 1078cb93a386Sopenharmony_ci} 1079cb93a386Sopenharmony_ci 1080cb93a386Sopenharmony_civoid SkGradientShader::RegisterFlattenables() { 1081cb93a386Sopenharmony_ci SK_REGISTER_FLATTENABLE(SkLinearGradient); 1082cb93a386Sopenharmony_ci SK_REGISTER_FLATTENABLE(SkRadialGradient); 1083cb93a386Sopenharmony_ci SK_REGISTER_FLATTENABLE(SkSweepGradient); 1084cb93a386Sopenharmony_ci SK_REGISTER_FLATTENABLE(SkTwoPointConicalGradient); 1085cb93a386Sopenharmony_ci} 1086