Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(53)

Side by Side Diff: src/effects/SkRRectsGaussianEdgeShader.cpp

Issue 2321713004: Add shader-based GaussianEdgeShader for reveal case (Closed)
Patch Set: Clean up Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright 2016 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 "SkRRectsGaussianEdgeShader.h"
9 #include "SkReadBuffer.h"
10 #include "SkWriteBuffer.h"
11
12 /** \class SkRRectsGaussianEdgeShaderImpl
13 * This shader applies a gaussian edge to the intersection of two round rects.
14 * The round rects must have the same radii at each corner and the x&y radii
15 * must also be equal.
16 */
17 class SkRRectsGaussianEdgeShaderImpl : public SkShader {
18 public:
19 SkRRectsGaussianEdgeShaderImpl(const SkRRect& first, const SkRRect& second,
20 SkScalar radius, SkScalar pad)
21 : fFirst(first)
22 , fSecond(second)
23 , fRadius(radius)
24 , fPad(pad) {
25 }
26
27 bool isOpaque() const override { return false; }
28
29 #if SK_SUPPORT_GPU
30 sk_sp<GrFragmentProcessor> asFragmentProcessor(const AsFPArgs&) const overri de;
31 #endif
32
33 class GaussianEdgeShaderContext : public SkShader::Context {
34 public:
35 GaussianEdgeShaderContext(const SkRRectsGaussianEdgeShaderImpl&, const C ontextRec&);
36
37 ~GaussianEdgeShaderContext() override { }
38
39 void shadeSpan(int x, int y, SkPMColor[], int count) override;
40
41 uint32_t getFlags() const override { return 0; }
42
43 private:
44 SkColor fPaintColor;
45
46 typedef SkShader::Context INHERITED;
47 };
48
49 SK_TO_STRING_OVERRIDE()
50 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkRRectsGaussianEdgeShad erImpl)
51
52 protected:
53 void flatten(SkWriteBuffer&) const override;
54 size_t onContextSize(const ContextRec&) const override;
55 Context* onCreateContext(const ContextRec&, void*) const override;
56
57 private:
58 SkRRect fFirst;
59 SkRRect fSecond;
60 SkScalar fRadius;
61 SkScalar fPad;
62
63 friend class SkRRectsGaussianEdgeShader; // for serialization registration s ystem
64
65 typedef SkShader INHERITED;
66 };
67
68 ////////////////////////////////////////////////////////////////////////////
69
70 #if SK_SUPPORT_GPU
71
72 #include "GrCoordTransform.h"
73 #include "GrFragmentProcessor.h"
74 #include "GrInvariantOutput.h"
75 #include "glsl/GrGLSLFragmentProcessor.h"
76 #include "glsl/GrGLSLFragmentShaderBuilder.h"
77 #include "glsl/GrGLSLProgramDataManager.h"
78 #include "glsl/GrGLSLUniformHandler.h"
79 #include "SkGr.h"
80 #include "SkGrPriv.h"
81
82 class RRectsGaussianEdgeFP : public GrFragmentProcessor {
83 public:
84 enum Mode {
85 kCircle_Mode,
86 kRect_Mode,
87 kSimpleCircular_Mode,
88 };
89
90 RRectsGaussianEdgeFP(const SkRRect& first, const SkRRect& second,
91 SkScalar radius, SkScalar pad)
92 : fFirst(first)
93 , fSecond(second)
94 , fRadius(radius)
95 , fPad(pad) {
96 this->initClassID<RRectsGaussianEdgeFP>();
97 this->setWillReadFragmentPosition();
98
99 fFirstMode = ComputeMode(fFirst);
100 fSecondMode = ComputeMode(fSecond);
101 }
102
103 class GLSLRRectsGaussianEdgeFP : public GrGLSLFragmentProcessor {
104 public:
105 GLSLRRectsGaussianEdgeFP() {
106 fCachedPositions[0] = fCachedPositions[1] =
107 fCachedPositions[2] = fCachedPositions[3] = SK _ScalarMax;
108 fCachedSizes[0] = fCachedSizes[1] = fCachedSizes[2] = fCachedSizes[3 ] = -1.0f;
109 fCachedRadii[0] = fCachedRadii[1] = fCachedRadii[2] = fCachedRadii[3 ] = -1.0f;
110 fCachedPadRad.set(-1.0f, -1.0f);
111 }
112
113 void emitModeCode(Mode mode,
114 GrGLSLFPFragmentBuilder* fragBuilder,
115 const char* posName,
116 const char* sizesName,
117 const char* radiiName,
118 const char* outputName,
119 const char indices[2]) { // how to access the params for the 2 rrects
120
121 // positive distance is towards the center of the circle
122 fragBuilder->codeAppendf("vec2 delta = %s.xy - %s.%s;",
123 fragBuilder->fragmentPosition(),
124 posName, indices);
125
126 switch (mode) {
127 case kCircle_Mode:
128 fragBuilder->codeAppendf("%s = %s.%c - length(delta);",
129 outputName,
130 sizesName, indices[0]);
131 break;
132 case kRect_Mode:
133 fragBuilder->codeAppendf("float xDist = %s.%c - abs(delta.x);",
134 sizesName, indices[0]);
135 fragBuilder->codeAppendf("float yDist = %s.%c - abs(delta.y);",
136 sizesName, indices[1]);
137 fragBuilder->codeAppendf("%s = min(xDist, yDist);", outputName);
138 break;
139 case kSimpleCircular_Mode:
140 // For the circular round rect we first compute the distance
141 // to the rect. Then we compute a multiplier that is 1 if the
142 // point is in one of the circular corners. We then compute the
143 // distance from the corner and then use the multiplier to mask
144 // between the two distances.
145 fragBuilder->codeAppendf("float xDist = %s.%c - abs(delta.x);",
146 sizesName, indices[0]);
147 fragBuilder->codeAppendf("float yDist = %s.%c - abs(delta.y);",
148 sizesName, indices[1]);
149 fragBuilder->codeAppend("float rectDist = min(xDist, yDist);");
150
151 fragBuilder->codeAppendf("vec2 cornerCenter = %s.%s - %s.%s;",
152 sizesName, indices,
153 radiiName, indices);
154 fragBuilder->codeAppend("delta = vec2(abs(delta.x) - cornerCente r.x,"
155 "abs(delta.y) - cornerCente r.y);");
156 fragBuilder->codeAppendf("xDist = %s.%c - abs(delta.x);",
157 radiiName, indices[0]);
158 fragBuilder->codeAppendf("yDist = %s.%c - abs(delta.y);",
159 radiiName, indices[1]);
160 fragBuilder->codeAppend("float cornerDist = min(xDist, yDist);") ;
161 fragBuilder->codeAppend("float multiplier = step(0.0, cornerDist );");
162
163 fragBuilder->codeAppendf("delta += %s.%s;", radiiName, indices);
164
165 fragBuilder->codeAppendf("cornerDist = 2.0 * %s.%c - length(delt a);",
166 radiiName, indices[0]);
167
168 fragBuilder->codeAppendf("%s = (multiplier * cornerDist) +"
169 "((1.0-multiplier) * rectDist);",
170 outputName);
171 break;
172 }
173 }
174
175 void emitCode(EmitArgs& args) override {
176 const RRectsGaussianEdgeFP& fp = args.fFp.cast<RRectsGaussianEdgeFP> ();
177 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
178 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
179
180 const char* positionsUniName = nullptr;
181 fPositionsUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
182 kVec4f_GrSLType, kDefault _GrSLPrecision,
183 "Positions", &positionsUn iName);
184 const char* sizesUniName = nullptr;
185 fSizesUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
186 kVec4f_GrSLType, kDefault_GrS LPrecision,
187 "Sizes", &sizesUniName);
188 const char* radiiUniName = nullptr;
189 fRadiiUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
190 kVec4f_GrSLType, kDefault_GrS LPrecision,
191 "Radii", &radiiUniName);
192 const char* padRadUniName = nullptr;
193 fPadRadUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
194 kVec2f_GrSLType, kDefault_Gr SLPrecision,
195 "PadRad", &padRadUniName);
196
197 fragBuilder->codeAppend("float firstDist;");
198 fragBuilder->codeAppend("{");
199 this->emitModeCode(fp.firstMode(), fragBuilder,
200 positionsUniName, sizesUniName, radiiUniName, "fi rstDist", "xy");
201 fragBuilder->codeAppend("}");
202
203 fragBuilder->codeAppend("float secondDist;");
204 fragBuilder->codeAppend("{");
205 this->emitModeCode(fp.secondMode(), fragBuilder,
206 positionsUniName, sizesUniName, radiiUniName, "se condDist", "zw");
207 fragBuilder->codeAppend("}");
208
209 // Here use the sign of the distance to the two round rects to mask off the different
210 // cases.
211 fragBuilder->codeAppend("float in1 = step(0.0f, firstDist);");
212 fragBuilder->codeAppend("float in2 = step(0.0f, secondDist);");
213 fragBuilder->codeAppend("float dist = "
214 "in1*in2 * min(firstDis t, secondDist);"
215 "in1*(1.0-in2) * firstDist +"
216 "(1.0-in1)*in2 * secondDist;" );
217
218 // Finally use the distance to apply the Gaussian edge
219 fragBuilder->codeAppendf("float factor = 1.0 - clamp((dist - %s.x)/% s.y, 0.0, 1.0);",
220 padRadUniName, padRadUniName);
221 fragBuilder->codeAppend("factor = exp(-factor * factor * 4.0) - 0.01 8;");
222 fragBuilder->codeAppendf("%s = vec4(%s.rgb, factor);",
223 args.fOutputColor, args.fInputColor);
224 }
225
226 static void GenKey(const GrProcessor& proc, const GrGLSLCaps&,
227 GrProcessorKeyBuilder* b) {
228 const RRectsGaussianEdgeFP& fp = proc.cast<RRectsGaussianEdgeFP>();
229
230 b->add32(fp.firstMode() | (fp.secondMode() << 4));
231 }
232
233 protected:
234 void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {
235 const RRectsGaussianEdgeFP& edgeFP = proc.cast<RRectsGaussianEdgeFP> ();
236
237 const SkRRect& first = edgeFP.first();
238 const SkRRect& second = edgeFP.second();
239
240 float positions[4] = {
241 first.getBounds().centerX(),
242 first.getBounds().centerY(),
243 second.getBounds().centerX(),
244 second.getBounds().centerY(),
245 };
246
247 if (positions[0] != fCachedPositions[0] || positions[1] != fCachedPo sitions[1] ||
bsalomon 2016/09/12 14:56:52 Given the use case wonder if this is worth doing.
robertphillips 2016/09/12 15:13:49 Yeah I was wondering that too. Removed.
248 positions[2] != fCachedPositions[2] || positions[3] != fCachedPo sitions[3]) {
249 pdman.set4fv(fPositionsUni, 1, positions);
250 memcpy(fCachedPositions, positions, sizeof(fCachedPositions));
251 }
252
253 float sizes[4] = {
254 0.5f * first.rect().width(),
255 0.5f * first.rect().height(),
256 0.5f * second.rect().width(),
257 0.5f * second.rect().height()
258 };
259
260 if (sizes[0] != fCachedSizes[0] || sizes[1] != fCachedSizes[1] ||
261 sizes[2] != fCachedSizes[2] || sizes[3] != fCachedSizes[3]) {
262 pdman.set4fv(fSizesUni, 1, sizes);
263 memcpy(fCachedSizes, sizes, sizeof(fCachedSizes));
264 }
265
266 // This is a bit of overkill since fX should equal fY for both round rects but it
267 // makes the shader code simpler.
268 float radii[4] = {
269 0.5f * first.getSimpleRadii().fX,
270 0.5f * first.getSimpleRadii().fY,
271 0.5f * second.getSimpleRadii().fX,
272 0.5f * second.getSimpleRadii().fY
273 };
274
275 if (radii[0] != fCachedRadii[0] || radii[1] != fCachedRadii[1] ||
276 radii[2] != fCachedRadii[2] || radii[3] != fCachedRadii[3]) {
277 pdman.set4fv(fRadiiUni, 1, radii);
278 memcpy(fCachedRadii, radii, sizeof(fCachedRadii));
279 }
280
281 SkPoint padRad = { edgeFP.pad(), edgeFP.radius() };
282
283 if (padRad != fCachedPadRad) {
284 pdman.set2fv(fPadRadUni, 1, &padRad.fX);
285 fCachedPadRad = padRad;
286 }
287 }
288
289 private:
290 // The centers of the two round rects (x1, y1, x2, y2)
291 GrGLSLProgramDataManager::UniformHandle fPositionsUni;
292 SkScalar fCachedPositions[4];
293
294 // The half widths and half heights of the two round rects (w1/2, h1/2, w2/2, h2/2)
295 // For circles we still upload both width & height to simplify things
296 GrGLSLProgramDataManager::UniformHandle fSizesUni;
297 SkScalar fCachedSizes[4];
298
299 // The half corner radii of the two round rects (rx1/2, ry1/2, rx2/2, ry 2/2)
300 // We upload both the x&y radii (although they are currently always the same) to make
301 // the indexing in the shader code simpler. In some future world we coul d also support
302 // non-circular corner round rects & ellipses.
303 GrGLSLProgramDataManager::UniformHandle fRadiiUni;
304 SkScalar fCachedRadii[4];
305
306 // The pad and radius parameters (padding, radius)
307 GrGLSLProgramDataManager::UniformHandle fPadRadUni;
308 SkPoint fCachedPadRad;
309
310 typedef GrGLSLFragmentProcessor INHERITED;
311 };
312
313 void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
314 GLSLRRectsGaussianEdgeFP::GenKey(*this, caps, b);
315 }
316
317 const char* name() const override { return "RRectsGaussianEdgeFP"; }
318
319 void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
320 inout->setToUnknown(GrInvariantOutput::kWill_ReadInput);
321 }
322
323 const SkRRect& first() const { return fFirst; }
324 Mode firstMode() const { return fFirstMode; }
325 const SkRRect& second() const { return fSecond; }
326 Mode secondMode() const { return fSecondMode; }
327 SkScalar radius() const { return fRadius; }
328 SkScalar pad() const { return fPad; }
329
330 private:
331 static Mode ComputeMode(const SkRRect& rr) {
332 if (rr.isCircle()) {
333 return kCircle_Mode;
334 } else if (rr.isRect()) {
335 return kRect_Mode;
336 } else {
337 SkASSERT(rr.isSimpleCircular());
338 return kSimpleCircular_Mode;
339 }
340 }
341
342 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
343 return new GLSLRRectsGaussianEdgeFP;
344 }
345
346 bool onIsEqual(const GrFragmentProcessor& proc) const override {
347 const RRectsGaussianEdgeFP& edgeFP = proc.cast<RRectsGaussianEdgeFP>();
348 return fFirst == edgeFP.fFirst && fSecond == edgeFP.fSecond &&
349 fRadius == edgeFP.fRadius && fPad == edgeFP.fPad;
350 }
351
352 SkRRect fFirst;
353 Mode fFirstMode;
354 SkRRect fSecond;
355 Mode fSecondMode;
356 SkScalar fRadius;
357 SkScalar fPad;
358
359 typedef GrFragmentProcessor INHERITED;
360 };
361
362 ////////////////////////////////////////////////////////////////////////////
363
364 sk_sp<GrFragmentProcessor> SkRRectsGaussianEdgeShaderImpl::asFragmentProcessor(
365 const AsFPAr gs& args) const {
366 return sk_make_sp<RRectsGaussianEdgeFP>(fFirst, fSecond, fRadius, fPad);
367 }
368
369 #endif
370
371 ////////////////////////////////////////////////////////////////////////////
372
373 SkRRectsGaussianEdgeShaderImpl::GaussianEdgeShaderContext::GaussianEdgeShaderCon text(
374 const SkRRectsGaussianEdgeSh aderImpl& shader,
375 const ContextRec& rec)
376 : INHERITED(shader, rec) {
377
378 fPaintColor = rec.fPaint->getColor();
379 }
380
381 void SkRRectsGaussianEdgeShaderImpl::GaussianEdgeShaderContext::shadeSpan(int x, int y,
382 SkPMCo lor result[],
383 int co unt) {
384 // TODO: implement
385 for (int i = 0; i < count; ++i) {
386 result[i] = fPaintColor;
387 }
388 }
389
390 ////////////////////////////////////////////////////////////////////////////
391
392 #ifndef SK_IGNORE_TO_STRING
393 void SkRRectsGaussianEdgeShaderImpl::toString(SkString* str) const {
394 str->appendf("RRectsGaussianEdgeShader: ()");
395 }
396 #endif
397
398 sk_sp<SkFlattenable> SkRRectsGaussianEdgeShaderImpl::CreateProc(SkReadBuffer& bu f) {
399 // Discarding SkShader flattenable params
400 bool hasLocalMatrix = buf.readBool();
401 SkAssertResult(!hasLocalMatrix);
402
403 SkRect rect1, rect2;
404
405 buf.readRect(&rect1);
406 SkScalar xRad1 = buf.readScalar();
407 SkScalar yRad1 = buf.readScalar();
408
409 buf.readRect(&rect2);
410 SkScalar xRad2 = buf.readScalar();
411 SkScalar yRad2 = buf.readScalar();
412
413 SkScalar radius = buf.readScalar();
414 SkScalar pad = buf.readScalar();
415
416 return sk_make_sp<SkRRectsGaussianEdgeShaderImpl>(SkRRect::MakeRectXY(rect1, xRad1, yRad1),
417 SkRRect::MakeRectXY(rect2, xRad2, yRad2),
418 radius, pad);
419 }
420
421 void SkRRectsGaussianEdgeShaderImpl::flatten(SkWriteBuffer& buf) const {
422 INHERITED::flatten(buf);
423
424 SkASSERT(fFirst.isRect() || fFirst.isCircle() || fFirst.isSimpleCircular());
425 buf.writeRect(fFirst.rect());
426 const SkVector& radii1 = fFirst.getSimpleRadii();
427 buf.writeScalar(radii1.fX);
428 buf.writeScalar(radii1.fY);
429
430 SkASSERT(fSecond.isRect() || fSecond.isCircle() || fSecond.isSimpleCircular( ));
431 buf.writeRect(fSecond.rect());
432 const SkVector& radii2 = fSecond.getSimpleRadii();
433 buf.writeScalar(radii2.fX);
434 buf.writeScalar(radii2.fY);
435
436 buf.writeScalar(fRadius);
437 buf.writeScalar(fPad);
438 }
439
440 size_t SkRRectsGaussianEdgeShaderImpl::onContextSize(const ContextRec& rec) cons t {
441 return sizeof(GaussianEdgeShaderContext);
442 }
443
444 SkShader::Context* SkRRectsGaussianEdgeShaderImpl::onCreateContext(const Context Rec& rec,
445 void* storage ) const {
446 return new (storage) GaussianEdgeShaderContext(*this, rec);
447 }
448
449 ///////////////////////////////////////////////////////////////////////////////
450
451 sk_sp<SkShader> SkRRectsGaussianEdgeShader::Make(const SkRRect& first,
452 const SkRRect& second,
453 SkScalar radius,
454 SkScalar pad) {
455 if ((!first.isRect() && !first.isCircle() && !first.isSimpleCircular()) ||
456 (!second.isRect() && !second.isCircle() && !second.isSimpleCircular())) {
457 // we only deal with the shapes where the x & y radii are equal
458 // and the same for all four corners
459 return nullptr;
460 }
461
462 return sk_make_sp<SkRRectsGaussianEdgeShaderImpl>(first, second, radius, pad );
463 }
464
465 ///////////////////////////////////////////////////////////////////////////////
466
467 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkRRectsGaussianEdgeShader)
468 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkRRectsGaussianEdgeShaderImpl)
469 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
470
471 ///////////////////////////////////////////////////////////////////////////////
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698