OLD | NEW |
---|---|
(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 /////////////////////////////////////////////////////////////////////////////// | |
OLD | NEW |