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