GrOvalOpFactory.cpp 141 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269
  1. /*
  2. * Copyright 2013 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. #include "include/core/SkStrokeRec.h"
  8. #include "src/core/SkRRectPriv.h"
  9. #include "src/gpu/GrCaps.h"
  10. #include "src/gpu/GrDrawOpTest.h"
  11. #include "src/gpu/GrGeometryProcessor.h"
  12. #include "src/gpu/GrOpFlushState.h"
  13. #include "src/gpu/GrProcessor.h"
  14. #include "src/gpu/GrResourceProvider.h"
  15. #include "src/gpu/GrShaderCaps.h"
  16. #include "src/gpu/GrStyle.h"
  17. #include "src/gpu/GrVertexWriter.h"
  18. #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
  19. #include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
  20. #include "src/gpu/glsl/GrGLSLProgramDataManager.h"
  21. #include "src/gpu/glsl/GrGLSLUniformHandler.h"
  22. #include "src/gpu/glsl/GrGLSLUtil.h"
  23. #include "src/gpu/glsl/GrGLSLVarying.h"
  24. #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
  25. #include "src/gpu/ops/GrMeshDrawOp.h"
  26. #include "src/gpu/ops/GrOvalOpFactory.h"
  27. #include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
  28. #include <utility>
  29. namespace {
  30. static inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); }
  31. // Produces TriStrip vertex data for an origin-centered rectangle from [-x, -y] to [x, y]
  32. static inline GrVertexWriter::TriStrip<float> origin_centered_tri_strip(float x, float y) {
  33. return GrVertexWriter::TriStrip<float>{ -x, -y, x, y };
  34. };
  35. }
  36. ///////////////////////////////////////////////////////////////////////////////
  37. /**
  38. * The output of this effect is a modulation of the input color and coverage for a circle. It
  39. * operates in a space normalized by the circle radius (outer radius in the case of a stroke)
  40. * with origin at the circle center. Three vertex attributes are used:
  41. * vec2f : position in device space of the bounding geometry vertices
  42. * vec4ub: color
  43. * vec4f : (p.xy, outerRad, innerRad)
  44. * p is the position in the normalized space.
  45. * outerRad is the outerRadius in device space.
  46. * innerRad is the innerRadius in normalized space (ignored if not stroking).
  47. * Additional clip planes are supported for rendering circular arcs. The additional planes are
  48. * either intersected or unioned together. Up to three planes are supported (an initial plane,
  49. * a plane intersected with the initial plane, and a plane unioned with the first two). Only two
  50. * are useful for any given arc, but having all three in one instance allows combining different
  51. * types of arcs.
  52. * Round caps for stroking are allowed as well. The caps are specified as two circle center points
  53. * in the same space as p.xy.
  54. */
  55. class CircleGeometryProcessor : public GrGeometryProcessor {
  56. public:
  57. CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane,
  58. bool roundCaps, bool wideColor, const SkMatrix& localMatrix)
  59. : INHERITED(kCircleGeometryProcessor_ClassID)
  60. , fLocalMatrix(localMatrix)
  61. , fStroke(stroke) {
  62. fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
  63. fInColor = MakeColorAttribute("inColor", wideColor);
  64. fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
  65. if (clipPlane) {
  66. fInClipPlane = {"inClipPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
  67. }
  68. if (isectPlane) {
  69. fInIsectPlane = {"inIsectPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
  70. }
  71. if (unionPlane) {
  72. fInUnionPlane = {"inUnionPlane", kFloat3_GrVertexAttribType, kHalf3_GrSLType};
  73. }
  74. if (roundCaps) {
  75. SkASSERT(stroke);
  76. SkASSERT(clipPlane);
  77. fInRoundCapCenters =
  78. {"inRoundCapCenters", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
  79. }
  80. this->setVertexAttributes(&fInPosition, 7);
  81. }
  82. ~CircleGeometryProcessor() override {}
  83. const char* name() const override { return "CircleEdge"; }
  84. void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
  85. GLSLProcessor::GenKey(*this, caps, b);
  86. }
  87. GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
  88. return new GLSLProcessor();
  89. }
  90. private:
  91. class GLSLProcessor : public GrGLSLGeometryProcessor {
  92. public:
  93. GLSLProcessor() {}
  94. void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
  95. const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>();
  96. GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
  97. GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
  98. GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
  99. GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
  100. // emit attributes
  101. varyingHandler->emitAttributes(cgp);
  102. fragBuilder->codeAppend("float4 circleEdge;");
  103. varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge");
  104. if (cgp.fInClipPlane.isInitialized()) {
  105. fragBuilder->codeAppend("half3 clipPlane;");
  106. varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane");
  107. }
  108. if (cgp.fInIsectPlane.isInitialized()) {
  109. fragBuilder->codeAppend("half3 isectPlane;");
  110. varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane");
  111. }
  112. if (cgp.fInUnionPlane.isInitialized()) {
  113. SkASSERT(cgp.fInClipPlane.isInitialized());
  114. fragBuilder->codeAppend("half3 unionPlane;");
  115. varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane");
  116. }
  117. GrGLSLVarying capRadius(kFloat_GrSLType);
  118. if (cgp.fInRoundCapCenters.isInitialized()) {
  119. fragBuilder->codeAppend("float4 roundCapCenters;");
  120. varyingHandler->addPassThroughAttribute(cgp.fInRoundCapCenters, "roundCapCenters");
  121. varyingHandler->addVarying("capRadius", &capRadius,
  122. GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
  123. // This is the cap radius in normalized space where the outer radius is 1 and
  124. // circledEdge.w is the normalized inner radius.
  125. vertBuilder->codeAppendf("%s = (1.0 - %s.w) / 2.0;", capRadius.vsOut(),
  126. cgp.fInCircleEdge.name());
  127. }
  128. // setup pass through color
  129. varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor);
  130. // Setup position
  131. this->writeOutputPosition(vertBuilder, gpArgs, cgp.fInPosition.name());
  132. // emit transforms
  133. this->emitTransforms(vertBuilder,
  134. varyingHandler,
  135. uniformHandler,
  136. cgp.fInPosition.asShaderVar(),
  137. cgp.fLocalMatrix,
  138. args.fFPCoordTransformHandler);
  139. fragBuilder->codeAppend("float d = length(circleEdge.xy);");
  140. fragBuilder->codeAppend("half distanceToOuterEdge = half(circleEdge.z * (1.0 - d));");
  141. fragBuilder->codeAppend("half edgeAlpha = saturate(distanceToOuterEdge);");
  142. if (cgp.fStroke) {
  143. fragBuilder->codeAppend(
  144. "half distanceToInnerEdge = half(circleEdge.z * (d - circleEdge.w));");
  145. fragBuilder->codeAppend("half innerAlpha = saturate(distanceToInnerEdge);");
  146. fragBuilder->codeAppend("edgeAlpha *= innerAlpha;");
  147. }
  148. if (cgp.fInClipPlane.isInitialized()) {
  149. fragBuilder->codeAppend(
  150. "half clip = half(saturate(circleEdge.z * dot(circleEdge.xy, "
  151. "clipPlane.xy) + clipPlane.z));");
  152. if (cgp.fInIsectPlane.isInitialized()) {
  153. fragBuilder->codeAppend(
  154. "clip *= half(saturate(circleEdge.z * dot(circleEdge.xy, "
  155. "isectPlane.xy) + isectPlane.z));");
  156. }
  157. if (cgp.fInUnionPlane.isInitialized()) {
  158. fragBuilder->codeAppend(
  159. "clip = saturate(clip + half(saturate(circleEdge.z * dot(circleEdge.xy,"
  160. " unionPlane.xy) + unionPlane.z)));");
  161. }
  162. fragBuilder->codeAppend("edgeAlpha *= clip;");
  163. if (cgp.fInRoundCapCenters.isInitialized()) {
  164. // We compute coverage of the round caps as circles at the butt caps produced
  165. // by the clip planes. The inverse of the clip planes is applied so that there
  166. // is no double counting.
  167. fragBuilder->codeAppendf(
  168. "half dcap1 = half(circleEdge.z * (%s - length(circleEdge.xy - "
  169. " roundCapCenters.xy)));"
  170. "half dcap2 = half(circleEdge.z * (%s - length(circleEdge.xy - "
  171. " roundCapCenters.zw)));"
  172. "half capAlpha = (1 - clip) * (max(dcap1, 0) + max(dcap2, 0));"
  173. "edgeAlpha = min(edgeAlpha + capAlpha, 1.0);",
  174. capRadius.fsIn(), capRadius.fsIn());
  175. }
  176. }
  177. fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
  178. }
  179. static void GenKey(const GrGeometryProcessor& gp,
  180. const GrShaderCaps&,
  181. GrProcessorKeyBuilder* b) {
  182. const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>();
  183. uint16_t key;
  184. key = cgp.fStroke ? 0x01 : 0x0;
  185. key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0;
  186. key |= cgp.fInClipPlane.isInitialized() ? 0x04 : 0x0;
  187. key |= cgp.fInIsectPlane.isInitialized() ? 0x08 : 0x0;
  188. key |= cgp.fInUnionPlane.isInitialized() ? 0x10 : 0x0;
  189. key |= cgp.fInRoundCapCenters.isInitialized() ? 0x20 : 0x0;
  190. b->add32(key);
  191. }
  192. void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
  193. FPCoordTransformIter&& transformIter) override {
  194. this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix,
  195. pdman, &transformIter);
  196. }
  197. private:
  198. typedef GrGLSLGeometryProcessor INHERITED;
  199. };
  200. SkMatrix fLocalMatrix;
  201. Attribute fInPosition;
  202. Attribute fInColor;
  203. Attribute fInCircleEdge;
  204. // Optional attributes.
  205. Attribute fInClipPlane;
  206. Attribute fInIsectPlane;
  207. Attribute fInUnionPlane;
  208. Attribute fInRoundCapCenters;
  209. bool fStroke;
  210. GR_DECLARE_GEOMETRY_PROCESSOR_TEST
  211. typedef GrGeometryProcessor INHERITED;
  212. };
  213. GR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor);
  214. #if GR_TEST_UTILS
  215. sk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
  216. bool stroke = d->fRandom->nextBool();
  217. bool roundCaps = stroke ? d->fRandom->nextBool() : false;
  218. bool wideColor = d->fRandom->nextBool();
  219. bool clipPlane = d->fRandom->nextBool();
  220. bool isectPlane = d->fRandom->nextBool();
  221. bool unionPlane = d->fRandom->nextBool();
  222. const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
  223. return sk_sp<GrGeometryProcessor>(new CircleGeometryProcessor(
  224. stroke, clipPlane, isectPlane, unionPlane, roundCaps, wideColor, matrix));
  225. }
  226. #endif
  227. class ButtCapDashedCircleGeometryProcessor : public GrGeometryProcessor {
  228. public:
  229. ButtCapDashedCircleGeometryProcessor(bool wideColor, const SkMatrix& localMatrix)
  230. : INHERITED(kButtCapStrokedCircleGeometryProcessor_ClassID), fLocalMatrix(localMatrix) {
  231. fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
  232. fInColor = MakeColorAttribute("inColor", wideColor);
  233. fInCircleEdge = {"inCircleEdge", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
  234. fInDashParams = {"inDashParams", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
  235. this->setVertexAttributes(&fInPosition, 4);
  236. }
  237. ~ButtCapDashedCircleGeometryProcessor() override {}
  238. const char* name() const override { return "ButtCapDashedCircleGeometryProcessor"; }
  239. void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
  240. GLSLProcessor::GenKey(*this, caps, b);
  241. }
  242. GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
  243. return new GLSLProcessor();
  244. }
  245. private:
  246. class GLSLProcessor : public GrGLSLGeometryProcessor {
  247. public:
  248. GLSLProcessor() {}
  249. void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
  250. const ButtCapDashedCircleGeometryProcessor& bcscgp =
  251. args.fGP.cast<ButtCapDashedCircleGeometryProcessor>();
  252. GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
  253. GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
  254. GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
  255. GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
  256. // emit attributes
  257. varyingHandler->emitAttributes(bcscgp);
  258. fragBuilder->codeAppend("float4 circleEdge;");
  259. varyingHandler->addPassThroughAttribute(bcscgp.fInCircleEdge, "circleEdge");
  260. fragBuilder->codeAppend("float4 dashParams;");
  261. varyingHandler->addPassThroughAttribute(
  262. bcscgp.fInDashParams, "dashParams",
  263. GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
  264. GrGLSLVarying wrapDashes(kHalf4_GrSLType);
  265. varyingHandler->addVarying("wrapDashes", &wrapDashes,
  266. GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
  267. GrGLSLVarying lastIntervalLength(kHalf_GrSLType);
  268. varyingHandler->addVarying("lastIntervalLength", &lastIntervalLength,
  269. GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
  270. vertBuilder->codeAppendf("float4 dashParams = %s;", bcscgp.fInDashParams.name());
  271. // Our fragment shader works in on/off intervals as specified by dashParams.xy:
  272. // x = length of on interval, y = length of on + off.
  273. // There are two other parameters in dashParams.zw:
  274. // z = start angle in radians, w = phase offset in radians in range -y/2..y/2.
  275. // Each interval has a "corresponding" dash which may be shifted partially or
  276. // fully out of its interval by the phase. So there may be up to two "visual"
  277. // dashes in an interval.
  278. // When computing coverage in an interval we look at three dashes. These are the
  279. // "corresponding" dashes from the current, previous, and next intervals. Any of these
  280. // may be phase shifted into our interval or even when phase=0 they may be within half a
  281. // pixel distance of a pixel center in the interval.
  282. // When in the first interval we need to check the dash from the last interval. And
  283. // similarly when in the last interval we need to check the dash from the first
  284. // interval. When 2pi is not perfectly divisible dashParams.y this is a boundary case.
  285. // We compute the dash begin/end angles in the vertex shader and apply them in the
  286. // fragment shader when we detect we're in the first/last interval.
  287. vertBuilder->codeAppend(R"(
  288. // The two boundary dash intervals are stored in wrapDashes.xy and .zw and fed
  289. // to the fragment shader as a varying.
  290. float4 wrapDashes;
  291. half lastIntervalLength = mod(6.28318530718, half(dashParams.y));
  292. // We can happen to be perfectly divisible.
  293. if (0 == lastIntervalLength) {
  294. lastIntervalLength = half(dashParams.y);
  295. }
  296. // Let 'l' be the last interval before reaching 2 pi.
  297. // Based on the phase determine whether (l-1)th, l-th, or (l+1)th interval's
  298. // "corresponding" dash appears in the l-th interval and is closest to the 0-th
  299. // interval.
  300. half offset = 0;
  301. if (-dashParams.w >= lastIntervalLength) {
  302. offset = half(-dashParams.y);
  303. } else if (dashParams.w > dashParams.y - lastIntervalLength) {
  304. offset = half(dashParams.y);
  305. }
  306. wrapDashes.x = -lastIntervalLength + offset - dashParams.w;
  307. // The end of this dash may be beyond the 2 pi and therefore clipped. Hence the
  308. // min.
  309. wrapDashes.y = min(wrapDashes.x + dashParams.x, 0);
  310. // Based on the phase determine whether the -1st, 0th, or 1st interval's
  311. // "corresponding" dash appears in the 0th interval and is closest to l.
  312. offset = 0;
  313. if (dashParams.w >= dashParams.x) {
  314. offset = half(dashParams.y);
  315. } else if (-dashParams.w > dashParams.y - dashParams.x) {
  316. offset = half(-dashParams.y);
  317. }
  318. wrapDashes.z = lastIntervalLength + offset - dashParams.w;
  319. wrapDashes.w = wrapDashes.z + dashParams.x;
  320. // The start of the dash we're considering may be clipped by the start of the
  321. // circle.
  322. wrapDashes.z = max(wrapDashes.z, lastIntervalLength);
  323. )");
  324. vertBuilder->codeAppendf("%s = half4(wrapDashes);", wrapDashes.vsOut());
  325. vertBuilder->codeAppendf("%s = lastIntervalLength;", lastIntervalLength.vsOut());
  326. fragBuilder->codeAppendf("half4 wrapDashes = %s;", wrapDashes.fsIn());
  327. fragBuilder->codeAppendf("half lastIntervalLength = %s;", lastIntervalLength.fsIn());
  328. // setup pass through color
  329. varyingHandler->addPassThroughAttribute(
  330. bcscgp.fInColor, args.fOutputColor,
  331. GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
  332. // Setup position
  333. this->writeOutputPosition(vertBuilder, gpArgs, bcscgp.fInPosition.name());
  334. // emit transforms
  335. this->emitTransforms(vertBuilder,
  336. varyingHandler,
  337. uniformHandler,
  338. bcscgp.fInPosition.asShaderVar(),
  339. bcscgp.fLocalMatrix,
  340. args.fFPCoordTransformHandler);
  341. GrShaderVar fnArgs[] = {
  342. GrShaderVar("angleToEdge", kFloat_GrSLType),
  343. GrShaderVar("diameter", kFloat_GrSLType),
  344. };
  345. SkString fnName;
  346. fragBuilder->emitFunction(kFloat_GrSLType, "coverage_from_dash_edge",
  347. SK_ARRAY_COUNT(fnArgs), fnArgs, R"(
  348. float linearDist;
  349. angleToEdge = clamp(angleToEdge, -3.1415, 3.1415);
  350. linearDist = diameter * sin(angleToEdge / 2);
  351. return saturate(linearDist + 0.5);
  352. )",
  353. &fnName);
  354. fragBuilder->codeAppend(R"(
  355. float d = length(circleEdge.xy) * circleEdge.z;
  356. // Compute coverage from outer/inner edges of the stroke.
  357. half distanceToOuterEdge = half(circleEdge.z - d);
  358. half edgeAlpha = saturate(distanceToOuterEdge);
  359. half distanceToInnerEdge = half(d - circleEdge.z * circleEdge.w);
  360. half innerAlpha = saturate(distanceToInnerEdge);
  361. edgeAlpha *= innerAlpha;
  362. half angleFromStart = half(atan(circleEdge.y, circleEdge.x) - dashParams.z);
  363. angleFromStart = mod(angleFromStart, 6.28318530718);
  364. float x = mod(angleFromStart, dashParams.y);
  365. // Convert the radial distance from center to pixel into a diameter.
  366. d *= 2;
  367. half2 currDash = half2(half(-dashParams.w), half(dashParams.x) -
  368. half(dashParams.w));
  369. half2 nextDash = half2(half(dashParams.y) - half(dashParams.w),
  370. half(dashParams.y) + half(dashParams.x) -
  371. half(dashParams.w));
  372. half2 prevDash = half2(half(-dashParams.y) - half(dashParams.w),
  373. half(-dashParams.y) + half(dashParams.x) -
  374. half(dashParams.w));
  375. half dashAlpha = 0;
  376. )");
  377. fragBuilder->codeAppendf(R"(
  378. if (angleFromStart - x + dashParams.y >= 6.28318530718) {
  379. dashAlpha += half(%s(x - wrapDashes.z, d) * %s(wrapDashes.w - x, d));
  380. currDash.y = min(currDash.y, lastIntervalLength);
  381. if (nextDash.x >= lastIntervalLength) {
  382. // The next dash is outside the 0..2pi range, throw it away
  383. nextDash.xy = half2(1000);
  384. } else {
  385. // Clip the end of the next dash to the end of the circle
  386. nextDash.y = min(nextDash.y, lastIntervalLength);
  387. }
  388. }
  389. )", fnName.c_str(), fnName.c_str());
  390. fragBuilder->codeAppendf(R"(
  391. if (angleFromStart - x - dashParams.y < -0.01) {
  392. dashAlpha += half(%s(x - wrapDashes.x, d) * %s(wrapDashes.y - x, d));
  393. currDash.x = max(currDash.x, 0);
  394. if (prevDash.y <= 0) {
  395. // The previous dash is outside the 0..2pi range, throw it away
  396. prevDash.xy = half2(1000);
  397. } else {
  398. // Clip the start previous dash to the start of the circle
  399. prevDash.x = max(prevDash.x, 0);
  400. }
  401. }
  402. )", fnName.c_str(), fnName.c_str());
  403. fragBuilder->codeAppendf(R"(
  404. dashAlpha += half(%s(x - currDash.x, d) * %s(currDash.y - x, d));
  405. dashAlpha += half(%s(x - nextDash.x, d) * %s(nextDash.y - x, d));
  406. dashAlpha += half(%s(x - prevDash.x, d) * %s(prevDash.y - x, d));
  407. dashAlpha = min(dashAlpha, 1);
  408. edgeAlpha *= dashAlpha;
  409. )", fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(), fnName.c_str(),
  410. fnName.c_str());
  411. fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage);
  412. }
  413. static void GenKey(const GrGeometryProcessor& gp,
  414. const GrShaderCaps&,
  415. GrProcessorKeyBuilder* b) {
  416. const ButtCapDashedCircleGeometryProcessor& bcscgp =
  417. gp.cast<ButtCapDashedCircleGeometryProcessor>();
  418. b->add32(bcscgp.fLocalMatrix.hasPerspective());
  419. }
  420. void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
  421. FPCoordTransformIter&& transformIter) override {
  422. this->setTransformDataHelper(
  423. primProc.cast<ButtCapDashedCircleGeometryProcessor>().fLocalMatrix, pdman,
  424. &transformIter);
  425. }
  426. private:
  427. typedef GrGLSLGeometryProcessor INHERITED;
  428. };
  429. SkMatrix fLocalMatrix;
  430. Attribute fInPosition;
  431. Attribute fInColor;
  432. Attribute fInCircleEdge;
  433. Attribute fInDashParams;
  434. GR_DECLARE_GEOMETRY_PROCESSOR_TEST
  435. typedef GrGeometryProcessor INHERITED;
  436. };
  437. #if GR_TEST_UTILS
  438. sk_sp<GrGeometryProcessor> ButtCapDashedCircleGeometryProcessor::TestCreate(GrProcessorTestData* d) {
  439. bool wideColor = d->fRandom->nextBool();
  440. const SkMatrix& matrix = GrTest::TestMatrix(d->fRandom);
  441. return sk_sp<GrGeometryProcessor>(new ButtCapDashedCircleGeometryProcessor(wideColor, matrix));
  442. }
  443. #endif
  444. ///////////////////////////////////////////////////////////////////////////////
  445. /**
  446. * The output of this effect is a modulation of the input color and coverage for an axis-aligned
  447. * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii,
  448. * in both x and y directions.
  449. *
  450. * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0.
  451. */
  452. class EllipseGeometryProcessor : public GrGeometryProcessor {
  453. public:
  454. EllipseGeometryProcessor(bool stroke, bool wideColor, bool useScale,
  455. const SkMatrix& localMatrix)
  456. : INHERITED(kEllipseGeometryProcessor_ClassID)
  457. , fLocalMatrix(localMatrix)
  458. , fStroke(stroke)
  459. , fUseScale(useScale) {
  460. fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
  461. fInColor = MakeColorAttribute("inColor", wideColor);
  462. if (useScale) {
  463. fInEllipseOffset = {"inEllipseOffset", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
  464. } else {
  465. fInEllipseOffset = {"inEllipseOffset", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
  466. }
  467. fInEllipseRadii = {"inEllipseRadii", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
  468. this->setVertexAttributes(&fInPosition, 4);
  469. }
  470. ~EllipseGeometryProcessor() override {}
  471. const char* name() const override { return "EllipseEdge"; }
  472. void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
  473. GLSLProcessor::GenKey(*this, caps, b);
  474. }
  475. GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
  476. return new GLSLProcessor();
  477. }
  478. private:
  479. class GLSLProcessor : public GrGLSLGeometryProcessor {
  480. public:
  481. GLSLProcessor() {}
  482. void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
  483. const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>();
  484. GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
  485. GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
  486. GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
  487. // emit attributes
  488. varyingHandler->emitAttributes(egp);
  489. GrSLType offsetType = egp.fUseScale ? kFloat3_GrSLType : kFloat2_GrSLType;
  490. GrGLSLVarying ellipseOffsets(offsetType);
  491. varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets);
  492. vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(),
  493. egp.fInEllipseOffset.name());
  494. GrGLSLVarying ellipseRadii(kFloat4_GrSLType);
  495. varyingHandler->addVarying("EllipseRadii", &ellipseRadii);
  496. vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii.name());
  497. GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
  498. // setup pass through color
  499. varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor);
  500. // Setup position
  501. this->writeOutputPosition(vertBuilder, gpArgs, egp.fInPosition.name());
  502. // emit transforms
  503. this->emitTransforms(vertBuilder,
  504. varyingHandler,
  505. uniformHandler,
  506. egp.fInPosition.asShaderVar(),
  507. egp.fLocalMatrix,
  508. args.fFPCoordTransformHandler);
  509. // For stroked ellipses, we use the full ellipse equation (x^2/a^2 + y^2/b^2 = 1)
  510. // to compute both the edges because we need two separate test equations for
  511. // the single offset.
  512. // For filled ellipses we can use a unit circle equation (x^2 + y^2 = 1), and warp
  513. // the distance by the gradient, non-uniformly scaled by the inverse of the
  514. // ellipse size.
  515. // On medium precision devices, we scale the denominator of the distance equation
  516. // before taking the inverse square root to minimize the chance that we're dividing
  517. // by zero, then we scale the result back.
  518. // for outer curve
  519. fragBuilder->codeAppendf("float2 offset = %s.xy;", ellipseOffsets.fsIn());
  520. if (egp.fStroke) {
  521. fragBuilder->codeAppendf("offset *= %s.xy;", ellipseRadii.fsIn());
  522. }
  523. fragBuilder->codeAppend("float test = dot(offset, offset) - 1.0;");
  524. if (egp.fUseScale) {
  525. fragBuilder->codeAppendf("float2 grad = 2.0*offset*(%s.z*%s.xy);",
  526. ellipseOffsets.fsIn(), ellipseRadii.fsIn());
  527. } else {
  528. fragBuilder->codeAppendf("float2 grad = 2.0*offset*%s.xy;", ellipseRadii.fsIn());
  529. }
  530. fragBuilder->codeAppend("float grad_dot = dot(grad, grad);");
  531. // avoid calling inversesqrt on zero.
  532. if (args.fShaderCaps->floatIs32Bits()) {
  533. fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
  534. } else {
  535. fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
  536. }
  537. if (egp.fUseScale) {
  538. fragBuilder->codeAppendf("float invlen = %s.z*inversesqrt(grad_dot);",
  539. ellipseOffsets.fsIn());
  540. } else {
  541. fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
  542. }
  543. fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
  544. // for inner curve
  545. if (egp.fStroke) {
  546. fragBuilder->codeAppendf("offset = %s.xy*%s.zw;", ellipseOffsets.fsIn(),
  547. ellipseRadii.fsIn());
  548. fragBuilder->codeAppend("test = dot(offset, offset) - 1.0;");
  549. if (egp.fUseScale) {
  550. fragBuilder->codeAppendf("grad = 2.0*offset*(%s.z*%s.zw);",
  551. ellipseOffsets.fsIn(), ellipseRadii.fsIn());
  552. } else {
  553. fragBuilder->codeAppendf("grad = 2.0*offset*%s.zw;", ellipseRadii.fsIn());
  554. }
  555. fragBuilder->codeAppend("grad_dot = dot(grad, grad);");
  556. if (!args.fShaderCaps->floatIs32Bits()) {
  557. fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
  558. }
  559. if (egp.fUseScale) {
  560. fragBuilder->codeAppendf("invlen = %s.z*inversesqrt(grad_dot);",
  561. ellipseOffsets.fsIn());
  562. } else {
  563. fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
  564. }
  565. fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
  566. }
  567. fragBuilder->codeAppendf("%s = half4(half(edgeAlpha));", args.fOutputCoverage);
  568. }
  569. static void GenKey(const GrGeometryProcessor& gp,
  570. const GrShaderCaps&,
  571. GrProcessorKeyBuilder* b) {
  572. const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>();
  573. uint16_t key = egp.fStroke ? 0x1 : 0x0;
  574. key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0;
  575. b->add32(key);
  576. }
  577. void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
  578. FPCoordTransformIter&& transformIter) override {
  579. const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>();
  580. this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter);
  581. }
  582. private:
  583. typedef GrGLSLGeometryProcessor INHERITED;
  584. };
  585. Attribute fInPosition;
  586. Attribute fInColor;
  587. Attribute fInEllipseOffset;
  588. Attribute fInEllipseRadii;
  589. SkMatrix fLocalMatrix;
  590. bool fStroke;
  591. bool fUseScale;
  592. GR_DECLARE_GEOMETRY_PROCESSOR_TEST
  593. typedef GrGeometryProcessor INHERITED;
  594. };
  595. GR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor);
  596. #if GR_TEST_UTILS
  597. sk_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
  598. return sk_sp<GrGeometryProcessor>(
  599. new EllipseGeometryProcessor(d->fRandom->nextBool(), d->fRandom->nextBool(),
  600. d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom)));
  601. }
  602. #endif
  603. ///////////////////////////////////////////////////////////////////////////////
  604. /**
  605. * The output of this effect is a modulation of the input color and coverage for an ellipse,
  606. * specified as a 2D offset from center for both the outer and inner paths (if stroked). The
  607. * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by
  608. * using differentials.
  609. *
  610. * The result is device-independent and can be used with any affine matrix.
  611. */
  612. enum class DIEllipseStyle { kStroke = 0, kHairline, kFill };
  613. class DIEllipseGeometryProcessor : public GrGeometryProcessor {
  614. public:
  615. DIEllipseGeometryProcessor(bool wideColor, bool useScale, const SkMatrix& viewMatrix,
  616. DIEllipseStyle style)
  617. : INHERITED(kDIEllipseGeometryProcessor_ClassID)
  618. , fViewMatrix(viewMatrix)
  619. , fUseScale(useScale)
  620. , fStyle(style) {
  621. fInPosition = {"inPosition", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
  622. fInColor = MakeColorAttribute("inColor", wideColor);
  623. if (useScale) {
  624. fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat3_GrVertexAttribType,
  625. kFloat3_GrSLType};
  626. } else {
  627. fInEllipseOffsets0 = {"inEllipseOffsets0", kFloat2_GrVertexAttribType,
  628. kFloat2_GrSLType};
  629. }
  630. fInEllipseOffsets1 = {"inEllipseOffsets1", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
  631. this->setVertexAttributes(&fInPosition, 4);
  632. }
  633. ~DIEllipseGeometryProcessor() override {}
  634. const char* name() const override { return "DIEllipseEdge"; }
  635. void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
  636. GLSLProcessor::GenKey(*this, caps, b);
  637. }
  638. GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
  639. return new GLSLProcessor();
  640. }
  641. private:
  642. class GLSLProcessor : public GrGLSLGeometryProcessor {
  643. public:
  644. GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {}
  645. void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
  646. const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>();
  647. GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
  648. GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
  649. GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
  650. // emit attributes
  651. varyingHandler->emitAttributes(diegp);
  652. GrSLType offsetType = (diegp.fUseScale) ? kFloat3_GrSLType : kFloat2_GrSLType;
  653. GrGLSLVarying offsets0(offsetType);
  654. varyingHandler->addVarying("EllipseOffsets0", &offsets0);
  655. vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0.name());
  656. GrGLSLVarying offsets1(kFloat2_GrSLType);
  657. varyingHandler->addVarying("EllipseOffsets1", &offsets1);
  658. vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1.name());
  659. GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
  660. varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor);
  661. // Setup position
  662. this->writeOutputPosition(vertBuilder,
  663. uniformHandler,
  664. gpArgs,
  665. diegp.fInPosition.name(),
  666. diegp.fViewMatrix,
  667. &fViewMatrixUniform);
  668. // emit transforms
  669. this->emitTransforms(vertBuilder,
  670. varyingHandler,
  671. uniformHandler,
  672. diegp.fInPosition.asShaderVar(),
  673. args.fFPCoordTransformHandler);
  674. // for outer curve
  675. fragBuilder->codeAppendf("float2 scaledOffset = %s.xy;", offsets0.fsIn());
  676. fragBuilder->codeAppend("float test = dot(scaledOffset, scaledOffset) - 1.0;");
  677. fragBuilder->codeAppendf("float2 duvdx = dFdx(%s.xy);", offsets0.fsIn());
  678. fragBuilder->codeAppendf("float2 duvdy = dFdy(%s.xy);", offsets0.fsIn());
  679. fragBuilder->codeAppendf(
  680. "float2 grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
  681. " %s.x*duvdy.x + %s.y*duvdy.y);",
  682. offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn());
  683. if (diegp.fUseScale) {
  684. fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
  685. }
  686. fragBuilder->codeAppend("float grad_dot = 4.0*dot(grad, grad);");
  687. // avoid calling inversesqrt on zero.
  688. if (args.fShaderCaps->floatIs32Bits()) {
  689. fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.1755e-38);");
  690. } else {
  691. fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
  692. }
  693. fragBuilder->codeAppend("float invlen = inversesqrt(grad_dot);");
  694. if (diegp.fUseScale) {
  695. fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
  696. }
  697. if (DIEllipseStyle::kHairline == diegp.fStyle) {
  698. // can probably do this with one step
  699. fragBuilder->codeAppend("float edgeAlpha = saturate(1.0-test*invlen);");
  700. fragBuilder->codeAppend("edgeAlpha *= saturate(1.0+test*invlen);");
  701. } else {
  702. fragBuilder->codeAppend("float edgeAlpha = saturate(0.5-test*invlen);");
  703. }
  704. // for inner curve
  705. if (DIEllipseStyle::kStroke == diegp.fStyle) {
  706. fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn());
  707. fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;");
  708. fragBuilder->codeAppendf("duvdx = float2(dFdx(%s));", offsets1.fsIn());
  709. fragBuilder->codeAppendf("duvdy = float2(dFdy(%s));", offsets1.fsIn());
  710. fragBuilder->codeAppendf(
  711. "grad = float2(%s.x*duvdx.x + %s.y*duvdx.y,"
  712. " %s.x*duvdy.x + %s.y*duvdy.y);",
  713. offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn());
  714. if (diegp.fUseScale) {
  715. fragBuilder->codeAppendf("grad *= %s.z;", offsets0.fsIn());
  716. }
  717. fragBuilder->codeAppend("grad_dot = 4.0*dot(grad, grad);");
  718. if (!args.fShaderCaps->floatIs32Bits()) {
  719. fragBuilder->codeAppend("grad_dot = max(grad_dot, 6.1036e-5);");
  720. }
  721. fragBuilder->codeAppend("invlen = inversesqrt(grad_dot);");
  722. if (diegp.fUseScale) {
  723. fragBuilder->codeAppendf("invlen *= %s.z;", offsets0.fsIn());
  724. }
  725. fragBuilder->codeAppend("edgeAlpha *= saturate(0.5+test*invlen);");
  726. }
  727. fragBuilder->codeAppendf("%s = half4(half(edgeAlpha));", args.fOutputCoverage);
  728. }
  729. static void GenKey(const GrGeometryProcessor& gp,
  730. const GrShaderCaps&,
  731. GrProcessorKeyBuilder* b) {
  732. const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
  733. uint16_t key = static_cast<uint16_t>(diegp.fStyle);
  734. key |= ComputePosKey(diegp.fViewMatrix) << 10;
  735. b->add32(key);
  736. }
  737. void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp,
  738. FPCoordTransformIter&& transformIter) override {
  739. const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>();
  740. if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) {
  741. fViewMatrix = diegp.fViewMatrix;
  742. float viewMatrix[3 * 3];
  743. GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix);
  744. pdman.setMatrix3f(fViewMatrixUniform, viewMatrix);
  745. }
  746. this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
  747. }
  748. private:
  749. SkMatrix fViewMatrix;
  750. UniformHandle fViewMatrixUniform;
  751. typedef GrGLSLGeometryProcessor INHERITED;
  752. };
  753. Attribute fInPosition;
  754. Attribute fInColor;
  755. Attribute fInEllipseOffsets0;
  756. Attribute fInEllipseOffsets1;
  757. SkMatrix fViewMatrix;
  758. bool fUseScale;
  759. DIEllipseStyle fStyle;
  760. GR_DECLARE_GEOMETRY_PROCESSOR_TEST
  761. typedef GrGeometryProcessor INHERITED;
  762. };
  763. GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor);
  764. #if GR_TEST_UTILS
  765. sk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) {
  766. return sk_sp<GrGeometryProcessor>(new DIEllipseGeometryProcessor(
  767. d->fRandom->nextBool(), d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom),
  768. (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2))));
  769. }
  770. #endif
  771. ///////////////////////////////////////////////////////////////////////////////
  772. // We have two possible cases for geometry for a circle:
  773. // In the case of a normal fill, we draw geometry for the circle as an octagon.
  774. static const uint16_t gFillCircleIndices[] = {
  775. // enter the octagon
  776. // clang-format off
  777. 0, 1, 8, 1, 2, 8,
  778. 2, 3, 8, 3, 4, 8,
  779. 4, 5, 8, 5, 6, 8,
  780. 6, 7, 8, 7, 0, 8
  781. // clang-format on
  782. };
  783. // For stroked circles, we use two nested octagons.
  784. static const uint16_t gStrokeCircleIndices[] = {
  785. // enter the octagon
  786. // clang-format off
  787. 0, 1, 9, 0, 9, 8,
  788. 1, 2, 10, 1, 10, 9,
  789. 2, 3, 11, 2, 11, 10,
  790. 3, 4, 12, 3, 12, 11,
  791. 4, 5, 13, 4, 13, 12,
  792. 5, 6, 14, 5, 14, 13,
  793. 6, 7, 15, 6, 15, 14,
  794. 7, 0, 8, 7, 8, 15,
  795. // clang-format on
  796. };
  797. // Normalized geometry for octagons that circumscribe and lie on a circle:
  798. static constexpr SkScalar kOctOffset = 0.41421356237f; // sqrt(2) - 1
  799. static constexpr SkPoint kOctagonOuter[] = {
  800. SkPoint::Make(-kOctOffset, -1),
  801. SkPoint::Make( kOctOffset, -1),
  802. SkPoint::Make( 1, -kOctOffset),
  803. SkPoint::Make( 1, kOctOffset),
  804. SkPoint::Make( kOctOffset, 1),
  805. SkPoint::Make(-kOctOffset, 1),
  806. SkPoint::Make(-1, kOctOffset),
  807. SkPoint::Make(-1, -kOctOffset),
  808. };
  809. // cosine and sine of pi/8
  810. static constexpr SkScalar kCosPi8 = 0.923579533f;
  811. static constexpr SkScalar kSinPi8 = 0.382683432f;
  812. static constexpr SkPoint kOctagonInner[] = {
  813. SkPoint::Make(-kSinPi8, -kCosPi8),
  814. SkPoint::Make( kSinPi8, -kCosPi8),
  815. SkPoint::Make( kCosPi8, -kSinPi8),
  816. SkPoint::Make( kCosPi8, kSinPi8),
  817. SkPoint::Make( kSinPi8, kCosPi8),
  818. SkPoint::Make(-kSinPi8, kCosPi8),
  819. SkPoint::Make(-kCosPi8, kSinPi8),
  820. SkPoint::Make(-kCosPi8, -kSinPi8),
  821. };
  822. static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
  823. static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
  824. static const int kVertsPerStrokeCircle = 16;
  825. static const int kVertsPerFillCircle = 9;
  826. static int circle_type_to_vert_count(bool stroked) {
  827. return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
  828. }
  829. static int circle_type_to_index_count(bool stroked) {
  830. return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
  831. }
  832. static const uint16_t* circle_type_to_indices(bool stroked) {
  833. return stroked ? gStrokeCircleIndices : gFillCircleIndices;
  834. }
  835. ///////////////////////////////////////////////////////////////////////////////
  836. class CircleOp final : public GrMeshDrawOp {
  837. private:
  838. using Helper = GrSimpleMeshDrawOpHelper;
  839. public:
  840. DEFINE_OP_CLASS_ID
  841. /** Optional extra params to render a partial arc rather than a full circle. */
  842. struct ArcParams {
  843. SkScalar fStartAngleRadians;
  844. SkScalar fSweepAngleRadians;
  845. bool fUseCenter;
  846. };
  847. static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
  848. GrPaint&& paint,
  849. const SkMatrix& viewMatrix,
  850. SkPoint center,
  851. SkScalar radius,
  852. const GrStyle& style,
  853. const ArcParams* arcParams = nullptr) {
  854. SkASSERT(circle_stays_circle(viewMatrix));
  855. if (style.hasPathEffect()) {
  856. return nullptr;
  857. }
  858. const SkStrokeRec& stroke = style.strokeRec();
  859. SkStrokeRec::Style recStyle = stroke.getStyle();
  860. if (arcParams) {
  861. // Arc support depends on the style.
  862. switch (recStyle) {
  863. case SkStrokeRec::kStrokeAndFill_Style:
  864. // This produces a strange result that this op doesn't implement.
  865. return nullptr;
  866. case SkStrokeRec::kFill_Style:
  867. // This supports all fills.
  868. break;
  869. case SkStrokeRec::kStroke_Style:
  870. // Strokes that don't use the center point are supported with butt and round
  871. // caps.
  872. if (arcParams->fUseCenter || stroke.getCap() == SkPaint::kSquare_Cap) {
  873. return nullptr;
  874. }
  875. break;
  876. case SkStrokeRec::kHairline_Style:
  877. // Hairline only supports butt cap. Round caps could be emulated by slightly
  878. // extending the angle range if we ever care to.
  879. if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) {
  880. return nullptr;
  881. }
  882. break;
  883. }
  884. }
  885. return Helper::FactoryHelper<CircleOp>(context, std::move(paint), viewMatrix, center,
  886. radius, style, arcParams);
  887. }
  888. CircleOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
  889. const SkMatrix& viewMatrix, SkPoint center, SkScalar radius, const GrStyle& style,
  890. const ArcParams* arcParams)
  891. : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
  892. const SkStrokeRec& stroke = style.strokeRec();
  893. SkStrokeRec::Style recStyle = stroke.getStyle();
  894. fRoundCaps = false;
  895. viewMatrix.mapPoints(&center, 1);
  896. radius = viewMatrix.mapRadius(radius);
  897. SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth());
  898. bool isStrokeOnly =
  899. SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle;
  900. bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle;
  901. SkScalar innerRadius = -SK_ScalarHalf;
  902. SkScalar outerRadius = radius;
  903. SkScalar halfWidth = 0;
  904. if (hasStroke) {
  905. if (SkScalarNearlyZero(strokeWidth)) {
  906. halfWidth = SK_ScalarHalf;
  907. } else {
  908. halfWidth = SkScalarHalf(strokeWidth);
  909. }
  910. outerRadius += halfWidth;
  911. if (isStrokeOnly) {
  912. innerRadius = radius - halfWidth;
  913. }
  914. }
  915. // The radii are outset for two reasons. First, it allows the shader to simply perform
  916. // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
  917. // Second, the outer radius is used to compute the verts of the bounding box that is
  918. // rendered and the outset ensures the box will cover all partially covered by the circle.
  919. outerRadius += SK_ScalarHalf;
  920. innerRadius -= SK_ScalarHalf;
  921. bool stroked = isStrokeOnly && innerRadius > 0.0f;
  922. fViewMatrixIfUsingLocalCoords = viewMatrix;
  923. // This makes every point fully inside the intersection plane.
  924. static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f};
  925. // This makes every point fully outside the union plane.
  926. static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f};
  927. static constexpr SkPoint kUnusedRoundCaps[] = {{1e10f, 1e10f}, {1e10f, 1e10f}};
  928. SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
  929. center.fX + outerRadius, center.fY + outerRadius);
  930. if (arcParams) {
  931. // The shader operates in a space where the circle is translated to be centered at the
  932. // origin. Here we compute points on the unit circle at the starting and ending angles.
  933. SkPoint startPoint, stopPoint;
  934. startPoint.fY = SkScalarSin(arcParams->fStartAngleRadians);
  935. startPoint.fX = SkScalarCos(arcParams->fStartAngleRadians);
  936. SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians;
  937. stopPoint.fY = SkScalarSin(endAngle);
  938. stopPoint.fX = SkScalarCos(endAngle);
  939. // Adjust the start and end points based on the view matrix (to handle rotated arcs)
  940. startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY);
  941. stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY);
  942. startPoint.normalize();
  943. stopPoint.normalize();
  944. // If the matrix included scale (on one axis) we need to swap our start and end points
  945. if ((viewMatrix.getScaleX() < 0) != (viewMatrix.getScaleY() < 0)) {
  946. using std::swap;
  947. swap(startPoint, stopPoint);
  948. }
  949. fRoundCaps = style.strokeRec().getWidth() > 0 &&
  950. style.strokeRec().getCap() == SkPaint::kRound_Cap;
  951. SkPoint roundCaps[2];
  952. if (fRoundCaps) {
  953. // Compute the cap center points in the normalized space.
  954. SkScalar midRadius = (innerRadius + outerRadius) / (2 * outerRadius);
  955. roundCaps[0] = startPoint * midRadius;
  956. roundCaps[1] = stopPoint * midRadius;
  957. } else {
  958. roundCaps[0] = kUnusedRoundCaps[0];
  959. roundCaps[1] = kUnusedRoundCaps[1];
  960. }
  961. // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against
  962. // radial lines. We treat round caps the same way, but tack coverage of circles at the
  963. // center of the butts.
  964. // However, in both cases we have to be careful about the half-circle.
  965. // case. In that case the two radial lines are equal and so that edge gets clipped
  966. // twice. Since the shared edge goes through the center we fall back on the !useCenter
  967. // case.
  968. auto absSweep = SkScalarAbs(arcParams->fSweepAngleRadians);
  969. bool useCenter = (arcParams->fUseCenter || isStrokeOnly) &&
  970. !SkScalarNearlyEqual(absSweep, SK_ScalarPI);
  971. if (useCenter) {
  972. SkVector norm0 = {startPoint.fY, -startPoint.fX};
  973. SkVector norm1 = {stopPoint.fY, -stopPoint.fX};
  974. // This ensures that norm0 is always the clockwise plane, and norm1 is CCW.
  975. if (arcParams->fSweepAngleRadians < 0) {
  976. std::swap(norm0, norm1);
  977. }
  978. norm0.negate();
  979. fClipPlane = true;
  980. if (absSweep > SK_ScalarPI) {
  981. fCircles.emplace_back(Circle{
  982. color,
  983. innerRadius,
  984. outerRadius,
  985. {norm0.fX, norm0.fY, 0.5f},
  986. {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
  987. {norm1.fX, norm1.fY, 0.5f},
  988. {roundCaps[0], roundCaps[1]},
  989. devBounds,
  990. stroked});
  991. fClipPlaneIsect = false;
  992. fClipPlaneUnion = true;
  993. } else {
  994. fCircles.emplace_back(Circle{
  995. color,
  996. innerRadius,
  997. outerRadius,
  998. {norm0.fX, norm0.fY, 0.5f},
  999. {norm1.fX, norm1.fY, 0.5f},
  1000. {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
  1001. {roundCaps[0], roundCaps[1]},
  1002. devBounds,
  1003. stroked});
  1004. fClipPlaneIsect = true;
  1005. fClipPlaneUnion = false;
  1006. }
  1007. } else {
  1008. // We clip to a secant of the original circle.
  1009. startPoint.scale(radius);
  1010. stopPoint.scale(radius);
  1011. SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX};
  1012. norm.normalize();
  1013. if (arcParams->fSweepAngleRadians > 0) {
  1014. norm.negate();
  1015. }
  1016. SkScalar d = -norm.dot(startPoint) + 0.5f;
  1017. fCircles.emplace_back(
  1018. Circle{color,
  1019. innerRadius,
  1020. outerRadius,
  1021. {norm.fX, norm.fY, d},
  1022. {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
  1023. {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
  1024. {roundCaps[0], roundCaps[1]},
  1025. devBounds,
  1026. stroked});
  1027. fClipPlane = true;
  1028. fClipPlaneIsect = false;
  1029. fClipPlaneUnion = false;
  1030. }
  1031. } else {
  1032. fCircles.emplace_back(
  1033. Circle{color,
  1034. innerRadius,
  1035. outerRadius,
  1036. {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
  1037. {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]},
  1038. {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]},
  1039. {kUnusedRoundCaps[0], kUnusedRoundCaps[1]},
  1040. devBounds,
  1041. stroked});
  1042. fClipPlane = false;
  1043. fClipPlaneIsect = false;
  1044. fClipPlaneUnion = false;
  1045. }
  1046. // Use the original radius and stroke radius for the bounds so that it does not include the
  1047. // AA bloat.
  1048. radius += halfWidth;
  1049. this->setBounds(
  1050. {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
  1051. HasAABloat::kYes, IsZeroArea::kNo);
  1052. fVertCount = circle_type_to_vert_count(stroked);
  1053. fIndexCount = circle_type_to_index_count(stroked);
  1054. fAllFill = !stroked;
  1055. }
  1056. const char* name() const override { return "CircleOp"; }
  1057. void visitProxies(const VisitProxyFunc& func) const override {
  1058. fHelper.visitProxies(func);
  1059. }
  1060. #ifdef SK_DEBUG
  1061. SkString dumpInfo() const override {
  1062. SkString string;
  1063. for (int i = 0; i < fCircles.count(); ++i) {
  1064. string.appendf(
  1065. "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
  1066. "InnerRad: %.2f, OuterRad: %.2f\n",
  1067. fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
  1068. fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
  1069. fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
  1070. fCircles[i].fOuterRadius);
  1071. }
  1072. string += fHelper.dumpInfo();
  1073. string += INHERITED::dumpInfo();
  1074. return string;
  1075. }
  1076. #endif
  1077. GrProcessorSet::Analysis finalize(
  1078. const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
  1079. GrClampType clampType) override {
  1080. SkPMColor4f* color = &fCircles.front().fColor;
  1081. return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
  1082. GrProcessorAnalysisCoverage::kSingleChannel, color,
  1083. &fWideColor);
  1084. }
  1085. FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
  1086. private:
  1087. void onPrepareDraws(Target* target) override {
  1088. SkMatrix localMatrix;
  1089. if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
  1090. return;
  1091. }
  1092. // Setup geometry processor
  1093. sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor(
  1094. !fAllFill, fClipPlane, fClipPlaneIsect, fClipPlaneUnion, fRoundCaps, fWideColor,
  1095. localMatrix));
  1096. sk_sp<const GrBuffer> vertexBuffer;
  1097. int firstVertex;
  1098. GrVertexWriter vertices{target->makeVertexSpace(gp->vertexStride(), fVertCount,
  1099. &vertexBuffer, &firstVertex)};
  1100. if (!vertices.fPtr) {
  1101. SkDebugf("Could not allocate vertices\n");
  1102. return;
  1103. }
  1104. sk_sp<const GrBuffer> indexBuffer = nullptr;
  1105. int firstIndex = 0;
  1106. uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
  1107. if (!indices) {
  1108. SkDebugf("Could not allocate indices\n");
  1109. return;
  1110. }
  1111. int currStartVertex = 0;
  1112. for (const auto& circle : fCircles) {
  1113. SkScalar innerRadius = circle.fInnerRadius;
  1114. SkScalar outerRadius = circle.fOuterRadius;
  1115. GrVertexColor color(circle.fColor, fWideColor);
  1116. const SkRect& bounds = circle.fDevBounds;
  1117. // The inner radius in the vertex data must be specified in normalized space.
  1118. innerRadius = innerRadius / outerRadius;
  1119. SkPoint radii = { outerRadius, innerRadius };
  1120. SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
  1121. SkScalar halfWidth = 0.5f * bounds.width();
  1122. SkVector geoClipPlane = { 0, 0 };
  1123. SkScalar offsetClipDist = SK_Scalar1;
  1124. if (!circle.fStroked && fClipPlane && fClipPlaneIsect &&
  1125. (circle.fClipPlane[0] * circle.fIsectPlane[0] +
  1126. circle.fClipPlane[1] * circle.fIsectPlane[1]) < 0.0f) {
  1127. // Acute arc. Clip the vertices to the perpendicular half-plane. We've constructed
  1128. // fClipPlane to be clockwise, and fISectPlane to be CCW, so we can can rotate them
  1129. // each 90 degrees to point "out", then average them. We back off by 1/2 pixel so
  1130. // the AA can extend just past the center of the circle.
  1131. geoClipPlane.set(circle.fClipPlane[1] - circle.fIsectPlane[1],
  1132. circle.fIsectPlane[0] - circle.fClipPlane[0]);
  1133. SkAssertResult(geoClipPlane.normalize());
  1134. offsetClipDist = 0.5f / halfWidth;
  1135. }
  1136. for (int i = 0; i < 8; ++i) {
  1137. // This clips the normalized offset to the half-plane we computed above. Then we
  1138. // compute the vertex position from this.
  1139. SkScalar dist = SkTMin(kOctagonOuter[i].dot(geoClipPlane) + offsetClipDist, 0.0f);
  1140. SkVector offset = kOctagonOuter[i] - geoClipPlane * dist;
  1141. vertices.write(center + offset * halfWidth,
  1142. color,
  1143. offset,
  1144. radii);
  1145. if (fClipPlane) {
  1146. vertices.write(circle.fClipPlane);
  1147. }
  1148. if (fClipPlaneIsect) {
  1149. vertices.write(circle.fIsectPlane);
  1150. }
  1151. if (fClipPlaneUnion) {
  1152. vertices.write(circle.fUnionPlane);
  1153. }
  1154. if (fRoundCaps) {
  1155. vertices.write(circle.fRoundCapCenters);
  1156. }
  1157. }
  1158. if (circle.fStroked) {
  1159. // compute the inner ring
  1160. for (int i = 0; i < 8; ++i) {
  1161. vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
  1162. color,
  1163. kOctagonInner[i] * innerRadius,
  1164. radii);
  1165. if (fClipPlane) {
  1166. vertices.write(circle.fClipPlane);
  1167. }
  1168. if (fClipPlaneIsect) {
  1169. vertices.write(circle.fIsectPlane);
  1170. }
  1171. if (fClipPlaneUnion) {
  1172. vertices.write(circle.fUnionPlane);
  1173. }
  1174. if (fRoundCaps) {
  1175. vertices.write(circle.fRoundCapCenters);
  1176. }
  1177. }
  1178. } else {
  1179. // filled
  1180. vertices.write(center, color, SkPoint::Make(0, 0), radii);
  1181. if (fClipPlane) {
  1182. vertices.write(circle.fClipPlane);
  1183. }
  1184. if (fClipPlaneIsect) {
  1185. vertices.write(circle.fIsectPlane);
  1186. }
  1187. if (fClipPlaneUnion) {
  1188. vertices.write(circle.fUnionPlane);
  1189. }
  1190. if (fRoundCaps) {
  1191. vertices.write(circle.fRoundCapCenters);
  1192. }
  1193. }
  1194. const uint16_t* primIndices = circle_type_to_indices(circle.fStroked);
  1195. const int primIndexCount = circle_type_to_index_count(circle.fStroked);
  1196. for (int i = 0; i < primIndexCount; ++i) {
  1197. *indices++ = primIndices[i] + currStartVertex;
  1198. }
  1199. currStartVertex += circle_type_to_vert_count(circle.fStroked);
  1200. }
  1201. GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
  1202. mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
  1203. GrPrimitiveRestart::kNo);
  1204. mesh->setVertexData(std::move(vertexBuffer), firstVertex);
  1205. target->recordDraw(std::move(gp), mesh);
  1206. }
  1207. void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
  1208. fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
  1209. }
  1210. CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
  1211. CircleOp* that = t->cast<CircleOp>();
  1212. // can only represent 65535 unique vertices with 16-bit indices
  1213. if (fVertCount + that->fVertCount > 65536) {
  1214. return CombineResult::kCannotCombine;
  1215. }
  1216. if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
  1217. return CombineResult::kCannotCombine;
  1218. }
  1219. if (fHelper.usesLocalCoords() &&
  1220. !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
  1221. return CombineResult::kCannotCombine;
  1222. }
  1223. // Because we've set up the ops that don't use the planes with noop values
  1224. // we can just accumulate used planes by later ops.
  1225. fClipPlane |= that->fClipPlane;
  1226. fClipPlaneIsect |= that->fClipPlaneIsect;
  1227. fClipPlaneUnion |= that->fClipPlaneUnion;
  1228. fRoundCaps |= that->fRoundCaps;
  1229. fWideColor |= that->fWideColor;
  1230. fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
  1231. fVertCount += that->fVertCount;
  1232. fIndexCount += that->fIndexCount;
  1233. fAllFill = fAllFill && that->fAllFill;
  1234. return CombineResult::kMerged;
  1235. }
  1236. struct Circle {
  1237. SkPMColor4f fColor;
  1238. SkScalar fInnerRadius;
  1239. SkScalar fOuterRadius;
  1240. SkScalar fClipPlane[3];
  1241. SkScalar fIsectPlane[3];
  1242. SkScalar fUnionPlane[3];
  1243. SkPoint fRoundCapCenters[2];
  1244. SkRect fDevBounds;
  1245. bool fStroked;
  1246. };
  1247. SkMatrix fViewMatrixIfUsingLocalCoords;
  1248. Helper fHelper;
  1249. SkSTArray<1, Circle, true> fCircles;
  1250. int fVertCount;
  1251. int fIndexCount;
  1252. bool fAllFill;
  1253. bool fClipPlane;
  1254. bool fClipPlaneIsect;
  1255. bool fClipPlaneUnion;
  1256. bool fRoundCaps;
  1257. bool fWideColor;
  1258. typedef GrMeshDrawOp INHERITED;
  1259. };
  1260. class ButtCapDashedCircleOp final : public GrMeshDrawOp {
  1261. private:
  1262. using Helper = GrSimpleMeshDrawOpHelper;
  1263. public:
  1264. DEFINE_OP_CLASS_ID
  1265. static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
  1266. GrPaint&& paint,
  1267. const SkMatrix& viewMatrix,
  1268. SkPoint center,
  1269. SkScalar radius,
  1270. SkScalar strokeWidth,
  1271. SkScalar startAngle,
  1272. SkScalar onAngle,
  1273. SkScalar offAngle,
  1274. SkScalar phaseAngle) {
  1275. SkASSERT(circle_stays_circle(viewMatrix));
  1276. SkASSERT(strokeWidth < 2 * radius);
  1277. return Helper::FactoryHelper<ButtCapDashedCircleOp>(context, std::move(paint), viewMatrix,
  1278. center, radius, strokeWidth, startAngle,
  1279. onAngle, offAngle, phaseAngle);
  1280. }
  1281. ButtCapDashedCircleOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
  1282. const SkMatrix& viewMatrix, SkPoint center, SkScalar radius,
  1283. SkScalar strokeWidth, SkScalar startAngle, SkScalar onAngle,
  1284. SkScalar offAngle, SkScalar phaseAngle)
  1285. : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) {
  1286. SkASSERT(circle_stays_circle(viewMatrix));
  1287. viewMatrix.mapPoints(&center, 1);
  1288. radius = viewMatrix.mapRadius(radius);
  1289. strokeWidth = viewMatrix.mapRadius(strokeWidth);
  1290. // Determine the angle where the circle starts in device space and whether its orientation
  1291. // has been reversed.
  1292. SkVector start;
  1293. bool reflection;
  1294. if (!startAngle) {
  1295. start = {1, 0};
  1296. } else {
  1297. start.fY = SkScalarSin(startAngle);
  1298. start.fX = SkScalarCos(startAngle);
  1299. }
  1300. viewMatrix.mapVectors(&start, 1);
  1301. startAngle = SkScalarATan2(start.fY, start.fX);
  1302. reflection = (viewMatrix.getScaleX() * viewMatrix.getScaleY() -
  1303. viewMatrix.getSkewX() * viewMatrix.getSkewY()) < 0;
  1304. auto totalAngle = onAngle + offAngle;
  1305. phaseAngle = SkScalarMod(phaseAngle + totalAngle / 2, totalAngle) - totalAngle / 2;
  1306. SkScalar halfWidth = 0;
  1307. if (SkScalarNearlyZero(strokeWidth)) {
  1308. halfWidth = SK_ScalarHalf;
  1309. } else {
  1310. halfWidth = SkScalarHalf(strokeWidth);
  1311. }
  1312. SkScalar outerRadius = radius + halfWidth;
  1313. SkScalar innerRadius = radius - halfWidth;
  1314. // The radii are outset for two reasons. First, it allows the shader to simply perform
  1315. // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
  1316. // Second, the outer radius is used to compute the verts of the bounding box that is
  1317. // rendered and the outset ensures the box will cover all partially covered by the circle.
  1318. outerRadius += SK_ScalarHalf;
  1319. innerRadius -= SK_ScalarHalf;
  1320. fViewMatrixIfUsingLocalCoords = viewMatrix;
  1321. SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius,
  1322. center.fX + outerRadius, center.fY + outerRadius);
  1323. // We store whether there is a reflection as a negative total angle.
  1324. if (reflection) {
  1325. totalAngle = -totalAngle;
  1326. }
  1327. fCircles.push_back(Circle{
  1328. color,
  1329. outerRadius,
  1330. innerRadius,
  1331. onAngle,
  1332. totalAngle,
  1333. startAngle,
  1334. phaseAngle,
  1335. devBounds
  1336. });
  1337. // Use the original radius and stroke radius for the bounds so that it does not include the
  1338. // AA bloat.
  1339. radius += halfWidth;
  1340. this->setBounds(
  1341. {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius},
  1342. HasAABloat::kYes, IsZeroArea::kNo);
  1343. fVertCount = circle_type_to_vert_count(true);
  1344. fIndexCount = circle_type_to_index_count(true);
  1345. }
  1346. const char* name() const override { return "ButtCappedDashedCircleOp"; }
  1347. void visitProxies(const VisitProxyFunc& func) const override {
  1348. fHelper.visitProxies(func);
  1349. }
  1350. #ifdef SK_DEBUG
  1351. SkString dumpInfo() const override {
  1352. SkString string;
  1353. for (int i = 0; i < fCircles.count(); ++i) {
  1354. string.appendf(
  1355. "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
  1356. "InnerRad: %.2f, OuterRad: %.2f, OnAngle: %.2f, TotalAngle: %.2f, "
  1357. "Phase: %.2f\n",
  1358. fCircles[i].fColor.toBytes_RGBA(), fCircles[i].fDevBounds.fLeft,
  1359. fCircles[i].fDevBounds.fTop, fCircles[i].fDevBounds.fRight,
  1360. fCircles[i].fDevBounds.fBottom, fCircles[i].fInnerRadius,
  1361. fCircles[i].fOuterRadius, fCircles[i].fOnAngle, fCircles[i].fTotalAngle,
  1362. fCircles[i].fPhaseAngle);
  1363. }
  1364. string += fHelper.dumpInfo();
  1365. string += INHERITED::dumpInfo();
  1366. return string;
  1367. }
  1368. #endif
  1369. GrProcessorSet::Analysis finalize(
  1370. const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
  1371. GrClampType clampType) override {
  1372. SkPMColor4f* color = &fCircles.front().fColor;
  1373. return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
  1374. GrProcessorAnalysisCoverage::kSingleChannel, color,
  1375. &fWideColor);
  1376. }
  1377. FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
  1378. private:
  1379. void onPrepareDraws(Target* target) override {
  1380. SkMatrix localMatrix;
  1381. if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
  1382. return;
  1383. }
  1384. // Setup geometry processor
  1385. sk_sp<GrGeometryProcessor> gp(new ButtCapDashedCircleGeometryProcessor(fWideColor,
  1386. localMatrix));
  1387. sk_sp<const GrBuffer> vertexBuffer;
  1388. int firstVertex;
  1389. GrVertexWriter vertices{target->makeVertexSpace(gp->vertexStride(), fVertCount,
  1390. &vertexBuffer, &firstVertex)};
  1391. if (!vertices.fPtr) {
  1392. SkDebugf("Could not allocate vertices\n");
  1393. return;
  1394. }
  1395. sk_sp<const GrBuffer> indexBuffer;
  1396. int firstIndex = 0;
  1397. uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
  1398. if (!indices) {
  1399. SkDebugf("Could not allocate indices\n");
  1400. return;
  1401. }
  1402. int currStartVertex = 0;
  1403. for (const auto& circle : fCircles) {
  1404. // The inner radius in the vertex data must be specified in normalized space so that
  1405. // length() can be called with smaller values to avoid precision issues with half
  1406. // floats.
  1407. auto normInnerRadius = circle.fInnerRadius / circle.fOuterRadius;
  1408. const SkRect& bounds = circle.fDevBounds;
  1409. bool reflect = false;
  1410. struct { float onAngle, totalAngle, startAngle, phaseAngle; } dashParams = {
  1411. circle.fOnAngle, circle.fTotalAngle, circle.fStartAngle, circle.fPhaseAngle
  1412. };
  1413. if (dashParams.totalAngle < 0) {
  1414. reflect = true;
  1415. dashParams.totalAngle = -dashParams.totalAngle;
  1416. dashParams.startAngle = -dashParams.startAngle;
  1417. }
  1418. GrVertexColor color(circle.fColor, fWideColor);
  1419. // The bounding geometry for the circle is composed of an outer bounding octagon and
  1420. // an inner bounded octagon.
  1421. // Compute the vertices of the outer octagon.
  1422. SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY());
  1423. SkScalar halfWidth = 0.5f * bounds.width();
  1424. auto reflectY = [=](const SkPoint& p) {
  1425. return SkPoint{ p.fX, reflect ? -p.fY : p.fY };
  1426. };
  1427. for (int i = 0; i < 8; ++i) {
  1428. vertices.write(center + kOctagonOuter[i] * halfWidth,
  1429. color,
  1430. reflectY(kOctagonOuter[i]),
  1431. circle.fOuterRadius,
  1432. normInnerRadius,
  1433. dashParams);
  1434. }
  1435. // Compute the vertices of the inner octagon.
  1436. for (int i = 0; i < 8; ++i) {
  1437. vertices.write(center + kOctagonInner[i] * circle.fInnerRadius,
  1438. color,
  1439. reflectY(kOctagonInner[i]) * normInnerRadius,
  1440. circle.fOuterRadius,
  1441. normInnerRadius,
  1442. dashParams);
  1443. }
  1444. const uint16_t* primIndices = circle_type_to_indices(true);
  1445. const int primIndexCount = circle_type_to_index_count(true);
  1446. for (int i = 0; i < primIndexCount; ++i) {
  1447. *indices++ = primIndices[i] + currStartVertex;
  1448. }
  1449. currStartVertex += circle_type_to_vert_count(true);
  1450. }
  1451. GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
  1452. mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
  1453. GrPrimitiveRestart::kNo);
  1454. mesh->setVertexData(std::move(vertexBuffer), firstVertex);
  1455. target->recordDraw(std::move(gp), mesh);
  1456. }
  1457. void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
  1458. fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
  1459. }
  1460. CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
  1461. ButtCapDashedCircleOp* that = t->cast<ButtCapDashedCircleOp>();
  1462. // can only represent 65535 unique vertices with 16-bit indices
  1463. if (fVertCount + that->fVertCount > 65536) {
  1464. return CombineResult::kCannotCombine;
  1465. }
  1466. if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
  1467. return CombineResult::kCannotCombine;
  1468. }
  1469. if (fHelper.usesLocalCoords() &&
  1470. !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
  1471. return CombineResult::kCannotCombine;
  1472. }
  1473. fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin());
  1474. fVertCount += that->fVertCount;
  1475. fIndexCount += that->fIndexCount;
  1476. fWideColor |= that->fWideColor;
  1477. return CombineResult::kMerged;
  1478. }
  1479. struct Circle {
  1480. SkPMColor4f fColor;
  1481. SkScalar fOuterRadius;
  1482. SkScalar fInnerRadius;
  1483. SkScalar fOnAngle;
  1484. SkScalar fTotalAngle;
  1485. SkScalar fStartAngle;
  1486. SkScalar fPhaseAngle;
  1487. SkRect fDevBounds;
  1488. };
  1489. SkMatrix fViewMatrixIfUsingLocalCoords;
  1490. Helper fHelper;
  1491. SkSTArray<1, Circle, true> fCircles;
  1492. int fVertCount;
  1493. int fIndexCount;
  1494. bool fWideColor;
  1495. typedef GrMeshDrawOp INHERITED;
  1496. };
  1497. ///////////////////////////////////////////////////////////////////////////////
  1498. class EllipseOp : public GrMeshDrawOp {
  1499. private:
  1500. using Helper = GrSimpleMeshDrawOpHelper;
  1501. struct DeviceSpaceParams {
  1502. SkPoint fCenter;
  1503. SkScalar fXRadius;
  1504. SkScalar fYRadius;
  1505. SkScalar fInnerXRadius;
  1506. SkScalar fInnerYRadius;
  1507. };
  1508. public:
  1509. DEFINE_OP_CLASS_ID
  1510. static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
  1511. GrPaint&& paint,
  1512. const SkMatrix& viewMatrix,
  1513. const SkRect& ellipse,
  1514. const SkStrokeRec& stroke) {
  1515. DeviceSpaceParams params;
  1516. // do any matrix crunching before we reset the draw state for device coords
  1517. params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
  1518. viewMatrix.mapPoints(&params.fCenter, 1);
  1519. SkScalar ellipseXRadius = SkScalarHalf(ellipse.width());
  1520. SkScalar ellipseYRadius = SkScalarHalf(ellipse.height());
  1521. params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius +
  1522. viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius);
  1523. params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius +
  1524. viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius);
  1525. // do (potentially) anisotropic mapping of stroke
  1526. SkVector scaledStroke;
  1527. SkScalar strokeWidth = stroke.getWidth();
  1528. scaledStroke.fX = SkScalarAbs(
  1529. strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
  1530. scaledStroke.fY = SkScalarAbs(
  1531. strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
  1532. SkStrokeRec::Style style = stroke.getStyle();
  1533. bool isStrokeOnly =
  1534. SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
  1535. bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
  1536. params.fInnerXRadius = 0;
  1537. params.fInnerYRadius = 0;
  1538. if (hasStroke) {
  1539. if (SkScalarNearlyZero(scaledStroke.length())) {
  1540. scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf);
  1541. } else {
  1542. scaledStroke.scale(SK_ScalarHalf);
  1543. }
  1544. // we only handle thick strokes for near-circular ellipses
  1545. if (scaledStroke.length() > SK_ScalarHalf &&
  1546. (0.5f * params.fXRadius > params.fYRadius ||
  1547. 0.5f * params.fYRadius > params.fXRadius)) {
  1548. return nullptr;
  1549. }
  1550. // we don't handle it if curvature of the stroke is less than curvature of the ellipse
  1551. if (scaledStroke.fX * (params.fXRadius * params.fYRadius) <
  1552. (scaledStroke.fY * scaledStroke.fY) * params.fXRadius ||
  1553. scaledStroke.fY * (params.fXRadius * params.fXRadius) <
  1554. (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) {
  1555. return nullptr;
  1556. }
  1557. // this is legit only if scale & translation (which should be the case at the moment)
  1558. if (isStrokeOnly) {
  1559. params.fInnerXRadius = params.fXRadius - scaledStroke.fX;
  1560. params.fInnerYRadius = params.fYRadius - scaledStroke.fY;
  1561. }
  1562. params.fXRadius += scaledStroke.fX;
  1563. params.fYRadius += scaledStroke.fY;
  1564. }
  1565. // For large ovals with low precision floats, we fall back to the path renderer.
  1566. // To compute the AA at the edge we divide by the gradient, which is clamped to a
  1567. // minimum value to avoid divides by zero. With large ovals and low precision this
  1568. // leads to blurring at the edge of the oval.
  1569. const SkScalar kMaxOvalRadius = 16384;
  1570. if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
  1571. (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
  1572. return nullptr;
  1573. }
  1574. return Helper::FactoryHelper<EllipseOp>(context, std::move(paint), viewMatrix,
  1575. params, stroke);
  1576. }
  1577. EllipseOp(const Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
  1578. const SkMatrix& viewMatrix, const DeviceSpaceParams& params,
  1579. const SkStrokeRec& stroke)
  1580. : INHERITED(ClassID())
  1581. , fHelper(helperArgs, GrAAType::kCoverage)
  1582. , fUseScale(false) {
  1583. SkStrokeRec::Style style = stroke.getStyle();
  1584. bool isStrokeOnly =
  1585. SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
  1586. fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius,
  1587. params.fInnerXRadius, params.fInnerYRadius,
  1588. SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius,
  1589. params.fCenter.fY - params.fYRadius,
  1590. params.fCenter.fX + params.fXRadius,
  1591. params.fCenter.fY + params.fYRadius)});
  1592. this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo);
  1593. // Outset bounds to include half-pixel width antialiasing.
  1594. fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf);
  1595. fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0;
  1596. fViewMatrixIfUsingLocalCoords = viewMatrix;
  1597. }
  1598. const char* name() const override { return "EllipseOp"; }
  1599. void visitProxies(const VisitProxyFunc& func) const override {
  1600. fHelper.visitProxies(func);
  1601. }
  1602. #ifdef SK_DEBUG
  1603. SkString dumpInfo() const override {
  1604. SkString string;
  1605. string.appendf("Stroked: %d\n", fStroked);
  1606. for (const auto& geo : fEllipses) {
  1607. string.appendf(
  1608. "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
  1609. "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
  1610. geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
  1611. geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
  1612. geo.fInnerXRadius, geo.fInnerYRadius);
  1613. }
  1614. string += fHelper.dumpInfo();
  1615. string += INHERITED::dumpInfo();
  1616. return string;
  1617. }
  1618. #endif
  1619. GrProcessorSet::Analysis finalize(
  1620. const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
  1621. GrClampType clampType) override {
  1622. fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
  1623. !caps.shaderCaps()->hasLowFragmentPrecision();
  1624. SkPMColor4f* color = &fEllipses.front().fColor;
  1625. return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
  1626. GrProcessorAnalysisCoverage::kSingleChannel, color,
  1627. &fWideColor);
  1628. }
  1629. FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
  1630. private:
  1631. void onPrepareDraws(Target* target) override {
  1632. SkMatrix localMatrix;
  1633. if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
  1634. return;
  1635. }
  1636. // Setup geometry processor
  1637. sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, fWideColor, fUseScale,
  1638. localMatrix));
  1639. QuadHelper helper(target, gp->vertexStride(), fEllipses.count());
  1640. GrVertexWriter verts{helper.vertices()};
  1641. if (!verts.fPtr) {
  1642. return;
  1643. }
  1644. for (const auto& ellipse : fEllipses) {
  1645. GrVertexColor color(ellipse.fColor, fWideColor);
  1646. SkScalar xRadius = ellipse.fXRadius;
  1647. SkScalar yRadius = ellipse.fYRadius;
  1648. // Compute the reciprocals of the radii here to save time in the shader
  1649. struct { float xOuter, yOuter, xInner, yInner; } invRadii = {
  1650. SkScalarInvert(xRadius),
  1651. SkScalarInvert(yRadius),
  1652. SkScalarInvert(ellipse.fInnerXRadius),
  1653. SkScalarInvert(ellipse.fInnerYRadius)
  1654. };
  1655. SkScalar xMaxOffset = xRadius + SK_ScalarHalf;
  1656. SkScalar yMaxOffset = yRadius + SK_ScalarHalf;
  1657. if (!fStroked) {
  1658. // For filled ellipses we map a unit circle in the vertex attributes rather than
  1659. // computing an ellipse and modifying that distance, so we normalize to 1
  1660. xMaxOffset /= xRadius;
  1661. yMaxOffset /= yRadius;
  1662. }
  1663. // The inner radius in the vertex data must be specified in normalized space.
  1664. verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fDevBounds),
  1665. color,
  1666. origin_centered_tri_strip(xMaxOffset, yMaxOffset),
  1667. GrVertexWriter::If(fUseScale, SkTMax(xRadius, yRadius)),
  1668. invRadii);
  1669. }
  1670. helper.recordDraw(target, std::move(gp));
  1671. }
  1672. void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
  1673. fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
  1674. }
  1675. CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
  1676. EllipseOp* that = t->cast<EllipseOp>();
  1677. if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
  1678. return CombineResult::kCannotCombine;
  1679. }
  1680. if (fStroked != that->fStroked) {
  1681. return CombineResult::kCannotCombine;
  1682. }
  1683. if (fHelper.usesLocalCoords() &&
  1684. !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
  1685. return CombineResult::kCannotCombine;
  1686. }
  1687. fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
  1688. fWideColor |= that->fWideColor;
  1689. return CombineResult::kMerged;
  1690. }
  1691. struct Ellipse {
  1692. SkPMColor4f fColor;
  1693. SkScalar fXRadius;
  1694. SkScalar fYRadius;
  1695. SkScalar fInnerXRadius;
  1696. SkScalar fInnerYRadius;
  1697. SkRect fDevBounds;
  1698. };
  1699. SkMatrix fViewMatrixIfUsingLocalCoords;
  1700. Helper fHelper;
  1701. bool fStroked;
  1702. bool fWideColor;
  1703. bool fUseScale;
  1704. SkSTArray<1, Ellipse, true> fEllipses;
  1705. typedef GrMeshDrawOp INHERITED;
  1706. };
  1707. /////////////////////////////////////////////////////////////////////////////////////////////////
  1708. class DIEllipseOp : public GrMeshDrawOp {
  1709. private:
  1710. using Helper = GrSimpleMeshDrawOpHelper;
  1711. struct DeviceSpaceParams {
  1712. SkPoint fCenter;
  1713. SkScalar fXRadius;
  1714. SkScalar fYRadius;
  1715. SkScalar fInnerXRadius;
  1716. SkScalar fInnerYRadius;
  1717. DIEllipseStyle fStyle;
  1718. };
  1719. public:
  1720. DEFINE_OP_CLASS_ID
  1721. static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
  1722. GrPaint&& paint,
  1723. const SkMatrix& viewMatrix,
  1724. const SkRect& ellipse,
  1725. const SkStrokeRec& stroke) {
  1726. DeviceSpaceParams params;
  1727. params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY());
  1728. params.fXRadius = SkScalarHalf(ellipse.width());
  1729. params.fYRadius = SkScalarHalf(ellipse.height());
  1730. SkStrokeRec::Style style = stroke.getStyle();
  1731. params.fStyle = (SkStrokeRec::kStroke_Style == style)
  1732. ? DIEllipseStyle::kStroke
  1733. : (SkStrokeRec::kHairline_Style == style)
  1734. ? DIEllipseStyle::kHairline
  1735. : DIEllipseStyle::kFill;
  1736. params.fInnerXRadius = 0;
  1737. params.fInnerYRadius = 0;
  1738. if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) {
  1739. SkScalar strokeWidth = stroke.getWidth();
  1740. if (SkScalarNearlyZero(strokeWidth)) {
  1741. strokeWidth = SK_ScalarHalf;
  1742. } else {
  1743. strokeWidth *= SK_ScalarHalf;
  1744. }
  1745. // we only handle thick strokes for near-circular ellipses
  1746. if (strokeWidth > SK_ScalarHalf &&
  1747. (SK_ScalarHalf * params.fXRadius > params.fYRadius ||
  1748. SK_ScalarHalf * params.fYRadius > params.fXRadius)) {
  1749. return nullptr;
  1750. }
  1751. // we don't handle it if curvature of the stroke is less than curvature of the ellipse
  1752. if (strokeWidth * (params.fYRadius * params.fYRadius) <
  1753. (strokeWidth * strokeWidth) * params.fXRadius) {
  1754. return nullptr;
  1755. }
  1756. if (strokeWidth * (params.fXRadius * params.fXRadius) <
  1757. (strokeWidth * strokeWidth) * params.fYRadius) {
  1758. return nullptr;
  1759. }
  1760. // set inner radius (if needed)
  1761. if (SkStrokeRec::kStroke_Style == style) {
  1762. params.fInnerXRadius = params.fXRadius - strokeWidth;
  1763. params.fInnerYRadius = params.fYRadius - strokeWidth;
  1764. }
  1765. params.fXRadius += strokeWidth;
  1766. params.fYRadius += strokeWidth;
  1767. }
  1768. // For large ovals with low precision floats, we fall back to the path renderer.
  1769. // To compute the AA at the edge we divide by the gradient, which is clamped to a
  1770. // minimum value to avoid divides by zero. With large ovals and low precision this
  1771. // leads to blurring at the edge of the oval.
  1772. const SkScalar kMaxOvalRadius = 16384;
  1773. if (!context->priv().caps()->shaderCaps()->floatIs32Bits() &&
  1774. (params.fXRadius >= kMaxOvalRadius || params.fYRadius >= kMaxOvalRadius)) {
  1775. return nullptr;
  1776. }
  1777. if (DIEllipseStyle::kStroke == params.fStyle &&
  1778. (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) {
  1779. params.fStyle = DIEllipseStyle::kFill;
  1780. }
  1781. return Helper::FactoryHelper<DIEllipseOp>(context, std::move(paint), params, viewMatrix);
  1782. }
  1783. DIEllipseOp(Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
  1784. const DeviceSpaceParams& params, const SkMatrix& viewMatrix)
  1785. : INHERITED(ClassID())
  1786. , fHelper(helperArgs, GrAAType::kCoverage)
  1787. , fUseScale(false) {
  1788. // This expands the outer rect so that after CTM we end up with a half-pixel border
  1789. SkScalar a = viewMatrix[SkMatrix::kMScaleX];
  1790. SkScalar b = viewMatrix[SkMatrix::kMSkewX];
  1791. SkScalar c = viewMatrix[SkMatrix::kMSkewY];
  1792. SkScalar d = viewMatrix[SkMatrix::kMScaleY];
  1793. SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c);
  1794. SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d);
  1795. fEllipses.emplace_back(
  1796. Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius,
  1797. params.fInnerYRadius, geoDx, geoDy, params.fStyle,
  1798. SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx,
  1799. params.fCenter.fY - params.fYRadius - geoDy,
  1800. params.fCenter.fX + params.fXRadius + geoDx,
  1801. params.fCenter.fY + params.fYRadius + geoDy)});
  1802. this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes,
  1803. IsZeroArea::kNo);
  1804. }
  1805. const char* name() const override { return "DIEllipseOp"; }
  1806. void visitProxies(const VisitProxyFunc& func) const override {
  1807. fHelper.visitProxies(func);
  1808. }
  1809. #ifdef SK_DEBUG
  1810. SkString dumpInfo() const override {
  1811. SkString string;
  1812. for (const auto& geo : fEllipses) {
  1813. string.appendf(
  1814. "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, "
  1815. "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, "
  1816. "GeoDY: %.2f\n",
  1817. geo.fColor.toBytes_RGBA(), geo.fBounds.fLeft, geo.fBounds.fTop,
  1818. geo.fBounds.fRight, geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius,
  1819. geo.fInnerXRadius, geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy);
  1820. }
  1821. string += fHelper.dumpInfo();
  1822. string += INHERITED::dumpInfo();
  1823. return string;
  1824. }
  1825. #endif
  1826. GrProcessorSet::Analysis finalize(
  1827. const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
  1828. GrClampType clampType) override {
  1829. fUseScale = !caps.shaderCaps()->floatIs32Bits() &&
  1830. !caps.shaderCaps()->hasLowFragmentPrecision();
  1831. SkPMColor4f* color = &fEllipses.front().fColor;
  1832. return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
  1833. GrProcessorAnalysisCoverage::kSingleChannel, color,
  1834. &fWideColor);
  1835. }
  1836. FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
  1837. private:
  1838. void onPrepareDraws(Target* target) override {
  1839. // Setup geometry processor
  1840. sk_sp<GrGeometryProcessor> gp(
  1841. new DIEllipseGeometryProcessor(fWideColor, fUseScale, this->viewMatrix(),
  1842. this->style()));
  1843. QuadHelper helper(target, gp->vertexStride(), fEllipses.count());
  1844. GrVertexWriter verts{helper.vertices()};
  1845. if (!verts.fPtr) {
  1846. return;
  1847. }
  1848. for (const auto& ellipse : fEllipses) {
  1849. GrVertexColor color(ellipse.fColor, fWideColor);
  1850. SkScalar xRadius = ellipse.fXRadius;
  1851. SkScalar yRadius = ellipse.fYRadius;
  1852. // This adjusts the "radius" to include the half-pixel border
  1853. SkScalar offsetDx = ellipse.fGeoDx / xRadius;
  1854. SkScalar offsetDy = ellipse.fGeoDy / yRadius;
  1855. // By default, constructed so that inner offset is (0, 0) for all points
  1856. SkScalar innerRatioX = -offsetDx;
  1857. SkScalar innerRatioY = -offsetDy;
  1858. // ... unless we're stroked
  1859. if (DIEllipseStyle::kStroke == this->style()) {
  1860. innerRatioX = xRadius / ellipse.fInnerXRadius;
  1861. innerRatioY = yRadius / ellipse.fInnerYRadius;
  1862. }
  1863. verts.writeQuad(GrVertexWriter::TriStripFromRect(ellipse.fBounds),
  1864. color,
  1865. origin_centered_tri_strip(1.0f + offsetDx, 1.0f + offsetDy),
  1866. GrVertexWriter::If(fUseScale, SkTMax(xRadius, yRadius)),
  1867. origin_centered_tri_strip(innerRatioX + offsetDx,
  1868. innerRatioY + offsetDy));
  1869. }
  1870. helper.recordDraw(target, std::move(gp));
  1871. }
  1872. void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
  1873. fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
  1874. }
  1875. CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
  1876. DIEllipseOp* that = t->cast<DIEllipseOp>();
  1877. if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
  1878. return CombineResult::kCannotCombine;
  1879. }
  1880. if (this->style() != that->style()) {
  1881. return CombineResult::kCannotCombine;
  1882. }
  1883. // TODO rewrite to allow positioning on CPU
  1884. if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
  1885. return CombineResult::kCannotCombine;
  1886. }
  1887. fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin());
  1888. fWideColor |= that->fWideColor;
  1889. return CombineResult::kMerged;
  1890. }
  1891. const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; }
  1892. DIEllipseStyle style() const { return fEllipses[0].fStyle; }
  1893. struct Ellipse {
  1894. SkMatrix fViewMatrix;
  1895. SkPMColor4f fColor;
  1896. SkScalar fXRadius;
  1897. SkScalar fYRadius;
  1898. SkScalar fInnerXRadius;
  1899. SkScalar fInnerYRadius;
  1900. SkScalar fGeoDx;
  1901. SkScalar fGeoDy;
  1902. DIEllipseStyle fStyle;
  1903. SkRect fBounds;
  1904. };
  1905. Helper fHelper;
  1906. bool fWideColor;
  1907. bool fUseScale;
  1908. SkSTArray<1, Ellipse, true> fEllipses;
  1909. typedef GrMeshDrawOp INHERITED;
  1910. };
  1911. ///////////////////////////////////////////////////////////////////////////////
  1912. // We have three possible cases for geometry for a roundrect.
  1913. //
  1914. // In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch:
  1915. // ____________
  1916. // |_|________|_|
  1917. // | | | |
  1918. // | | | |
  1919. // | | | |
  1920. // |_|________|_|
  1921. // |_|________|_|
  1922. //
  1923. // For strokes, we don't draw the center quad.
  1924. //
  1925. // For circular roundrects, in the case where the stroke width is greater than twice
  1926. // the corner radius (overstroke), we add additional geometry to mark out the rectangle
  1927. // in the center. The shared vertices are duplicated so we can set a different outer radius
  1928. // for the fill calculation.
  1929. // ____________
  1930. // |_|________|_|
  1931. // | |\ ____ /| |
  1932. // | | | | | |
  1933. // | | |____| | |
  1934. // |_|/______\|_|
  1935. // |_|________|_|
  1936. //
  1937. // We don't draw the center quad from the fill rect in this case.
  1938. //
  1939. // For filled rrects that need to provide a distance vector we resuse the overstroke
  1940. // geometry but make the inner rect degenerate (either a point or a horizontal or
  1941. // vertical line).
  1942. static const uint16_t gOverstrokeRRectIndices[] = {
  1943. // clang-format off
  1944. // overstroke quads
  1945. // we place this at the beginning so that we can skip these indices when rendering normally
  1946. 16, 17, 19, 16, 19, 18,
  1947. 19, 17, 23, 19, 23, 21,
  1948. 21, 23, 22, 21, 22, 20,
  1949. 22, 16, 18, 22, 18, 20,
  1950. // corners
  1951. 0, 1, 5, 0, 5, 4,
  1952. 2, 3, 7, 2, 7, 6,
  1953. 8, 9, 13, 8, 13, 12,
  1954. 10, 11, 15, 10, 15, 14,
  1955. // edges
  1956. 1, 2, 6, 1, 6, 5,
  1957. 4, 5, 9, 4, 9, 8,
  1958. 6, 7, 11, 6, 11, 10,
  1959. 9, 10, 14, 9, 14, 13,
  1960. // center
  1961. // we place this at the end so that we can ignore these indices when not rendering as filled
  1962. 5, 6, 10, 5, 10, 9,
  1963. // clang-format on
  1964. };
  1965. // fill and standard stroke indices skip the overstroke "ring"
  1966. static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4;
  1967. // overstroke count is arraysize minus the center indices
  1968. static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6;
  1969. // fill count skips overstroke indices and includes center
  1970. static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6;
  1971. // stroke count is fill count minus center indices
  1972. static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6;
  1973. static const int kVertsPerStandardRRect = 16;
  1974. static const int kVertsPerOverstrokeRRect = 24;
  1975. enum RRectType {
  1976. kFill_RRectType,
  1977. kStroke_RRectType,
  1978. kOverstroke_RRectType,
  1979. };
  1980. static int rrect_type_to_vert_count(RRectType type) {
  1981. switch (type) {
  1982. case kFill_RRectType:
  1983. case kStroke_RRectType:
  1984. return kVertsPerStandardRRect;
  1985. case kOverstroke_RRectType:
  1986. return kVertsPerOverstrokeRRect;
  1987. }
  1988. SK_ABORT("Invalid type");
  1989. return 0;
  1990. }
  1991. static int rrect_type_to_index_count(RRectType type) {
  1992. switch (type) {
  1993. case kFill_RRectType:
  1994. return kIndicesPerFillRRect;
  1995. case kStroke_RRectType:
  1996. return kIndicesPerStrokeRRect;
  1997. case kOverstroke_RRectType:
  1998. return kIndicesPerOverstrokeRRect;
  1999. }
  2000. SK_ABORT("Invalid type");
  2001. return 0;
  2002. }
  2003. static const uint16_t* rrect_type_to_indices(RRectType type) {
  2004. switch (type) {
  2005. case kFill_RRectType:
  2006. case kStroke_RRectType:
  2007. return gStandardRRectIndices;
  2008. case kOverstroke_RRectType:
  2009. return gOverstrokeRRectIndices;
  2010. }
  2011. SK_ABORT("Invalid type");
  2012. return 0;
  2013. }
  2014. ///////////////////////////////////////////////////////////////////////////////////////////////////
  2015. // For distance computations in the interior of filled rrects we:
  2016. //
  2017. // add a interior degenerate (point or line) rect
  2018. // each vertex of that rect gets -outerRad as its radius
  2019. // this makes the computation of the distance to the outer edge be negative
  2020. // negative values are caught and then handled differently in the GP's onEmitCode
  2021. // each vertex is also given the normalized x & y distance from the interior rect's edge
  2022. // the GP takes the min of those depths +1 to get the normalized distance to the outer edge
  2023. class CircularRRectOp : public GrMeshDrawOp {
  2024. private:
  2025. using Helper = GrSimpleMeshDrawOpHelper;
  2026. public:
  2027. DEFINE_OP_CLASS_ID
  2028. // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates
  2029. // whether the rrect is only stroked or stroked and filled.
  2030. static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
  2031. GrPaint&& paint,
  2032. const SkMatrix& viewMatrix,
  2033. const SkRect& devRect,
  2034. float devRadius,
  2035. float devStrokeWidth,
  2036. bool strokeOnly) {
  2037. return Helper::FactoryHelper<CircularRRectOp>(context, std::move(paint), viewMatrix,
  2038. devRect, devRadius,
  2039. devStrokeWidth, strokeOnly);
  2040. }
  2041. CircularRRectOp(Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
  2042. const SkMatrix& viewMatrix, const SkRect& devRect, float devRadius,
  2043. float devStrokeWidth, bool strokeOnly)
  2044. : INHERITED(ClassID())
  2045. , fViewMatrixIfUsingLocalCoords(viewMatrix)
  2046. , fHelper(helperArgs, GrAAType::kCoverage) {
  2047. SkRect bounds = devRect;
  2048. SkASSERT(!(devStrokeWidth <= 0 && strokeOnly));
  2049. SkScalar innerRadius = 0.0f;
  2050. SkScalar outerRadius = devRadius;
  2051. SkScalar halfWidth = 0;
  2052. RRectType type = kFill_RRectType;
  2053. if (devStrokeWidth > 0) {
  2054. if (SkScalarNearlyZero(devStrokeWidth)) {
  2055. halfWidth = SK_ScalarHalf;
  2056. } else {
  2057. halfWidth = SkScalarHalf(devStrokeWidth);
  2058. }
  2059. if (strokeOnly) {
  2060. // Outset stroke by 1/4 pixel
  2061. devStrokeWidth += 0.25f;
  2062. // If stroke is greater than width or height, this is still a fill
  2063. // Otherwise we compute stroke params
  2064. if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) {
  2065. innerRadius = devRadius - halfWidth;
  2066. type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType;
  2067. }
  2068. }
  2069. outerRadius += halfWidth;
  2070. bounds.outset(halfWidth, halfWidth);
  2071. }
  2072. // The radii are outset for two reasons. First, it allows the shader to simply perform
  2073. // simpler computation because the computed alpha is zero, rather than 50%, at the radius.
  2074. // Second, the outer radius is used to compute the verts of the bounding box that is
  2075. // rendered and the outset ensures the box will cover all partially covered by the rrect
  2076. // corners.
  2077. outerRadius += SK_ScalarHalf;
  2078. innerRadius -= SK_ScalarHalf;
  2079. this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
  2080. // Expand the rect for aa to generate correct vertices.
  2081. bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
  2082. fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type});
  2083. fVertCount = rrect_type_to_vert_count(type);
  2084. fIndexCount = rrect_type_to_index_count(type);
  2085. fAllFill = (kFill_RRectType == type);
  2086. }
  2087. const char* name() const override { return "CircularRRectOp"; }
  2088. void visitProxies(const VisitProxyFunc& func) const override {
  2089. fHelper.visitProxies(func);
  2090. }
  2091. #ifdef SK_DEBUG
  2092. SkString dumpInfo() const override {
  2093. SkString string;
  2094. for (int i = 0; i < fRRects.count(); ++i) {
  2095. string.appendf(
  2096. "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f],"
  2097. "InnerRad: %.2f, OuterRad: %.2f\n",
  2098. fRRects[i].fColor.toBytes_RGBA(), fRRects[i].fDevBounds.fLeft,
  2099. fRRects[i].fDevBounds.fTop, fRRects[i].fDevBounds.fRight,
  2100. fRRects[i].fDevBounds.fBottom, fRRects[i].fInnerRadius,
  2101. fRRects[i].fOuterRadius);
  2102. }
  2103. string += fHelper.dumpInfo();
  2104. string += INHERITED::dumpInfo();
  2105. return string;
  2106. }
  2107. #endif
  2108. GrProcessorSet::Analysis finalize(
  2109. const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
  2110. GrClampType clampType) override {
  2111. SkPMColor4f* color = &fRRects.front().fColor;
  2112. return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
  2113. GrProcessorAnalysisCoverage::kSingleChannel, color,
  2114. &fWideColor);
  2115. }
  2116. FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
  2117. private:
  2118. static void FillInOverstrokeVerts(GrVertexWriter& verts, const SkRect& bounds, SkScalar smInset,
  2119. SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius,
  2120. SkScalar innerRadius, const GrVertexColor& color) {
  2121. SkASSERT(smInset < bigInset);
  2122. // TL
  2123. verts.write(bounds.fLeft + smInset, bounds.fTop + smInset,
  2124. color,
  2125. xOffset, 0.0f,
  2126. outerRadius, innerRadius);
  2127. // TR
  2128. verts.write(bounds.fRight - smInset, bounds.fTop + smInset,
  2129. color,
  2130. xOffset, 0.0f,
  2131. outerRadius, innerRadius);
  2132. verts.write(bounds.fLeft + bigInset, bounds.fTop + bigInset,
  2133. color,
  2134. 0.0f, 0.0f,
  2135. outerRadius, innerRadius);
  2136. verts.write(bounds.fRight - bigInset, bounds.fTop + bigInset,
  2137. color,
  2138. 0.0f, 0.0f,
  2139. outerRadius, innerRadius);
  2140. verts.write(bounds.fLeft + bigInset, bounds.fBottom - bigInset,
  2141. color,
  2142. 0.0f, 0.0f,
  2143. outerRadius, innerRadius);
  2144. verts.write(bounds.fRight - bigInset, bounds.fBottom - bigInset,
  2145. color,
  2146. 0.0f, 0.0f,
  2147. outerRadius, innerRadius);
  2148. // BL
  2149. verts.write(bounds.fLeft + smInset, bounds.fBottom - smInset,
  2150. color,
  2151. xOffset, 0.0f,
  2152. outerRadius, innerRadius);
  2153. // BR
  2154. verts.write(bounds.fRight - smInset, bounds.fBottom - smInset,
  2155. color,
  2156. xOffset, 0.0f,
  2157. outerRadius, innerRadius);
  2158. }
  2159. void onPrepareDraws(Target* target) override {
  2160. // Invert the view matrix as a local matrix (if any other processors require coords).
  2161. SkMatrix localMatrix;
  2162. if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
  2163. return;
  2164. }
  2165. // Setup geometry processor
  2166. sk_sp<GrGeometryProcessor> gp(
  2167. new CircleGeometryProcessor(!fAllFill, false, false, false, false, fWideColor,
  2168. localMatrix));
  2169. sk_sp<const GrBuffer> vertexBuffer;
  2170. int firstVertex;
  2171. GrVertexWriter verts{target->makeVertexSpace(gp->vertexStride(), fVertCount,
  2172. &vertexBuffer, &firstVertex)};
  2173. if (!verts.fPtr) {
  2174. SkDebugf("Could not allocate vertices\n");
  2175. return;
  2176. }
  2177. sk_sp<const GrBuffer> indexBuffer;
  2178. int firstIndex = 0;
  2179. uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex);
  2180. if (!indices) {
  2181. SkDebugf("Could not allocate indices\n");
  2182. return;
  2183. }
  2184. int currStartVertex = 0;
  2185. for (const auto& rrect : fRRects) {
  2186. GrVertexColor color(rrect.fColor, fWideColor);
  2187. SkScalar outerRadius = rrect.fOuterRadius;
  2188. const SkRect& bounds = rrect.fDevBounds;
  2189. SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius,
  2190. bounds.fBottom - outerRadius, bounds.fBottom};
  2191. SkScalar yOuterRadii[4] = {-1, 0, 0, 1};
  2192. // The inner radius in the vertex data must be specified in normalized space.
  2193. // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius.
  2194. SkScalar innerRadius = rrect.fType != kFill_RRectType
  2195. ? rrect.fInnerRadius / rrect.fOuterRadius
  2196. : -1.0f / rrect.fOuterRadius;
  2197. for (int i = 0; i < 4; ++i) {
  2198. verts.write(bounds.fLeft, yCoords[i],
  2199. color,
  2200. -1.0f, yOuterRadii[i],
  2201. outerRadius, innerRadius);
  2202. verts.write(bounds.fLeft + outerRadius, yCoords[i],
  2203. color,
  2204. 0.0f, yOuterRadii[i],
  2205. outerRadius, innerRadius);
  2206. verts.write(bounds.fRight - outerRadius, yCoords[i],
  2207. color,
  2208. 0.0f, yOuterRadii[i],
  2209. outerRadius, innerRadius);
  2210. verts.write(bounds.fRight, yCoords[i],
  2211. color,
  2212. 1.0f, yOuterRadii[i],
  2213. outerRadius, innerRadius);
  2214. }
  2215. // Add the additional vertices for overstroked rrects.
  2216. // Effectively this is an additional stroked rrect, with its
  2217. // outer radius = outerRadius - innerRadius, and inner radius = 0.
  2218. // This will give us correct AA in the center and the correct
  2219. // distance to the outer edge.
  2220. //
  2221. // Also, the outer offset is a constant vector pointing to the right, which
  2222. // guarantees that the distance value along the outer rectangle is constant.
  2223. if (kOverstroke_RRectType == rrect.fType) {
  2224. SkASSERT(rrect.fInnerRadius <= 0.0f);
  2225. SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius;
  2226. // this is the normalized distance from the outer rectangle of this
  2227. // geometry to the outer edge
  2228. SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius;
  2229. FillInOverstrokeVerts(verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset,
  2230. overstrokeOuterRadius, 0.0f, color);
  2231. }
  2232. const uint16_t* primIndices = rrect_type_to_indices(rrect.fType);
  2233. const int primIndexCount = rrect_type_to_index_count(rrect.fType);
  2234. for (int i = 0; i < primIndexCount; ++i) {
  2235. *indices++ = primIndices[i] + currStartVertex;
  2236. }
  2237. currStartVertex += rrect_type_to_vert_count(rrect.fType);
  2238. }
  2239. GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles);
  2240. mesh->setIndexed(std::move(indexBuffer), fIndexCount, firstIndex, 0, fVertCount - 1,
  2241. GrPrimitiveRestart::kNo);
  2242. mesh->setVertexData(std::move(vertexBuffer), firstVertex);
  2243. target->recordDraw(std::move(gp), mesh);
  2244. }
  2245. void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
  2246. fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
  2247. }
  2248. CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
  2249. CircularRRectOp* that = t->cast<CircularRRectOp>();
  2250. // can only represent 65535 unique vertices with 16-bit indices
  2251. if (fVertCount + that->fVertCount > 65536) {
  2252. return CombineResult::kCannotCombine;
  2253. }
  2254. if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
  2255. return CombineResult::kCannotCombine;
  2256. }
  2257. if (fHelper.usesLocalCoords() &&
  2258. !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
  2259. return CombineResult::kCannotCombine;
  2260. }
  2261. fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
  2262. fVertCount += that->fVertCount;
  2263. fIndexCount += that->fIndexCount;
  2264. fAllFill = fAllFill && that->fAllFill;
  2265. fWideColor = fWideColor || that->fWideColor;
  2266. return CombineResult::kMerged;
  2267. }
  2268. struct RRect {
  2269. SkPMColor4f fColor;
  2270. SkScalar fInnerRadius;
  2271. SkScalar fOuterRadius;
  2272. SkRect fDevBounds;
  2273. RRectType fType;
  2274. };
  2275. SkMatrix fViewMatrixIfUsingLocalCoords;
  2276. Helper fHelper;
  2277. int fVertCount;
  2278. int fIndexCount;
  2279. bool fAllFill;
  2280. bool fWideColor;
  2281. SkSTArray<1, RRect, true> fRRects;
  2282. typedef GrMeshDrawOp INHERITED;
  2283. };
  2284. static const int kNumRRectsInIndexBuffer = 256;
  2285. GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
  2286. GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
  2287. static sk_sp<const GrBuffer> get_rrect_index_buffer(RRectType type,
  2288. GrResourceProvider* resourceProvider) {
  2289. GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey);
  2290. GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey);
  2291. switch (type) {
  2292. case kFill_RRectType:
  2293. return resourceProvider->findOrCreatePatternedIndexBuffer(
  2294. gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer,
  2295. kVertsPerStandardRRect, gRRectOnlyIndexBufferKey);
  2296. case kStroke_RRectType:
  2297. return resourceProvider->findOrCreatePatternedIndexBuffer(
  2298. gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer,
  2299. kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey);
  2300. default:
  2301. SkASSERT(false);
  2302. return nullptr;
  2303. }
  2304. }
  2305. class EllipticalRRectOp : public GrMeshDrawOp {
  2306. private:
  2307. using Helper = GrSimpleMeshDrawOpHelper;
  2308. public:
  2309. DEFINE_OP_CLASS_ID
  2310. // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates
  2311. // whether the rrect is only stroked or stroked and filled.
  2312. static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
  2313. GrPaint&& paint,
  2314. const SkMatrix& viewMatrix,
  2315. const SkRect& devRect,
  2316. float devXRadius,
  2317. float devYRadius,
  2318. SkVector devStrokeWidths,
  2319. bool strokeOnly) {
  2320. SkASSERT(devXRadius >= 0.5);
  2321. SkASSERT(devYRadius >= 0.5);
  2322. SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0));
  2323. SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0));
  2324. if (devStrokeWidths.fX > 0) {
  2325. if (SkScalarNearlyZero(devStrokeWidths.length())) {
  2326. devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf);
  2327. } else {
  2328. devStrokeWidths.scale(SK_ScalarHalf);
  2329. }
  2330. // we only handle thick strokes for near-circular ellipses
  2331. if (devStrokeWidths.length() > SK_ScalarHalf &&
  2332. (SK_ScalarHalf * devXRadius > devYRadius ||
  2333. SK_ScalarHalf * devYRadius > devXRadius)) {
  2334. return nullptr;
  2335. }
  2336. // we don't handle it if curvature of the stroke is less than curvature of the ellipse
  2337. if (devStrokeWidths.fX * (devYRadius * devYRadius) <
  2338. (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) {
  2339. return nullptr;
  2340. }
  2341. if (devStrokeWidths.fY * (devXRadius * devXRadius) <
  2342. (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) {
  2343. return nullptr;
  2344. }
  2345. }
  2346. return Helper::FactoryHelper<EllipticalRRectOp>(context, std::move(paint),
  2347. viewMatrix, devRect,
  2348. devXRadius, devYRadius, devStrokeWidths,
  2349. strokeOnly);
  2350. }
  2351. EllipticalRRectOp(Helper::MakeArgs helperArgs, const SkPMColor4f& color,
  2352. const SkMatrix& viewMatrix, const SkRect& devRect, float devXRadius,
  2353. float devYRadius, SkVector devStrokeHalfWidths, bool strokeOnly)
  2354. : INHERITED(ClassID())
  2355. , fHelper(helperArgs, GrAAType::kCoverage)
  2356. , fUseScale(false) {
  2357. SkScalar innerXRadius = 0.0f;
  2358. SkScalar innerYRadius = 0.0f;
  2359. SkRect bounds = devRect;
  2360. bool stroked = false;
  2361. if (devStrokeHalfWidths.fX > 0) {
  2362. // this is legit only if scale & translation (which should be the case at the moment)
  2363. if (strokeOnly) {
  2364. innerXRadius = devXRadius - devStrokeHalfWidths.fX;
  2365. innerYRadius = devYRadius - devStrokeHalfWidths.fY;
  2366. stroked = (innerXRadius >= 0 && innerYRadius >= 0);
  2367. }
  2368. devXRadius += devStrokeHalfWidths.fX;
  2369. devYRadius += devStrokeHalfWidths.fY;
  2370. bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY);
  2371. }
  2372. fStroked = stroked;
  2373. fViewMatrixIfUsingLocalCoords = viewMatrix;
  2374. this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
  2375. // Expand the rect for aa in order to generate the correct vertices.
  2376. bounds.outset(SK_ScalarHalf, SK_ScalarHalf);
  2377. fRRects.emplace_back(
  2378. RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds});
  2379. }
  2380. const char* name() const override { return "EllipticalRRectOp"; }
  2381. void visitProxies(const VisitProxyFunc& func) const override {
  2382. fHelper.visitProxies(func);
  2383. }
  2384. #ifdef SK_DEBUG
  2385. SkString dumpInfo() const override {
  2386. SkString string;
  2387. string.appendf("Stroked: %d\n", fStroked);
  2388. for (const auto& geo : fRRects) {
  2389. string.appendf(
  2390. "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], "
  2391. "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n",
  2392. geo.fColor.toBytes_RGBA(), geo.fDevBounds.fLeft, geo.fDevBounds.fTop,
  2393. geo.fDevBounds.fRight, geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius,
  2394. geo.fInnerXRadius, geo.fInnerYRadius);
  2395. }
  2396. string += fHelper.dumpInfo();
  2397. string += INHERITED::dumpInfo();
  2398. return string;
  2399. }
  2400. #endif
  2401. GrProcessorSet::Analysis finalize(
  2402. const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
  2403. GrClampType clampType) override {
  2404. fUseScale = !caps.shaderCaps()->floatIs32Bits();
  2405. SkPMColor4f* color = &fRRects.front().fColor;
  2406. return fHelper.finalizeProcessors(caps, clip, hasMixedSampledCoverage, clampType,
  2407. GrProcessorAnalysisCoverage::kSingleChannel, color,
  2408. &fWideColor);
  2409. }
  2410. FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
  2411. private:
  2412. void onPrepareDraws(Target* target) override {
  2413. SkMatrix localMatrix;
  2414. if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
  2415. return;
  2416. }
  2417. // Setup geometry processor
  2418. sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, fWideColor, fUseScale,
  2419. localMatrix));
  2420. // drop out the middle quad if we're stroked
  2421. int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect;
  2422. sk_sp<const GrBuffer> indexBuffer = get_rrect_index_buffer(
  2423. fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider());
  2424. if (!indexBuffer) {
  2425. SkDebugf("Could not allocate indices\n");
  2426. return;
  2427. }
  2428. PatternHelper helper(target, GrPrimitiveType::kTriangles, gp->vertexStride(),
  2429. std::move(indexBuffer), kVertsPerStandardRRect, indicesPerInstance,
  2430. fRRects.count());
  2431. GrVertexWriter verts{helper.vertices()};
  2432. if (!verts.fPtr) {
  2433. SkDebugf("Could not allocate vertices\n");
  2434. return;
  2435. }
  2436. for (const auto& rrect : fRRects) {
  2437. GrVertexColor color(rrect.fColor, fWideColor);
  2438. // Compute the reciprocals of the radii here to save time in the shader
  2439. float reciprocalRadii[4] = {
  2440. SkScalarInvert(rrect.fXRadius),
  2441. SkScalarInvert(rrect.fYRadius),
  2442. SkScalarInvert(rrect.fInnerXRadius),
  2443. SkScalarInvert(rrect.fInnerYRadius)
  2444. };
  2445. // Extend the radii out half a pixel to antialias.
  2446. SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf;
  2447. SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf;
  2448. SkScalar xMaxOffset = xOuterRadius;
  2449. SkScalar yMaxOffset = yOuterRadius;
  2450. if (!fStroked) {
  2451. // For filled rrects we map a unit circle in the vertex attributes rather than
  2452. // computing an ellipse and modifying that distance, so we normalize to 1.
  2453. xMaxOffset /= rrect.fXRadius;
  2454. yMaxOffset /= rrect.fYRadius;
  2455. }
  2456. const SkRect& bounds = rrect.fDevBounds;
  2457. SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius,
  2458. bounds.fBottom - yOuterRadius, bounds.fBottom};
  2459. SkScalar yOuterOffsets[4] = {yMaxOffset,
  2460. SK_ScalarNearlyZero, // we're using inversesqrt() in
  2461. // shader, so can't be exactly 0
  2462. SK_ScalarNearlyZero, yMaxOffset};
  2463. auto maybeScale = GrVertexWriter::If(fUseScale, SkTMax(rrect.fXRadius, rrect.fYRadius));
  2464. for (int i = 0; i < 4; ++i) {
  2465. verts.write(bounds.fLeft, yCoords[i],
  2466. color,
  2467. xMaxOffset, yOuterOffsets[i],
  2468. maybeScale,
  2469. reciprocalRadii);
  2470. verts.write(bounds.fLeft + xOuterRadius, yCoords[i],
  2471. color,
  2472. SK_ScalarNearlyZero, yOuterOffsets[i],
  2473. maybeScale,
  2474. reciprocalRadii);
  2475. verts.write(bounds.fRight - xOuterRadius, yCoords[i],
  2476. color,
  2477. SK_ScalarNearlyZero, yOuterOffsets[i],
  2478. maybeScale,
  2479. reciprocalRadii);
  2480. verts.write(bounds.fRight, yCoords[i],
  2481. color,
  2482. xMaxOffset, yOuterOffsets[i],
  2483. maybeScale,
  2484. reciprocalRadii);
  2485. }
  2486. }
  2487. helper.recordDraw(target, std::move(gp));
  2488. }
  2489. void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
  2490. fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
  2491. }
  2492. CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
  2493. EllipticalRRectOp* that = t->cast<EllipticalRRectOp>();
  2494. if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
  2495. return CombineResult::kCannotCombine;
  2496. }
  2497. if (fStroked != that->fStroked) {
  2498. return CombineResult::kCannotCombine;
  2499. }
  2500. if (fHelper.usesLocalCoords() &&
  2501. !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
  2502. return CombineResult::kCannotCombine;
  2503. }
  2504. fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin());
  2505. fWideColor = fWideColor || that->fWideColor;
  2506. return CombineResult::kMerged;
  2507. }
  2508. struct RRect {
  2509. SkPMColor4f fColor;
  2510. SkScalar fXRadius;
  2511. SkScalar fYRadius;
  2512. SkScalar fInnerXRadius;
  2513. SkScalar fInnerYRadius;
  2514. SkRect fDevBounds;
  2515. };
  2516. SkMatrix fViewMatrixIfUsingLocalCoords;
  2517. Helper fHelper;
  2518. bool fStroked;
  2519. bool fWideColor;
  2520. bool fUseScale;
  2521. SkSTArray<1, RRect, true> fRRects;
  2522. typedef GrMeshDrawOp INHERITED;
  2523. };
  2524. std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeCircularRRectOp(GrRecordingContext* context,
  2525. GrPaint&& paint,
  2526. const SkMatrix& viewMatrix,
  2527. const SkRRect& rrect,
  2528. const SkStrokeRec& stroke,
  2529. const GrShaderCaps* shaderCaps) {
  2530. SkASSERT(viewMatrix.rectStaysRect());
  2531. SkASSERT(viewMatrix.isSimilarity());
  2532. SkASSERT(rrect.isSimple());
  2533. SkASSERT(!rrect.isOval());
  2534. SkASSERT(SkRRectPriv::GetSimpleRadii(rrect).fX == SkRRectPriv::GetSimpleRadii(rrect).fY);
  2535. // RRect ops only handle simple, but not too simple, rrects.
  2536. // Do any matrix crunching before we reset the draw state for device coords.
  2537. const SkRect& rrectBounds = rrect.getBounds();
  2538. SkRect bounds;
  2539. viewMatrix.mapRect(&bounds, rrectBounds);
  2540. SkScalar radius = SkRRectPriv::GetSimpleRadii(rrect).fX;
  2541. SkScalar scaledRadius = SkScalarAbs(radius * (viewMatrix[SkMatrix::kMScaleX] +
  2542. viewMatrix[SkMatrix::kMSkewY]));
  2543. // Do mapping of stroke. Use -1 to indicate fill-only draws.
  2544. SkScalar scaledStroke = -1;
  2545. SkScalar strokeWidth = stroke.getWidth();
  2546. SkStrokeRec::Style style = stroke.getStyle();
  2547. bool isStrokeOnly =
  2548. SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
  2549. bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
  2550. if (hasStroke) {
  2551. if (SkStrokeRec::kHairline_Style == style) {
  2552. scaledStroke = SK_Scalar1;
  2553. } else {
  2554. scaledStroke = SkScalarAbs(strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
  2555. }
  2556. }
  2557. // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
  2558. // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
  2559. // patch will have fractional coverage. This only matters when the interior is actually filled.
  2560. // We could consider falling back to rect rendering here, since a tiny radius is
  2561. // indistinguishable from a square corner.
  2562. if (!isStrokeOnly && SK_ScalarHalf > scaledRadius) {
  2563. return nullptr;
  2564. }
  2565. return CircularRRectOp::Make(context, std::move(paint), viewMatrix, bounds, scaledRadius,
  2566. scaledStroke, isStrokeOnly);
  2567. }
  2568. static std::unique_ptr<GrDrawOp> make_rrect_op(GrRecordingContext* context,
  2569. GrPaint&& paint,
  2570. const SkMatrix& viewMatrix,
  2571. const SkRRect& rrect,
  2572. const SkStrokeRec& stroke) {
  2573. SkASSERT(viewMatrix.rectStaysRect());
  2574. SkASSERT(rrect.isSimple());
  2575. SkASSERT(!rrect.isOval());
  2576. // RRect ops only handle simple, but not too simple, rrects.
  2577. // Do any matrix crunching before we reset the draw state for device coords.
  2578. const SkRect& rrectBounds = rrect.getBounds();
  2579. SkRect bounds;
  2580. viewMatrix.mapRect(&bounds, rrectBounds);
  2581. SkVector radii = SkRRectPriv::GetSimpleRadii(rrect);
  2582. SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX +
  2583. viewMatrix[SkMatrix::kMSkewY] * radii.fY);
  2584. SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX +
  2585. viewMatrix[SkMatrix::kMScaleY] * radii.fY);
  2586. SkStrokeRec::Style style = stroke.getStyle();
  2587. // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws.
  2588. SkVector scaledStroke = {-1, -1};
  2589. SkScalar strokeWidth = stroke.getWidth();
  2590. bool isStrokeOnly =
  2591. SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style;
  2592. bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style;
  2593. if (hasStroke) {
  2594. if (SkStrokeRec::kHairline_Style == style) {
  2595. scaledStroke.set(1, 1);
  2596. } else {
  2597. scaledStroke.fX = SkScalarAbs(
  2598. strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY]));
  2599. scaledStroke.fY = SkScalarAbs(
  2600. strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY]));
  2601. }
  2602. // if half of strokewidth is greater than radius, we don't handle that right now
  2603. if ((SK_ScalarHalf * scaledStroke.fX > xRadius ||
  2604. SK_ScalarHalf * scaledStroke.fY > yRadius)) {
  2605. return nullptr;
  2606. }
  2607. }
  2608. // The matrix may have a rotation by an odd multiple of 90 degrees.
  2609. if (viewMatrix.getScaleX() == 0) {
  2610. std::swap(xRadius, yRadius);
  2611. std::swap(scaledStroke.fX, scaledStroke.fY);
  2612. }
  2613. // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on
  2614. // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine-
  2615. // patch will have fractional coverage. This only matters when the interior is actually filled.
  2616. // We could consider falling back to rect rendering here, since a tiny radius is
  2617. // indistinguishable from a square corner.
  2618. if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) {
  2619. return nullptr;
  2620. }
  2621. // if the corners are circles, use the circle renderer
  2622. return EllipticalRRectOp::Make(context, std::move(paint), viewMatrix, bounds,
  2623. xRadius, yRadius, scaledStroke, isStrokeOnly);
  2624. }
  2625. std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrRecordingContext* context,
  2626. GrPaint&& paint,
  2627. const SkMatrix& viewMatrix,
  2628. const SkRRect& rrect,
  2629. const SkStrokeRec& stroke,
  2630. const GrShaderCaps* shaderCaps) {
  2631. if (rrect.isOval()) {
  2632. return MakeOvalOp(context, std::move(paint), viewMatrix, rrect.getBounds(),
  2633. GrStyle(stroke, nullptr), shaderCaps);
  2634. }
  2635. if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) {
  2636. return nullptr;
  2637. }
  2638. return make_rrect_op(context, std::move(paint), viewMatrix, rrect, stroke);
  2639. }
  2640. ///////////////////////////////////////////////////////////////////////////////
  2641. std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeCircleOp(GrRecordingContext* context,
  2642. GrPaint&& paint,
  2643. const SkMatrix& viewMatrix,
  2644. const SkRect& oval,
  2645. const GrStyle& style,
  2646. const GrShaderCaps* shaderCaps) {
  2647. SkScalar width = oval.width();
  2648. SkASSERT(width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
  2649. circle_stays_circle(viewMatrix));
  2650. auto r = width / 2.f;
  2651. SkPoint center = { oval.centerX(), oval.centerY() };
  2652. if (style.hasNonDashPathEffect()) {
  2653. return nullptr;
  2654. } else if (style.isDashed()) {
  2655. if (style.strokeRec().getCap() != SkPaint::kButt_Cap ||
  2656. style.dashIntervalCnt() != 2 || style.strokeRec().getWidth() >= width) {
  2657. return nullptr;
  2658. }
  2659. auto onInterval = style.dashIntervals()[0];
  2660. auto offInterval = style.dashIntervals()[1];
  2661. if (offInterval == 0) {
  2662. GrStyle strokeStyle(style.strokeRec(), nullptr);
  2663. return MakeOvalOp(context, std::move(paint), viewMatrix, oval,
  2664. strokeStyle, shaderCaps);
  2665. } else if (onInterval == 0) {
  2666. // There is nothing to draw but we have no way to indicate that here.
  2667. return nullptr;
  2668. }
  2669. auto angularOnInterval = onInterval / r;
  2670. auto angularOffInterval = offInterval / r;
  2671. auto phaseAngle = style.dashPhase() / r;
  2672. // Currently this function doesn't accept ovals with different start angles, though
  2673. // it could.
  2674. static const SkScalar kStartAngle = 0.f;
  2675. return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix, center, r,
  2676. style.strokeRec().getWidth(), kStartAngle,
  2677. angularOnInterval, angularOffInterval, phaseAngle);
  2678. }
  2679. return CircleOp::Make(context, std::move(paint), viewMatrix, center, r, style);
  2680. }
  2681. std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrRecordingContext* context,
  2682. GrPaint&& paint,
  2683. const SkMatrix& viewMatrix,
  2684. const SkRect& oval,
  2685. const GrStyle& style,
  2686. const GrShaderCaps* shaderCaps) {
  2687. // we can draw circles
  2688. SkScalar width = oval.width();
  2689. if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) &&
  2690. circle_stays_circle(viewMatrix)) {
  2691. return MakeCircleOp(context, std::move(paint), viewMatrix, oval, style, shaderCaps);
  2692. }
  2693. if (style.pathEffect()) {
  2694. return nullptr;
  2695. }
  2696. // prefer the device space ellipse op for batchability
  2697. if (viewMatrix.rectStaysRect()) {
  2698. return EllipseOp::Make(context, std::move(paint), viewMatrix, oval, style.strokeRec());
  2699. }
  2700. // Otherwise, if we have shader derivative support, render as device-independent
  2701. if (shaderCaps->shaderDerivativeSupport()) {
  2702. SkScalar a = viewMatrix[SkMatrix::kMScaleX];
  2703. SkScalar b = viewMatrix[SkMatrix::kMSkewX];
  2704. SkScalar c = viewMatrix[SkMatrix::kMSkewY];
  2705. SkScalar d = viewMatrix[SkMatrix::kMScaleY];
  2706. // Check for near-degenerate matrix
  2707. if (a*a + c*c > SK_ScalarNearlyZero && b*b + d*d > SK_ScalarNearlyZero) {
  2708. return DIEllipseOp::Make(context, std::move(paint), viewMatrix, oval,
  2709. style.strokeRec());
  2710. }
  2711. }
  2712. return nullptr;
  2713. }
  2714. ///////////////////////////////////////////////////////////////////////////////
  2715. std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrRecordingContext* context,
  2716. GrPaint&& paint,
  2717. const SkMatrix& viewMatrix,
  2718. const SkRect& oval, SkScalar startAngle,
  2719. SkScalar sweepAngle, bool useCenter,
  2720. const GrStyle& style,
  2721. const GrShaderCaps* shaderCaps) {
  2722. SkASSERT(!oval.isEmpty());
  2723. SkASSERT(sweepAngle);
  2724. SkScalar width = oval.width();
  2725. if (SkScalarAbs(sweepAngle) >= 360.f) {
  2726. return nullptr;
  2727. }
  2728. if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) {
  2729. return nullptr;
  2730. }
  2731. SkPoint center = {oval.centerX(), oval.centerY()};
  2732. CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle),
  2733. useCenter};
  2734. return CircleOp::Make(context, std::move(paint), viewMatrix,
  2735. center, width / 2.f, style, &arcParams);
  2736. }
  2737. ///////////////////////////////////////////////////////////////////////////////
  2738. #if GR_TEST_UTILS
  2739. GR_DRAW_OP_TEST_DEFINE(CircleOp) {
  2740. do {
  2741. SkScalar rotate = random->nextSScalar1() * 360.f;
  2742. SkScalar translateX = random->nextSScalar1() * 1000.f;
  2743. SkScalar translateY = random->nextSScalar1() * 1000.f;
  2744. SkScalar scale;
  2745. do {
  2746. scale = random->nextSScalar1() * 100.f;
  2747. } while (scale == 0);
  2748. SkMatrix viewMatrix;
  2749. viewMatrix.setRotate(rotate);
  2750. viewMatrix.postTranslate(translateX, translateY);
  2751. viewMatrix.postScale(scale, scale);
  2752. SkRect circle = GrTest::TestSquare(random);
  2753. SkPoint center = {circle.centerX(), circle.centerY()};
  2754. SkScalar radius = circle.width() / 2.f;
  2755. SkStrokeRec stroke = GrTest::TestStrokeRec(random);
  2756. CircleOp::ArcParams arcParamsTmp;
  2757. const CircleOp::ArcParams* arcParams = nullptr;
  2758. if (random->nextBool()) {
  2759. arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2;
  2760. arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f;
  2761. arcParamsTmp.fUseCenter = random->nextBool();
  2762. arcParams = &arcParamsTmp;
  2763. }
  2764. std::unique_ptr<GrDrawOp> op = CircleOp::Make(context, std::move(paint), viewMatrix,
  2765. center, radius,
  2766. GrStyle(stroke, nullptr), arcParams);
  2767. if (op) {
  2768. return op;
  2769. }
  2770. assert_alive(paint);
  2771. } while (true);
  2772. }
  2773. GR_DRAW_OP_TEST_DEFINE(ButtCapDashedCircleOp) {
  2774. SkScalar rotate = random->nextSScalar1() * 360.f;
  2775. SkScalar translateX = random->nextSScalar1() * 1000.f;
  2776. SkScalar translateY = random->nextSScalar1() * 1000.f;
  2777. SkScalar scale;
  2778. do {
  2779. scale = random->nextSScalar1() * 100.f;
  2780. } while (scale == 0);
  2781. SkMatrix viewMatrix;
  2782. viewMatrix.setRotate(rotate);
  2783. viewMatrix.postTranslate(translateX, translateY);
  2784. viewMatrix.postScale(scale, scale);
  2785. SkRect circle = GrTest::TestSquare(random);
  2786. SkPoint center = {circle.centerX(), circle.centerY()};
  2787. SkScalar radius = circle.width() / 2.f;
  2788. SkScalar strokeWidth = random->nextRangeScalar(0.001f * radius, 1.8f * radius);
  2789. SkScalar onAngle = random->nextRangeScalar(0.01f, 1000.f);
  2790. SkScalar offAngle = random->nextRangeScalar(0.01f, 1000.f);
  2791. SkScalar startAngle = random->nextRangeScalar(-1000.f, 1000.f);
  2792. SkScalar phase = random->nextRangeScalar(-1000.f, 1000.f);
  2793. return ButtCapDashedCircleOp::Make(context, std::move(paint), viewMatrix,
  2794. center, radius, strokeWidth,
  2795. startAngle, onAngle, offAngle, phase);
  2796. }
  2797. GR_DRAW_OP_TEST_DEFINE(EllipseOp) {
  2798. SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
  2799. SkRect ellipse = GrTest::TestSquare(random);
  2800. return EllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
  2801. GrTest::TestStrokeRec(random));
  2802. }
  2803. GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) {
  2804. SkMatrix viewMatrix = GrTest::TestMatrix(random);
  2805. SkRect ellipse = GrTest::TestSquare(random);
  2806. return DIEllipseOp::Make(context, std::move(paint), viewMatrix, ellipse,
  2807. GrTest::TestStrokeRec(random));
  2808. }
  2809. GR_DRAW_OP_TEST_DEFINE(CircularRRectOp) {
  2810. do {
  2811. SkScalar rotate = random->nextSScalar1() * 360.f;
  2812. SkScalar translateX = random->nextSScalar1() * 1000.f;
  2813. SkScalar translateY = random->nextSScalar1() * 1000.f;
  2814. SkScalar scale;
  2815. do {
  2816. scale = random->nextSScalar1() * 100.f;
  2817. } while (scale == 0);
  2818. SkMatrix viewMatrix;
  2819. viewMatrix.setRotate(rotate);
  2820. viewMatrix.postTranslate(translateX, translateY);
  2821. viewMatrix.postScale(scale, scale);
  2822. SkRect rect = GrTest::TestRect(random);
  2823. SkScalar radius = random->nextRangeF(0.1f, 10.f);
  2824. SkRRect rrect = SkRRect::MakeRectXY(rect, radius, radius);
  2825. if (rrect.isOval()) {
  2826. continue;
  2827. }
  2828. std::unique_ptr<GrDrawOp> op =
  2829. GrOvalOpFactory::MakeCircularRRectOp(context, std::move(paint), viewMatrix, rrect,
  2830. GrTest::TestStrokeRec(random), nullptr);
  2831. if (op) {
  2832. return op;
  2833. }
  2834. assert_alive(paint);
  2835. } while (true);
  2836. }
  2837. GR_DRAW_OP_TEST_DEFINE(RRectOp) {
  2838. SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random);
  2839. const SkRRect& rrect = GrTest::TestRRectSimple(random);
  2840. return make_rrect_op(context, std::move(paint), viewMatrix, rrect,
  2841. GrTest::TestStrokeRec(random));
  2842. }
  2843. #endif