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

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

Issue 2321713004: Add shader-based GaussianEdgeShader for reveal case (Closed)
Patch Set: Address code review comments 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
107 void emitModeCode(Mode mode,
108 GrGLSLFPFragmentBuilder* fragBuilder,
109 const char* posName,
110 const char* sizesName,
111 const char* radiiName,
112 const char* outputName,
113 const char indices[2]) { // how to access the params for the 2 rrects
114
115 // positive distance is towards the center of the circle
116 fragBuilder->codeAppendf("vec2 delta = %s.xy - %s.%s;",
117 fragBuilder->fragmentPosition(),
118 posName, indices);
119
120 switch (mode) {
121 case kCircle_Mode:
122 fragBuilder->codeAppendf("%s = %s.%c - length(delta);",
123 outputName,
124 sizesName, indices[0]);
125 break;
126 case kRect_Mode:
127 fragBuilder->codeAppendf("float xDist = %s.%c - abs(delta.x);",
128 sizesName, indices[0]);
129 fragBuilder->codeAppendf("float yDist = %s.%c - abs(delta.y);",
130 sizesName, indices[1]);
131 fragBuilder->codeAppendf("%s = min(xDist, yDist);", outputName);
132 break;
133 case kSimpleCircular_Mode:
134 // For the circular round rect we first compute the distance
135 // to the rect. Then we compute a multiplier that is 1 if the
136 // point is in one of the circular corners. We then compute the
137 // distance from the corner and then use the multiplier to mask
138 // between the two distances.
139 fragBuilder->codeAppendf("float xDist = %s.%c - abs(delta.x);",
140 sizesName, indices[0]);
141 fragBuilder->codeAppendf("float yDist = %s.%c - abs(delta.y);",
142 sizesName, indices[1]);
143 fragBuilder->codeAppend("float rectDist = min(xDist, yDist);");
144
145 fragBuilder->codeAppendf("vec2 cornerCenter = %s.%s - %s.%s;",
146 sizesName, indices,
147 radiiName, indices);
148 fragBuilder->codeAppend("delta = vec2(abs(delta.x) - cornerCente r.x,"
149 "abs(delta.y) - cornerCente r.y);");
150 fragBuilder->codeAppendf("xDist = %s.%c - abs(delta.x);",
151 radiiName, indices[0]);
152 fragBuilder->codeAppendf("yDist = %s.%c - abs(delta.y);",
153 radiiName, indices[1]);
154 fragBuilder->codeAppend("float cornerDist = min(xDist, yDist);") ;
155 fragBuilder->codeAppend("float multiplier = step(0.0, cornerDist );");
156
157 fragBuilder->codeAppendf("delta += %s.%s;", radiiName, indices);
158
159 fragBuilder->codeAppendf("cornerDist = 2.0 * %s.%c - length(delt a);",
160 radiiName, indices[0]);
161
162 fragBuilder->codeAppendf("%s = (multiplier * cornerDist) +"
163 "((1.0-multiplier) * rectDist);",
164 outputName);
165 break;
166 }
167 }
168
169 void emitCode(EmitArgs& args) override {
170 const RRectsGaussianEdgeFP& fp = args.fFp.cast<RRectsGaussianEdgeFP> ();
171 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
172 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
173
174 const char* positionsUniName = nullptr;
175 fPositionsUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
176 kVec4f_GrSLType, kDefault _GrSLPrecision,
177 "Positions", &positionsUn iName);
178 const char* sizesUniName = nullptr;
179 fSizesUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
180 kVec4f_GrSLType, kDefault_GrS LPrecision,
181 "Sizes", &sizesUniName);
182 const char* radiiUniName = nullptr;
183 fRadiiUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
184 kVec4f_GrSLType, kDefault_GrS LPrecision,
185 "Radii", &radiiUniName);
186 const char* padRadUniName = nullptr;
187 fPadRadUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
188 kVec2f_GrSLType, kDefault_Gr SLPrecision,
189 "PadRad", &padRadUniName);
190
191 fragBuilder->codeAppend("float firstDist;");
192 fragBuilder->codeAppend("{");
193 this->emitModeCode(fp.firstMode(), fragBuilder,
194 positionsUniName, sizesUniName, radiiUniName, "fi rstDist", "xy");
195 fragBuilder->codeAppend("}");
196
197 fragBuilder->codeAppend("float secondDist;");
198 fragBuilder->codeAppend("{");
199 this->emitModeCode(fp.secondMode(), fragBuilder,
200 positionsUniName, sizesUniName, radiiUniName, "se condDist", "zw");
201 fragBuilder->codeAppend("}");
202
203 // Here use the sign of the distance to the two round rects to mask off the different
204 // cases.
205 fragBuilder->codeAppend("float in1 = step(0.0f, firstDist);");
206 fragBuilder->codeAppend("float in2 = step(0.0f, secondDist);");
207 fragBuilder->codeAppend("float dist = "
208 "in1*in2 * min(firstDis t, secondDist);"
209 "in1*(1.0-in2) * firstDist +"
210 "(1.0-in1)*in2 * secondDist;" );
211
212 // Finally use the distance to apply the Gaussian edge
213 fragBuilder->codeAppendf("float factor = 1.0 - clamp((dist - %s.x)/% s.y, 0.0, 1.0);",
214 padRadUniName, padRadUniName);
215 fragBuilder->codeAppend("factor = exp(-factor * factor * 4.0) - 0.01 8;");
216 fragBuilder->codeAppendf("%s = vec4(%s.rgb, factor);",
217 args.fOutputColor, args.fInputColor);
218 }
219
220 static void GenKey(const GrProcessor& proc, const GrGLSLCaps&,
221 GrProcessorKeyBuilder* b) {
222 const RRectsGaussianEdgeFP& fp = proc.cast<RRectsGaussianEdgeFP>();
223
224 b->add32(fp.firstMode() | (fp.secondMode() << 4));
225 }
226
227 protected:
228 void onSetData(const GrGLSLProgramDataManager& pdman, const GrProcessor& proc) override {
229 const RRectsGaussianEdgeFP& edgeFP = proc.cast<RRectsGaussianEdgeFP> ();
230
231 const SkRRect& first = edgeFP.first();
232 const SkRRect& second = edgeFP.second();
233
234 pdman.set4f(fPositionsUni,
235 first.getBounds().centerX(),
236 first.getBounds().centerY(),
237 second.getBounds().centerX(),
238 second.getBounds().centerY());
239
240 pdman.set4f(fSizesUni,
241 0.5f * first.rect().width(),
242 0.5f * first.rect().height(),
243 0.5f * second.rect().width(),
244 0.5f * second.rect().height());
245
246 // This is a bit of overkill since fX should equal fY for both round rects but it
247 // makes the shader code simpler.
248 pdman.set4f(fRadiiUni,
249 0.5f * first.getSimpleRadii().fX,
250 0.5f * first.getSimpleRadii().fY,
251 0.5f * second.getSimpleRadii().fX,
252 0.5f * second.getSimpleRadii().fY);
253
254 pdman.set2f(fPadRadUni, edgeFP.pad(), edgeFP.radius());
255 }
256
257 private:
258 // The centers of the two round rects (x1, y1, x2, y2)
259 GrGLSLProgramDataManager::UniformHandle fPositionsUni;
260
261 // The half widths and half heights of the two round rects (w1/2, h1/2, w2/2, h2/2)
262 // For circles we still upload both width & height to simplify things
263 GrGLSLProgramDataManager::UniformHandle fSizesUni;
264
265 // The half corner radii of the two round rects (rx1/2, ry1/2, rx2/2, ry 2/2)
266 // We upload both the x&y radii (although they are currently always the same) to make
267 // the indexing in the shader code simpler. In some future world we coul d also support
268 // non-circular corner round rects & ellipses.
269 GrGLSLProgramDataManager::UniformHandle fRadiiUni;
270
271 // The pad and radius parameters (padding, radius)
272 GrGLSLProgramDataManager::UniformHandle fPadRadUni;
273
274 typedef GrGLSLFragmentProcessor INHERITED;
275 };
276
277 void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
278 GLSLRRectsGaussianEdgeFP::GenKey(*this, caps, b);
279 }
280
281 const char* name() const override { return "RRectsGaussianEdgeFP"; }
282
283 void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
284 inout->setToUnknown(GrInvariantOutput::kWill_ReadInput);
285 }
286
287 const SkRRect& first() const { return fFirst; }
288 Mode firstMode() const { return fFirstMode; }
289 const SkRRect& second() const { return fSecond; }
290 Mode secondMode() const { return fSecondMode; }
291 SkScalar radius() const { return fRadius; }
292 SkScalar pad() const { return fPad; }
293
294 private:
295 static Mode ComputeMode(const SkRRect& rr) {
296 if (rr.isCircle()) {
297 return kCircle_Mode;
298 } else if (rr.isRect()) {
299 return kRect_Mode;
300 } else {
301 SkASSERT(rr.isSimpleCircular());
302 return kSimpleCircular_Mode;
303 }
304 }
305
306 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
307 return new GLSLRRectsGaussianEdgeFP;
308 }
309
310 bool onIsEqual(const GrFragmentProcessor& proc) const override {
311 const RRectsGaussianEdgeFP& edgeFP = proc.cast<RRectsGaussianEdgeFP>();
312 return fFirst == edgeFP.fFirst && fSecond == edgeFP.fSecond &&
313 fRadius == edgeFP.fRadius && fPad == edgeFP.fPad;
314 }
315
316 SkRRect fFirst;
317 Mode fFirstMode;
318 SkRRect fSecond;
319 Mode fSecondMode;
320 SkScalar fRadius;
321 SkScalar fPad;
322
323 typedef GrFragmentProcessor INHERITED;
324 };
325
326 ////////////////////////////////////////////////////////////////////////////
327
328 sk_sp<GrFragmentProcessor> SkRRectsGaussianEdgeShaderImpl::asFragmentProcessor(
329 const AsFPAr gs& args) const {
330 return sk_make_sp<RRectsGaussianEdgeFP>(fFirst, fSecond, fRadius, fPad);
331 }
332
333 #endif
334
335 ////////////////////////////////////////////////////////////////////////////
336
337 SkRRectsGaussianEdgeShaderImpl::GaussianEdgeShaderContext::GaussianEdgeShaderCon text(
338 const SkRRectsGaussianEdgeSh aderImpl& shader,
339 const ContextRec& rec)
340 : INHERITED(shader, rec) {
341
342 fPaintColor = rec.fPaint->getColor();
343 }
344
345 void SkRRectsGaussianEdgeShaderImpl::GaussianEdgeShaderContext::shadeSpan(int x, int y,
346 SkPMCo lor result[],
347 int co unt) {
348 // TODO: implement
349 for (int i = 0; i < count; ++i) {
350 result[i] = fPaintColor;
351 }
352 }
353
354 ////////////////////////////////////////////////////////////////////////////
355
356 #ifndef SK_IGNORE_TO_STRING
357 void SkRRectsGaussianEdgeShaderImpl::toString(SkString* str) const {
358 str->appendf("RRectsGaussianEdgeShader: ()");
359 }
360 #endif
361
362 sk_sp<SkFlattenable> SkRRectsGaussianEdgeShaderImpl::CreateProc(SkReadBuffer& bu f) {
363 // Discarding SkShader flattenable params
364 bool hasLocalMatrix = buf.readBool();
365 SkAssertResult(!hasLocalMatrix);
366
367 SkRect rect1, rect2;
368
369 buf.readRect(&rect1);
370 SkScalar xRad1 = buf.readScalar();
371 SkScalar yRad1 = buf.readScalar();
372
373 buf.readRect(&rect2);
374 SkScalar xRad2 = buf.readScalar();
375 SkScalar yRad2 = buf.readScalar();
376
377 SkScalar radius = buf.readScalar();
378 SkScalar pad = buf.readScalar();
379
380 return sk_make_sp<SkRRectsGaussianEdgeShaderImpl>(SkRRect::MakeRectXY(rect1, xRad1, yRad1),
381 SkRRect::MakeRectXY(rect2, xRad2, yRad2),
382 radius, pad);
383 }
384
385 void SkRRectsGaussianEdgeShaderImpl::flatten(SkWriteBuffer& buf) const {
386 INHERITED::flatten(buf);
387
388 SkASSERT(fFirst.isRect() || fFirst.isCircle() || fFirst.isSimpleCircular());
389 buf.writeRect(fFirst.rect());
390 const SkVector& radii1 = fFirst.getSimpleRadii();
391 buf.writeScalar(radii1.fX);
392 buf.writeScalar(radii1.fY);
393
394 SkASSERT(fSecond.isRect() || fSecond.isCircle() || fSecond.isSimpleCircular( ));
395 buf.writeRect(fSecond.rect());
396 const SkVector& radii2 = fSecond.getSimpleRadii();
397 buf.writeScalar(radii2.fX);
398 buf.writeScalar(radii2.fY);
399
400 buf.writeScalar(fRadius);
401 buf.writeScalar(fPad);
402 }
403
404 size_t SkRRectsGaussianEdgeShaderImpl::onContextSize(const ContextRec& rec) cons t {
405 return sizeof(GaussianEdgeShaderContext);
406 }
407
408 SkShader::Context* SkRRectsGaussianEdgeShaderImpl::onCreateContext(const Context Rec& rec,
409 void* storage ) const {
410 return new (storage) GaussianEdgeShaderContext(*this, rec);
411 }
412
413 ///////////////////////////////////////////////////////////////////////////////
414
415 sk_sp<SkShader> SkRRectsGaussianEdgeShader::Make(const SkRRect& first,
416 const SkRRect& second,
417 SkScalar radius,
418 SkScalar pad) {
419 if ((!first.isRect() && !first.isCircle() && !first.isSimpleCircular()) ||
420 (!second.isRect() && !second.isCircle() && !second.isSimpleCircular())) {
421 // we only deal with the shapes where the x & y radii are equal
422 // and the same for all four corners
423 return nullptr;
424 }
425
426 return sk_make_sp<SkRRectsGaussianEdgeShaderImpl>(first, second, radius, pad );
427 }
428
429 ///////////////////////////////////////////////////////////////////////////////
430
431 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkRRectsGaussianEdgeShader)
432 SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkRRectsGaussianEdgeShaderImpl)
433 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
434
435 ///////////////////////////////////////////////////////////////////////////////
OLDNEW
« no previous file with comments | « include/effects/SkRRectsGaussianEdgeShader.h ('k') | src/ports/SkGlobalInitialization_default.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698