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 "SkColorSpaceXformer.h" 9 #include "SkRadialGradient.h" 10 #include "SkNx.h" 11 12 namespace { 13 14 // GCC doesn't like using static functions as template arguments. So force these to be non-static. 15 inline SkFixed mirror_tileproc_nonstatic(SkFixed x) { 16 return mirror_tileproc(x); 17 } 18 19 inline SkFixed repeat_tileproc_nonstatic(SkFixed x) { 20 return repeat_tileproc(x); 21 } 22 23 SkMatrix rad_to_unit_matrix(const SkPoint& center, SkScalar radius) { 24 SkScalar inv = SkScalarInvert(radius); 25 26 SkMatrix matrix; 27 matrix.setTranslate(-center.fX, -center.fY); 28 matrix.postScale(inv, inv); 29 return matrix; 30 } 31 32 33 } // namespace 34 35 ///////////////////////////////////////////////////////////////////// 36 37 SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius, const Descriptor& desc) 38 : SkGradientShaderBase(desc, rad_to_unit_matrix(center, radius)) 39 , fCenter(center) 40 , fRadius(radius) { 41 } 42 43 SkShaderBase::Context* SkRadialGradient::onMakeContext( 44 const ContextRec& rec, SkArenaAlloc* alloc) const 45 { 46 return CheckedMakeContext<RadialGradientContext>(alloc, *this, rec); 47 } 48 49 SkRadialGradient::RadialGradientContext::RadialGradientContext( 50 const SkRadialGradient& shader, const ContextRec& rec) 51 : INHERITED(shader, rec) {} 52 53 SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const { 54 if (info) { 55 commonAsAGradient(info); 56 info->fPoint[0] = fCenter; 57 info->fRadius[0] = fRadius; 58 } 59 return kRadial_GradientType; 60 } 61 62 sk_sp<SkFlattenable> SkRadialGradient::CreateProc(SkReadBuffer& buffer) { 63 DescriptorScope desc; 64 if (!desc.unflatten(buffer)) { 65 return nullptr; 66 } 67 const SkPoint center = buffer.readPoint(); 68 const SkScalar radius = buffer.readScalar(); 69 return SkGradientShader::MakeRadial(center, radius, desc.fColors, std::move(desc.fColorSpace), 70 desc.fPos, desc.fCount, desc.fTileMode, desc.fGradFlags, 71 desc.fLocalMatrix); 72 } 73 74 void SkRadialGradient::flatten(SkWriteBuffer& buffer) const { 75 this->INHERITED::flatten(buffer); 76 buffer.writePoint(fCenter); 77 buffer.writeScalar(fRadius); 78 } 79 80 namespace { 81 82 inline bool radial_completely_pinned(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy) { 83 // fast, overly-conservative test: checks unit square instead of unit circle 84 bool xClamped = (fx >= 1 && dx >= 0) || (fx <= -1 && dx <= 0); 85 bool yClamped = (fy >= 1 && dy >= 0) || (fy <= -1 && dy <= 0); 86 return xClamped || yClamped; 87 } 88 89 typedef void (* RadialShadeProc)(SkScalar sfx, SkScalar sdx, 90 SkScalar sfy, SkScalar sdy, 91 SkPMColor* dstC, const SkPMColor* cache, 92 int count, int toggle); 93 94 static inline Sk4f fast_sqrt(const Sk4f& R) { 95 return R * R.rsqrt(); 96 } 97 98 static inline Sk4f sum_squares(const Sk4f& a, const Sk4f& b) { 99 return a * a + b * b; 100 } 101 102 void shadeSpan_radial_clamp2(SkScalar sfx, SkScalar sdx, SkScalar sfy, SkScalar sdy, 103 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 104 int count, int toggle) { 105 if (radial_completely_pinned(sfx, sdx, sfy, sdy)) { 106 unsigned fi = SkGradientShaderBase::kCache32Count - 1; 107 sk_memset32_dither(dstC, 108 cache[toggle + fi], 109 cache[next_dither_toggle(toggle) + fi], 110 count); 111 } else { 112 const Sk4f min(SK_ScalarNearlyZero); 113 const Sk4f max(255); 114 const float scale = 255; 115 sfx *= scale; 116 sfy *= scale; 117 sdx *= scale; 118 sdy *= scale; 119 const Sk4f fx4(sfx, sfx + sdx, sfx + 2*sdx, sfx + 3*sdx); 120 const Sk4f fy4(sfy, sfy + sdy, sfy + 2*sdy, sfy + 3*sdy); 121 const Sk4f dx4(sdx * 4); 122 const Sk4f dy4(sdy * 4); 123 124 Sk4f tmpxy = fx4 * dx4 + fy4 * dy4; 125 Sk4f tmpdxdy = sum_squares(dx4, dy4); 126 Sk4f R = Sk4f::Max(sum_squares(fx4, fy4), min); 127 Sk4f dR = tmpxy + tmpxy + tmpdxdy; 128 const Sk4f ddR = tmpdxdy + tmpdxdy; 129 130 for (int i = 0; i < (count >> 2); ++i) { 131 Sk4f dist = Sk4f::Min(fast_sqrt(R), max); 132 R = Sk4f::Max(R + dR, min); 133 dR = dR + ddR; 134 135 uint8_t fi[4]; 136 SkNx_cast<uint8_t>(dist).store(fi); 137 138 for (int i = 0; i < 4; i++) { 139 *dstC++ = cache[toggle + fi[i]]; 140 toggle = next_dither_toggle(toggle); 141 } 142 } 143 count &= 3; 144 if (count) { 145 Sk4f dist = Sk4f::Min(fast_sqrt(R), max); 146 147 uint8_t fi[4]; 148 SkNx_cast<uint8_t>(dist).store(fi); 149 for (int i = 0; i < count; i++) { 150 *dstC++ = cache[toggle + fi[i]]; 151 toggle = next_dither_toggle(toggle); 152 } 153 } 154 } 155 } 156 157 // Unrolling this loop doesn't seem to help (when float); we're stalling to 158 // get the results of the sqrt (?), and don't have enough extra registers to 159 // have many in flight. 160 template <SkFixed (*TileProc)(SkFixed)> 161 void shadeSpan_radial(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy, 162 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 163 int count, int toggle) { 164 do { 165 const SkFixed dist = SkFloatToFixed(sk_float_sqrt(fx*fx + fy*fy)); 166 const unsigned fi = TileProc(dist); 167 SkASSERT(fi <= 0xFFFF); 168 *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)]; 169 toggle = next_dither_toggle(toggle); 170 fx += dx; 171 fy += dy; 172 } while (--count != 0); 173 } 174 175 void shadeSpan_radial_mirror(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy, 176 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 177 int count, int toggle) { 178 shadeSpan_radial<mirror_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle); 179 } 180 181 void shadeSpan_radial_repeat(SkScalar fx, SkScalar dx, SkScalar fy, SkScalar dy, 182 SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache, 183 int count, int toggle) { 184 shadeSpan_radial<repeat_tileproc_nonstatic>(fx, dx, fy, dy, dstC, cache, count, toggle); 185 } 186 187 } // namespace 188 189 void SkRadialGradient::RadialGradientContext::shadeSpan(int x, int y, 190 SkPMColor* SK_RESTRICT dstC, int count) { 191 SkASSERT(count > 0); 192 193 const SkRadialGradient& radialGradient = static_cast<const SkRadialGradient&>(fShader); 194 195 SkPoint srcPt; 196 SkMatrix::MapXYProc dstProc = fDstToIndexProc; 197 const SkPMColor* SK_RESTRICT cache = fCache->getCache32(); 198 int toggle = init_dither_toggle(x, y); 199 200 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, 201 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 202 SkScalar sdx = fDstToIndex.getScaleX(); 203 SkScalar sdy = fDstToIndex.getSkewY(); 204 205 RadialShadeProc shadeProc = shadeSpan_radial_repeat; 206 if (SkShader::kClamp_TileMode == radialGradient.fTileMode) { 207 shadeProc = shadeSpan_radial_clamp2; 208 } else if (SkShader::kMirror_TileMode == radialGradient.fTileMode) { 209 shadeProc = shadeSpan_radial_mirror; 210 } else { 211 SkASSERT(SkShader::kRepeat_TileMode == radialGradient.fTileMode); 212 } 213 (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle); 214 } 215 216 ///////////////////////////////////////////////////////////////////// 217 218 #if SK_SUPPORT_GPU 219 220 #include "SkGr.h" 221 #include "GrShaderCaps.h" 222 #include "glsl/GrGLSLFragmentShaderBuilder.h" 223 224 class GrRadialGradient : public GrGradientEffect { 225 public: 226 class GLSLRadialProcessor; 227 228 static sk_sp<GrFragmentProcessor> Make(const CreateArgs& args) { 229 auto processor = sk_sp<GrRadialGradient>(new GrRadialGradient(args)); 230 return processor->isValid() ? std::move(processor) : nullptr; 231 } 232 233 ~GrRadialGradient() override {} 234 235 const char* name() const override { return "Radial Gradient"; } 236 237 private: 238 GrRadialGradient(const CreateArgs& args) : INHERITED(args, args.fShader->colorsAreOpaque()) { 239 this->initClassID<GrRadialGradient>(); 240 } 241 242 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override; 243 244 virtual void onGetGLSLProcessorKey(const GrShaderCaps& caps, 245 GrProcessorKeyBuilder* b) const override; 246 247 GR_DECLARE_FRAGMENT_PROCESSOR_TEST 248 249 typedef GrGradientEffect INHERITED; 250 }; 251 252 ///////////////////////////////////////////////////////////////////// 253 254 class GrRadialGradient::GLSLRadialProcessor : public GrGradientEffect::GLSLProcessor { 255 public: 256 GLSLRadialProcessor(const GrProcessor&) {} 257 ~GLSLRadialProcessor() override {} 258 259 virtual void emitCode(EmitArgs&) override; 260 261 static void GenKey(const GrProcessor& processor, const GrShaderCaps&, GrProcessorKeyBuilder* b) { 262 b->add32(GenBaseGradientKey(processor)); 263 } 264 265 private: 266 typedef GrGradientEffect::GLSLProcessor INHERITED; 267 268 }; 269 270 ///////////////////////////////////////////////////////////////////// 271 272 GrGLSLFragmentProcessor* GrRadialGradient::onCreateGLSLInstance() const { 273 return new GrRadialGradient::GLSLRadialProcessor(*this); 274 } 275 276 void GrRadialGradient::onGetGLSLProcessorKey(const GrShaderCaps& caps, 277 GrProcessorKeyBuilder* b) const { 278 GrRadialGradient::GLSLRadialProcessor::GenKey(*this, caps, b); 279 } 280 281 ///////////////////////////////////////////////////////////////////// 282 283 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrRadialGradient); 284 285 #if GR_TEST_UTILS 286 sk_sp<GrFragmentProcessor> GrRadialGradient::TestCreate(GrProcessorTestData* d) { 287 sk_sp<SkShader> shader; 288 do { 289 RandomGradientParams params(d->fRandom); 290 SkPoint center = {d->fRandom->nextUScalar1(), d->fRandom->nextUScalar1()}; 291 SkScalar radius = d->fRandom->nextUScalar1(); 292 shader = params.fUseColors4f 293 ? SkGradientShader::MakeRadial(center, radius, params.fColors4f, 294 params.fColorSpace, params.fStops, 295 params.fColorCount, params.fTileMode) 296 : SkGradientShader::MakeRadial(center, radius, params.fColors, 297 params.fStops, params.fColorCount, 298 params.fTileMode); 299 } while (!shader); 300 GrTest::TestAsFPArgs asFPArgs(d); 301 sk_sp<GrFragmentProcessor> fp = as_SB(shader)->asFragmentProcessor(asFPArgs.args()); 302 GrAlwaysAssert(fp); 303 return fp; 304 } 305 #endif 306 307 ///////////////////////////////////////////////////////////////////// 308 309 void GrRadialGradient::GLSLRadialProcessor::emitCode(EmitArgs& args) { 310 const GrRadialGradient& ge = args.fFp.cast<GrRadialGradient>(); 311 this->emitUniforms(args.fUniformHandler, ge); 312 SkString t("length("); 313 t.append(args.fFragBuilder->ensureCoords2D(args.fTransformedCoords[0])); 314 t.append(")"); 315 this->emitColor(args.fFragBuilder, 316 args.fUniformHandler, 317 args.fShaderCaps, 318 ge, t.c_str(), 319 args.fOutputColor, 320 args.fInputColor, 321 args.fTexSamplers); 322 } 323 324 ///////////////////////////////////////////////////////////////////// 325 326 sk_sp<GrFragmentProcessor> SkRadialGradient::asFragmentProcessor(const AsFPArgs& args) const { 327 SkASSERT(args.fContext); 328 329 SkMatrix matrix; 330 if (!this->getLocalMatrix().invert(&matrix)) { 331 return nullptr; 332 } 333 if (args.fLocalMatrix) { 334 SkMatrix inv; 335 if (!args.fLocalMatrix->invert(&inv)) { 336 return nullptr; 337 } 338 matrix.postConcat(inv); 339 } 340 matrix.postConcat(fPtsToUnit); 341 sk_sp<GrColorSpaceXform> colorSpaceXform = GrColorSpaceXform::Make(fColorSpace.get(), 342 args.fDstColorSpace); 343 sk_sp<GrFragmentProcessor> inner(GrRadialGradient::Make( 344 GrGradientEffect::CreateArgs(args.fContext, this, &matrix, fTileMode, 345 std::move(colorSpaceXform), SkToBool(args.fDstColorSpace)))); 346 if (!inner) { 347 return nullptr; 348 } 349 return GrFragmentProcessor::MulOutputByInputAlpha(std::move(inner)); 350 } 351 352 #endif 353 354 sk_sp<SkShader> SkRadialGradient::onMakeColorSpace(SkColorSpaceXformer* xformer) const { 355 SkSTArray<8, SkColor> xformedColors(fColorCount); 356 xformer->apply(xformedColors.begin(), fOrigColors, fColorCount); 357 return SkGradientShader::MakeRadial(fCenter, fRadius, xformedColors.begin(), fOrigPos, 358 fColorCount, fTileMode, fGradFlags, 359 &this->getLocalMatrix()); 360 } 361 362 bool SkRadialGradient::adjustMatrixAndAppendStages(SkArenaAlloc* alloc, 363 SkMatrix* matrix, 364 SkRasterPipeline* p, 365 SkRasterPipeline*) const { 366 matrix->postTranslate(-fCenter.fX, -fCenter.fY); 367 matrix->postScale(1/fRadius, 1/fRadius); 368 369 p->append(SkRasterPipeline::xy_to_radius); 370 return true; 371 } 372 373 #ifndef SK_IGNORE_TO_STRING 374 void SkRadialGradient::toString(SkString* str) const { 375 str->append("SkRadialGradient: ("); 376 377 str->append("center: ("); 378 str->appendScalar(fCenter.fX); 379 str->append(", "); 380 str->appendScalar(fCenter.fY); 381 str->append(") radius: "); 382 str->appendScalar(fRadius); 383 str->append(" "); 384 385 this->INHERITED::toString(str); 386 387 str->append(")"); 388 } 389 #endif 390