1/* 2 * Copyright 2012 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "SkTwoPointConicalGradient.h" 9 10#include "SkRasterPipeline.h" 11#include "SkReadBuffer.h" 12#include "SkWriteBuffer.h" 13#include "../../jumper/SkJumper.h" 14 15// Please see https://skia.org/dev/design/conical for how our shader works. 16 17void SkTwoPointConicalGradient::FocalData::set(SkScalar r0, SkScalar r1, SkMatrix& matrix) { 18 fIsSwapped = false; 19 fFocalX = r0 / (r0 - r1); 20 if (SkScalarNearlyZero(fFocalX - 1)) { 21 // swap r0, r1 22 matrix.postTranslate(-1, 0); 23 matrix.postScale(-1, 1); 24 std::swap(r0, r1); 25 fFocalX = 0; // because r0 is now 0 26 fIsSwapped = true; 27 } 28 29 // Map {focal point, (1, 0)} to {(0, 0), (1, 0)} 30 const SkPoint from[2] = { {fFocalX, 0}, {1, 0} }; 31 const SkPoint to[2] = { {0, 0}, {1, 0} }; 32 SkMatrix focalMatrix; 33 if (!focalMatrix.setPolyToPoly(from, to, 2)) { 34 SkDEBUGFAILF("Mapping focal point failed unexpectedly for focalX = %f.\n", fFocalX); 35 // We won't be able to draw the gradient; at least make sure that we initialize the 36 // memory to prevent security issues. 37 focalMatrix = SkMatrix::MakeScale(1, 1); 38 } 39 matrix.postConcat(focalMatrix); 40 fR1 = r1 / SkScalarAbs(1 - fFocalX); // focalMatrix has a scale of 1/(1-f) 41 42 // The following transformations are just to accelerate the shader computation by saving 43 // some arithmatic operations. 44 if (this->isFocalOnCircle()) { 45 matrix.postScale(0.5, 0.5); 46 } else { 47 matrix.postScale(fR1 / (fR1 * fR1 - 1), 1 / sqrt(SkScalarAbs(fR1 * fR1 - 1))); 48 } 49 matrix.postScale(SkScalarAbs(1 - fFocalX), SkScalarAbs(1 - fFocalX)); // scale |1 - f| 50} 51 52sk_sp<SkShader> SkTwoPointConicalGradient::Create(const SkPoint& c0, SkScalar r0, 53 const SkPoint& c1, SkScalar r1, 54 const Descriptor& desc) { 55 SkMatrix gradientMatrix; 56 Type gradientType; 57 58 if (SkScalarNearlyZero((c0 - c1).length())) { 59 // Concentric case: we can pretend we're radial (with a tiny twist). 60 const SkScalar scale = 1.0f / SkTMax(r0, r1); 61 gradientMatrix = SkMatrix::MakeTrans(-c1.x(), -c1.y()); 62 gradientMatrix.postScale(scale, scale); 63 64 gradientType = Type::kRadial; 65 } else { 66 const SkPoint centers[2] = { c0 , c1 }; 67 const SkPoint unitvec[2] = { {0, 0}, {1, 0} }; 68 69 if (!gradientMatrix.setPolyToPoly(centers, unitvec, 2)) { 70 // Degenerate case. 71 return nullptr; 72 } 73 74 gradientType = SkScalarNearlyZero(r1 - r0) ? Type::kStrip : Type::kFocal; 75 } 76 77 FocalData focalData; 78 if (gradientType == Type::kFocal) { 79 const auto dCenter = (c0 - c1).length(); 80 focalData.set(r0 / dCenter, r1 / dCenter, gradientMatrix); // this may change gradientMatrix 81 } 82 return sk_sp<SkShader>(new SkTwoPointConicalGradient(c0, r0, c1, r1, desc, 83 gradientType, gradientMatrix, focalData)); 84} 85 86SkTwoPointConicalGradient::SkTwoPointConicalGradient( 87 const SkPoint& start, SkScalar startRadius, 88 const SkPoint& end, SkScalar endRadius, 89 const Descriptor& desc, Type type, const SkMatrix& gradientMatrix, const FocalData& data) 90 : SkGradientShaderBase(desc, gradientMatrix) 91 , fCenter1(start) 92 , fCenter2(end) 93 , fRadius1(startRadius) 94 , fRadius2(endRadius) 95 , fType(type) 96{ 97 // this is degenerate, and should be caught by our caller 98 SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2); 99 if (type == Type::kFocal) { 100 fFocalData = data; 101 } 102} 103 104bool SkTwoPointConicalGradient::isOpaque() const { 105 // Because areas outside the cone are left untouched, we cannot treat the 106 // shader as opaque even if the gradient itself is opaque. 107 // TODO(junov): Compute whether the cone fills the plane crbug.com/222380 108 return false; 109} 110 111// Returns the original non-sorted version of the gradient 112SkShader::GradientType SkTwoPointConicalGradient::asAGradient(GradientInfo* info) const { 113 if (info) { 114 commonAsAGradient(info); 115 info->fPoint[0] = fCenter1; 116 info->fPoint[1] = fCenter2; 117 info->fRadius[0] = fRadius1; 118 info->fRadius[1] = fRadius2; 119 } 120 return kConical_GradientType; 121} 122 123sk_sp<SkFlattenable> SkTwoPointConicalGradient::CreateProc(SkReadBuffer& buffer) { 124 DescriptorScope desc; 125 if (!desc.unflatten(buffer)) { 126 return nullptr; 127 } 128 SkPoint c1 = buffer.readPoint(); 129 SkPoint c2 = buffer.readPoint(); 130 SkScalar r1 = buffer.readScalar(); 131 SkScalar r2 = buffer.readScalar(); 132 133 if (buffer.isVersionLT(SkReadBuffer::k2PtConicalNoFlip_Version) && buffer.readBool()) { 134 // legacy flipped gradient 135 SkTSwap(c1, c2); 136 SkTSwap(r1, r2); 137 138 SkColor4f* colors = desc.mutableColors(); 139 SkScalar* pos = desc.mutablePos(); 140 const int last = desc.fCount - 1; 141 const int half = desc.fCount >> 1; 142 for (int i = 0; i < half; ++i) { 143 SkTSwap(colors[i], colors[last - i]); 144 if (pos) { 145 SkScalar tmp = pos[i]; 146 pos[i] = SK_Scalar1 - pos[last - i]; 147 pos[last - i] = SK_Scalar1 - tmp; 148 } 149 } 150 if (pos) { 151 if (desc.fCount & 1) { 152 pos[half] = SK_Scalar1 - pos[half]; 153 } 154 } 155 } 156 157 return SkGradientShader::MakeTwoPointConical(c1, r1, c2, r2, desc.fColors, 158 std::move(desc.fColorSpace), desc.fPos, 159 desc.fCount, desc.fTileMode, desc.fGradFlags, 160 desc.fLocalMatrix); 161} 162 163void SkTwoPointConicalGradient::flatten(SkWriteBuffer& buffer) const { 164 this->INHERITED::flatten(buffer); 165 buffer.writePoint(fCenter1); 166 buffer.writePoint(fCenter2); 167 buffer.writeScalar(fRadius1); 168 buffer.writeScalar(fRadius2); 169} 170 171#if SK_SUPPORT_GPU 172 173#include "SkGr.h" 174#include "SkTwoPointConicalGradient_gpu.h" 175 176std::unique_ptr<GrFragmentProcessor> SkTwoPointConicalGradient::asFragmentProcessor( 177 const GrFPArgs& args) const { 178 SkASSERT(args.fContext); 179 return Gr2PtConicalGradientEffect::Make( 180 GrGradientEffect::CreateArgs(args.fContext, this, args.fLocalMatrix, fTileMode, 181 args.fDstColorSpaceInfo->colorSpace())); 182} 183 184#endif 185 186sk_sp<SkShader> SkTwoPointConicalGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const { 187 const AutoXformColors xformedColors(*this, xformer); 188 return SkGradientShader::MakeTwoPointConical(fCenter1, fRadius1, fCenter2, fRadius2, 189 xformedColors.fColors.get(), fOrigPos, fColorCount, 190 fTileMode, fGradFlags, &this->getLocalMatrix()); 191} 192 193 194#ifndef SK_IGNORE_TO_STRING 195void SkTwoPointConicalGradient::toString(SkString* str) const { 196 str->append("SkTwoPointConicalGradient: ("); 197 198 str->append("center1: ("); 199 str->appendScalar(fCenter1.fX); 200 str->append(", "); 201 str->appendScalar(fCenter1.fY); 202 str->append(") radius1: "); 203 str->appendScalar(fRadius1); 204 str->append(" "); 205 206 str->append("center2: ("); 207 str->appendScalar(fCenter2.fX); 208 str->append(", "); 209 str->appendScalar(fCenter2.fY); 210 str->append(") radius2: "); 211 str->appendScalar(fRadius2); 212 str->append(" "); 213 214 this->INHERITED::toString(str); 215 216 str->append(")"); 217} 218#endif 219 220void SkTwoPointConicalGradient::appendGradientStages(SkArenaAlloc* alloc, SkRasterPipeline* p, 221 SkRasterPipeline* postPipeline) const { 222 const auto dRadius = fRadius2 - fRadius1; 223 224 if (fType == Type::kRadial) { 225 p->append(SkRasterPipeline::xy_to_radius); 226 227 // Tiny twist: radial computes a t for [0, r2], but we want a t for [r1, r2]. 228 auto scale = SkTMax(fRadius1, fRadius2) / dRadius; 229 auto bias = -fRadius1 / dRadius; 230 231 p->append_matrix(alloc, SkMatrix::Concat(SkMatrix::MakeTrans(bias, 0), 232 SkMatrix::MakeScale(scale, 1))); 233 return; 234 } 235 236 if (fType == Type::kStrip) { 237 auto* ctx = alloc->make<SkJumper_2PtConicalCtx>(); 238 SkScalar scaledR0 = fRadius1 / this->getCenterX1(); 239 ctx->fP0 = scaledR0 * scaledR0; 240 p->append(SkRasterPipeline::xy_to_2pt_conical_strip, ctx); 241 p->append(SkRasterPipeline::mask_2pt_conical_nan, ctx); 242 postPipeline->append(SkRasterPipeline::apply_vector_mask, &ctx->fMask); 243 return; 244 } 245 246 auto* ctx = alloc->make<SkJumper_2PtConicalCtx>(); 247 ctx->fP0 = 1/fFocalData.fR1; 248 ctx->fP1 = fFocalData.fFocalX; 249 250 if (fFocalData.isFocalOnCircle()) { 251 p->append(SkRasterPipeline::xy_to_2pt_conical_focal_on_circle); 252 } else if (fFocalData.isWellBehaved()) { 253 p->append(SkRasterPipeline::xy_to_2pt_conical_well_behaved, ctx); 254 } else if (fFocalData.isSwapped() || 1 - fFocalData.fFocalX < 0) { 255 p->append(SkRasterPipeline::xy_to_2pt_conical_smaller, ctx); 256 } else { 257 p->append(SkRasterPipeline::xy_to_2pt_conical_greater, ctx); 258 } 259 260 if (!fFocalData.isWellBehaved()) { 261 p->append(SkRasterPipeline::mask_2pt_conical_degenerates, ctx); 262 } 263 if (1 - fFocalData.fFocalX < 0) { 264 p->append(SkRasterPipeline::negate_x); 265 } 266 if (!fFocalData.isNativelyFocal()) { 267 p->append(SkRasterPipeline::alter_2pt_conical_compensate_focal, ctx); 268 } 269 if (fFocalData.isSwapped()) { 270 p->append(SkRasterPipeline::alter_2pt_conical_unswap); 271 } 272 if (!fFocalData.isWellBehaved()) { 273 postPipeline->append(SkRasterPipeline::apply_vector_mask, &ctx->fMask); 274 } 275} 276