GrOvalOpFactory.cpp revision 88d99c63878c2d3d340120f0321676f72afcb4f0
1b51eaa183e048a928fb363bac4404e6acf0e3badGuido van Rossum/* 2b51eaa183e048a928fb363bac4404e6acf0e3badGuido van Rossum * Copyright 2013 Google Inc. 343e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna * 443e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna * Use of this source code is governed by a BSD-style license that can be 543e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna * found in the LICENSE file. 6428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson */ 743e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna 843e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna#include "GrOvalOpFactory.h" 943e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna#include "GrDrawOpTest.h" 104efb6e964376a46aaa3acf365a6627a37af236bfTim Peters#include "GrGeometryProcessor.h" 114efb6e964376a46aaa3acf365a6627a37af236bfTim Peters#include "GrOpFlushState.h" 124efb6e964376a46aaa3acf365a6627a37af236bfTim Peters#include "GrProcessor.h" 134efb6e964376a46aaa3acf365a6627a37af236bfTim Peters#include "GrResourceProvider.h" 144efb6e964376a46aaa3acf365a6627a37af236bfTim Peters#include "GrShaderCaps.h" 154efb6e964376a46aaa3acf365a6627a37af236bfTim Peters#include "GrStyle.h" 164efb6e964376a46aaa3acf365a6627a37af236bfTim Peters#include "SkRRect.h" 174efb6e964376a46aaa3acf365a6627a37af236bfTim Peters#include "SkStrokeRec.h" 184efb6e964376a46aaa3acf365a6627a37af236bfTim Peters#include "glsl/GrGLSLFragmentShaderBuilder.h" 1943e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna#include "glsl/GrGLSLGeometryProcessor.h" 2043e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna#include "glsl/GrGLSLProgramDataManager.h" 2143e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna#include "glsl/GrGLSLUniformHandler.h" 221aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum#include "glsl/GrGLSLUtil.h" 23244c593598af4db19e410032fb10793617a895ceKa-Ping Yee#include "glsl/GrGLSLVarying.h" 24428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson#include "glsl/GrGLSLVertexShaderBuilder.h" 25428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson#include "ops/GrMeshDrawOp.h" 26428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson#include "ops/GrSimpleMeshDrawOpHelper.h" 27cf4a2f29adb6bdae0b18e983250d7c48d486c9d6Serhiy Storchaka 28433f32c3be3b23adc4ec389ff9e78f49c7288f3dBenjamin Petersonnamespace { 293fb79c747b0cd0884f2a6ede9e36673bec8745f2Raymond Hettinger 3058c0752a33253641c1423fac2d4ef3f623fbcb46Victor Stinnerstruct EllipseVertex { 315b8d2c3af76e704926cf5915ad0e6af59a232e61Terry Jan Reedy SkPoint fPos; 321c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith GrColor fColor; 335b8d2c3af76e704926cf5915ad0e6af59a232e61Terry Jan Reedy SkPoint fOffset; 345b8d2c3af76e704926cf5915ad0e6af59a232e61Terry Jan Reedy SkPoint fOuterRadii; 355b8d2c3af76e704926cf5915ad0e6af59a232e61Terry Jan Reedy SkPoint fInnerRadii; 365b8d2c3af76e704926cf5915ad0e6af59a232e61Terry Jan Reedy}; 37e431d3c9aadb52dd1eea4d1e606e94f1c8471459Serhiy Storchaka 38768c16ce0273a74fa846cc388753280b17b02cfcSerhiy Storchakastruct DIEllipseVertex { 394d8e859e8f0a209a7e999ce9cc0988156c795949Guido van Rossum SkPoint fPos; 4040fc16059f04ee8fda0b5956cc4883eb21ca8f8cSkip Montanaro GrColor fColor; 41b9d10d08c4eb0dedaea3b1bcde0f13b033e16c85Alexander Belopolsky SkPoint fOuterOffset; 42b9d10d08c4eb0dedaea3b1bcde0f13b033e16c85Alexander Belopolsky SkPoint fInnerOffset; 4340fc16059f04ee8fda0b5956cc4883eb21ca8f8cSkip Montanaro}; 4440fc16059f04ee8fda0b5956cc4883eb21ca8f8cSkip Montanaro 451aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossumstatic inline bool circle_stays_circle(const SkMatrix& m) { return m.isSimilarity(); } 461aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum} 47a90c78b9186f5ba8d91d3be0e684f81f2068c771Guido van Rossum 48a90c78b9186f5ba8d91d3be0e684f81f2068c771Guido van Rossum/////////////////////////////////////////////////////////////////////////////// 49428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson 50428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson/** 51428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson * The output of this effect is a modulation of the input color and coverage for a circle. It 5200c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge * operates in a space normalized by the circle radius (outer radius in the case of a stroke) 5300c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge * with origin at the circle center. Three vertex attributes are used: 5400c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge * vec2f : position in device space of the bounding geometry vertices 5500c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge * vec4ub: color 5600c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge * vec4f : (p.xy, outerRad, innerRad) 5700c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge * p is the position in the normalized space. 5800c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge * outerRad is the outerRadius in device space. 5900c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge * innerRad is the innerRadius in normalized space (ignored if not stroking). 6000c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge * Additional clip planes are supported for rendering circular arcs. The additional planes are 6100c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge * either intersected or unioned together. Up to three planes are supported (an initial plane, 6200c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge * a plane intersected with the initial plane, and a plane unioned with the first two). Only two 6300c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge * are useful for any given arc, but having all three in one instance allows combining different 6400c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge * types of arcs. 6500c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge */ 6600c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge 6700c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Ingeclass CircleGeometryProcessor : public GrGeometryProcessor { 6800c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Ingepublic: 6900c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge CircleGeometryProcessor(bool stroke, bool clipPlane, bool isectPlane, bool unionPlane, 7000c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge const SkMatrix& localMatrix) 7100c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge : fLocalMatrix(localMatrix) { 7200c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge this->initClassID<CircleGeometryProcessor>(); 7300c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType, 7400c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge kHigh_GrSLPrecision); 7500c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType); 7600c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge fInCircleEdge = &this->addVertexAttrib("inCircleEdge", kVec4f_GrVertexAttribType, 7700c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge kHigh_GrSLPrecision); 7800c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge if (clipPlane) { 7900c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge fInClipPlane = &this->addVertexAttrib("inClipPlane", kVec3f_GrVertexAttribType); 8000c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge } else { 8100c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge fInClipPlane = nullptr; 8200c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge } 8300c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge if (isectPlane) { 8400c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge fInIsectPlane = &this->addVertexAttrib("inIsectPlane", kVec3f_GrVertexAttribType); 8500c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge } else { 8600c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge fInIsectPlane = nullptr; 8700c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge } 8800c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge if (unionPlane) { 8900c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge fInUnionPlane = &this->addVertexAttrib("inUnionPlane", kVec3f_GrVertexAttribType); 9000c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge } else { 9100c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge fInUnionPlane = nullptr; 9200c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge } 9300c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge fStroke = stroke; 9400c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge } 95d51374ed78a3e3145911a16cdf3b9b84b3ba7d15Benjamin Peterson 96d51374ed78a3e3145911a16cdf3b9b84b3ba7d15Benjamin Peterson ~CircleGeometryProcessor() override {} 9700c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge 981aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum const char* name() const override { return "CircleEdge"; } 993fb79c747b0cd0884f2a6ede9e36673bec8745f2Raymond Hettinger 100aa17a7fc98773e0f2b2a23e59a0a2b3d9f1bca84Raymond Hettinger void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override { 101a0e79408bcf14015995fb4f1f1c3ad88df017496Raymond Hettinger GLSLProcessor::GenKey(*this, caps, b); 102a0e79408bcf14015995fb4f1f1c3ad88df017496Raymond Hettinger } 103a0e79408bcf14015995fb4f1f1c3ad88df017496Raymond Hettinger 104aa17a7fc98773e0f2b2a23e59a0a2b3d9f1bca84Raymond Hettinger GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override { 10500c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge return new GLSLProcessor(); 10600c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge } 10700c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge 10800c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Ingeprivate: 10900c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge class GLSLProcessor : public GrGLSLGeometryProcessor { 11000c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge public: 11100c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge GLSLProcessor() {} 112b08b2d316653bf22d39ad76814b5a0e7dad30c31Eric S. Raymond 11368468eba635570400f607e140425a222018e56f9Guido van Rossum void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { 11468468eba635570400f607e140425a222018e56f9Guido van Rossum const CircleGeometryProcessor& cgp = args.fGP.cast<CircleGeometryProcessor>(); 1154d8e859e8f0a209a7e999ce9cc0988156c795949Guido van Rossum GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; 116fd036451bf0e0ade8783e21df801abf7be96d020Antoine Pitrou GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; 117fd036451bf0e0ade8783e21df801abf7be96d020Antoine Pitrou GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; 1183b631775b26b866e072cd3340f303bf5903af883Guido van Rossum GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder; 1193b631775b26b866e072cd3340f303bf5903af883Guido van Rossum 1203b631775b26b866e072cd3340f303bf5903af883Guido van Rossum // emit attributes 12133856de84d1115a18b699e0ca93c3b921bc6a1afBenjamin Peterson varyingHandler->emitAttributes(cgp); 1224d8e859e8f0a209a7e999ce9cc0988156c795949Guido van Rossum fragBuilder->codeAppend("highfloat4 circleEdge;"); 123a721abac299bb6529021000a71847486d531b41aBrett Cannon varyingHandler->addPassThroughAttribute(cgp.fInCircleEdge, "circleEdge", 124a721abac299bb6529021000a71847486d531b41aBrett Cannon kHigh_GrSLPrecision); 125a721abac299bb6529021000a71847486d531b41aBrett Cannon if (cgp.fInClipPlane) { 126a721abac299bb6529021000a71847486d531b41aBrett Cannon fragBuilder->codeAppend("half3 clipPlane;"); 127cd16bf640405065e4702539632ce577536207d88Guido van Rossum varyingHandler->addPassThroughAttribute(cgp.fInClipPlane, "clipPlane"); 128a721abac299bb6529021000a71847486d531b41aBrett Cannon } 129a721abac299bb6529021000a71847486d531b41aBrett Cannon if (cgp.fInIsectPlane) { 130a721abac299bb6529021000a71847486d531b41aBrett Cannon SkASSERT(cgp.fInClipPlane); 131a721abac299bb6529021000a71847486d531b41aBrett Cannon fragBuilder->codeAppend("half3 isectPlane;"); 1321aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum varyingHandler->addPassThroughAttribute(cgp.fInIsectPlane, "isectPlane"); 133a721abac299bb6529021000a71847486d531b41aBrett Cannon } 1341aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum if (cgp.fInUnionPlane) { 1354d8e859e8f0a209a7e999ce9cc0988156c795949Guido van Rossum SkASSERT(cgp.fInClipPlane); 1361c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith fragBuilder->codeAppend("half3 unionPlane;"); 1371c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith varyingHandler->addPassThroughAttribute(cgp.fInUnionPlane, "unionPlane"); 1381c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith } 1391c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith 1401c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith // setup pass through color 1411c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith varyingHandler->addPassThroughAttribute(cgp.fInColor, args.fOutputColor); 1421c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith 1431c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith // Setup position 1441c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith this->writeOutputPosition(vertBuilder, gpArgs, cgp.fInPosition->fName); 1451c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith 1461c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith // emit transforms 1471c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith this->emitTransforms(vertBuilder, 1481c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith varyingHandler, 1491c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith uniformHandler, 1501c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith gpArgs->fPositionVar, 1511c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith cgp.fInPosition->fName, 1521c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith cgp.fLocalMatrix, 1531c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith args.fFPCoordTransformHandler); 1541c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith 1551c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith fragBuilder->codeAppend("highfloat d = length(circleEdge.xy);"); 1561c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith fragBuilder->codeAppend("half distanceToOuterEdge = circleEdge.z * (1.0 - d);"); 1571c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith fragBuilder->codeAppend("half edgeAlpha = clamp(distanceToOuterEdge, 0.0, 1.0);"); 158c0eaecafe9809757301551285f2a41ea89f1f228Armin Ronacher if (cgp.fStroke) { 159de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters fragBuilder->codeAppend( 160de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters "half distanceToInnerEdge = circleEdge.z * (d - circleEdge.w);"); 161de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters fragBuilder->codeAppend("half innerAlpha = clamp(distanceToInnerEdge, 0.0, 1.0);"); 162de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters fragBuilder->codeAppend("edgeAlpha *= innerAlpha;"); 163de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters } 164de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters 165de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters if (cgp.fInClipPlane) { 166de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters fragBuilder->codeAppend( 167c0eaecafe9809757301551285f2a41ea89f1f228Armin Ronacher "half clip = clamp(circleEdge.z * dot(circleEdge.xy, clipPlane.xy) + " 168de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters "clipPlane.z, 0.0, 1.0);"); 169c0eaecafe9809757301551285f2a41ea89f1f228Armin Ronacher if (cgp.fInIsectPlane) { 170c0eaecafe9809757301551285f2a41ea89f1f228Armin Ronacher fragBuilder->codeAppend( 1714d8e859e8f0a209a7e999ce9cc0988156c795949Guido van Rossum "clip *= clamp(circleEdge.z * dot(circleEdge.xy, isectPlane.xy) + " 172de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters "isectPlane.z, 0.0, 1.0);"); 173de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters } 174de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters if (cgp.fInUnionPlane) { 175b053cd8f40dd19985b16f50661640dcefb69888fGuido van Rossum fragBuilder->codeAppend( 176c150536b5efadf71fcb4187cad7258be7268e157Neal Norwitz "clip += (1.0 - clip)*clamp(circleEdge.z * dot(circleEdge.xy, " 177d51374ed78a3e3145911a16cdf3b9b84b3ba7d15Benjamin Peterson "unionPlane.xy) + unionPlane.z, 0.0, 1.0);"); 178de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters } 179e1519a1b4d8e24ab6a98083c6ec8bf4ec7594111Thomas Wouters fragBuilder->codeAppend("edgeAlpha *= clip;"); 1804d8e859e8f0a209a7e999ce9cc0988156c795949Guido van Rossum } 181dde002899db8d04ac25d630fcc3a27e8bbf282eaGeorg Brandl fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage); 182fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum } 1834d8e859e8f0a209a7e999ce9cc0988156c795949Guido van Rossum 1843b631775b26b866e072cd3340f303bf5903af883Guido van Rossum static void GenKey(const GrGeometryProcessor& gp, 185fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum const GrShaderCaps&, 1864d8e859e8f0a209a7e999ce9cc0988156c795949Guido van Rossum GrProcessorKeyBuilder* b) { 187de49583a0d59f806b88b0f6a869f470047b3cbceTim Peters const CircleGeometryProcessor& cgp = gp.cast<CircleGeometryProcessor>(); 188c0eaecafe9809757301551285f2a41ea89f1f228Armin Ronacher uint16_t key; 1891ff08b1243dcb07db975640b2f3cbc82985bee81Ka-Ping Yee key = cgp.fStroke ? 0x01 : 0x0; 190c0eaecafe9809757301551285f2a41ea89f1f228Armin Ronacher key |= cgp.fLocalMatrix.hasPerspective() ? 0x02 : 0x0; 1911ff08b1243dcb07db975640b2f3cbc82985bee81Ka-Ping Yee key |= cgp.fInClipPlane ? 0x04 : 0x0; 1922cc3b4ba9ffa658784da03f14a0a068e2c61d1b3Ezio Melotti key |= cgp.fInIsectPlane ? 0x08 : 0x0; 1933b631775b26b866e072cd3340f303bf5903af883Guido van Rossum key |= cgp.fInUnionPlane ? 0x10 : 0x0; 1941aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum b->add32(key); 1951c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith } 1961c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith 1971c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc, 1981c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith FPCoordTransformIter&& transformIter) override { 1991c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith this->setTransformDataHelper(primProc.cast<CircleGeometryProcessor>().fLocalMatrix, 2001c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith pdman, &transformIter); 2011c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith } 2021c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith 2031c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith private: 2041c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith typedef GrGLSLGeometryProcessor INHERITED; 2051c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith }; 2061c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith 2071c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith SkMatrix fLocalMatrix; 2081c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith const Attribute* fInPosition; 2091c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith const Attribute* fInColor; 2101c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith const Attribute* fInCircleEdge; 2111c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith const Attribute* fInClipPlane; 2121c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith const Attribute* fInIsectPlane; 2131c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith const Attribute* fInUnionPlane; 2149d6897accc49f40414fbecafeb1c65562c6e4647Guido van Rossum bool fStroke; 215fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum 2169b8d801c37fa29420848ebc1b50c601893b36287Fred Drake GR_DECLARE_GEOMETRY_PROCESSOR_TEST 21728c62bbdb2545eddf04ba7e2f2daa0dcedbb6052Ka-Ping Yee 21828c62bbdb2545eddf04ba7e2f2daa0dcedbb6052Ka-Ping Yee typedef GrGeometryProcessor INHERITED; 21928c62bbdb2545eddf04ba7e2f2daa0dcedbb6052Ka-Ping Yee}; 2209b8d801c37fa29420848ebc1b50c601893b36287Fred Drake 2215ca576ed0a0c697c7e7547adfd0b3af010fd2053Tim PetersGR_DEFINE_GEOMETRY_PROCESSOR_TEST(CircleGeometryProcessor); 22289f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters 22389f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters#if GR_TEST_UTILS 22489f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouterssk_sp<GrGeometryProcessor> CircleGeometryProcessor::TestCreate(GrProcessorTestData* d) { 22589f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters return sk_sp<GrGeometryProcessor>(new CircleGeometryProcessor( 22689f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters d->fRandom->nextBool(), d->fRandom->nextBool(), d->fRandom->nextBool(), 22789f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom))); 228428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson} 22989f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters#endif 23089f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters 23189f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters/////////////////////////////////////////////////////////////////////////////// 2325e6db313686c200da425a54d2e0c95fa40107b1dTerry Jan Reedy 2335e6db313686c200da425a54d2e0c95fa40107b1dTerry Jan Reedy/** 2345e6db313686c200da425a54d2e0c95fa40107b1dTerry Jan Reedy * The output of this effect is a modulation of the input color and coverage for an axis-aligned 2359dc3a36c849c15c227a8af218cfb215abe7b3c48Terry Jan Reedy * ellipse, specified as a 2D offset from center, and the reciprocals of the outer and inner radii, 236f106f8f29cf5eb90f39e0734d248a53b071f05c0Terry Jan Reedy * in both x and y directions. 2379dc3a36c849c15c227a8af218cfb215abe7b3c48Terry Jan Reedy * 2389dc3a36c849c15c227a8af218cfb215abe7b3c48Terry Jan Reedy * We are using an implicit function of x^2/a^2 + y^2/b^2 - 1 = 0. 23989f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters */ 24089f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters 24189f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Woutersclass EllipseGeometryProcessor : public GrGeometryProcessor { 24289f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouterspublic: 24389f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters EllipseGeometryProcessor(bool stroke, const SkMatrix& localMatrix) : fLocalMatrix(localMatrix) { 2445b8d2c3af76e704926cf5915ad0e6af59a232e61Terry Jan Reedy this->initClassID<EllipseGeometryProcessor>(); 245e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType); 246e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType); 2475b8d2c3af76e704926cf5915ad0e6af59a232e61Terry Jan Reedy fInEllipseOffset = &this->addVertexAttrib("inEllipseOffset", kVec2f_GrVertexAttribType); 24889f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters fInEllipseRadii = &this->addVertexAttrib("inEllipseRadii", kVec4f_GrVertexAttribType); 2495b8d2c3af76e704926cf5915ad0e6af59a232e61Terry Jan Reedy fStroke = stroke; 25089f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters } 25189f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters 252428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson ~EllipseGeometryProcessor() override {} 253428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson 254428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson const char* name() const override { return "EllipseEdge"; } 2559dc3a36c849c15c227a8af218cfb215abe7b3c48Terry Jan Reedy 2569dc3a36c849c15c227a8af218cfb215abe7b3c48Terry Jan Reedy void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override { 257e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang GLSLProcessor::GenKey(*this, caps, b); 258e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang } 259e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang 260e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override { 261e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang return new GLSLProcessor(); 262e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang } 263e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang 264e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wangprivate: 265e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang class GLSLProcessor : public GrGLSLGeometryProcessor { 266e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang public: 267e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang GLSLProcessor() {} 268e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang 269e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { 270e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang const EllipseGeometryProcessor& egp = args.fGP.cast<EllipseGeometryProcessor>(); 271e411b6629fb5f7bc01bec89df75737875ce6d8f5Dingyuan Wang GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; 27289f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; 27389f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; 27489f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters 27589f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters // emit attributes 27689f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters varyingHandler->emitAttributes(egp); 27789f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters 27889f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters GrGLSLVertToFrag ellipseOffsets(kHalf2_GrSLType); 27989f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters varyingHandler->addVarying("EllipseOffsets", &ellipseOffsets); 28089f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters vertBuilder->codeAppendf("%s = %s;", ellipseOffsets.vsOut(), 28189f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters egp.fInEllipseOffset->fName); 28289f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters 2835b8d2c3af76e704926cf5915ad0e6af59a232e61Terry Jan Reedy GrGLSLVertToFrag ellipseRadii(kHalf4_GrSLType); 284ba4af493a5bcece67bc6ae369bfea0592b10f9e5Christian Heimes varyingHandler->addVarying("EllipseRadii", &ellipseRadii); 2855b8d2c3af76e704926cf5915ad0e6af59a232e61Terry Jan Reedy vertBuilder->codeAppendf("%s = %s;", ellipseRadii.vsOut(), egp.fInEllipseRadii->fName); 2865b8d2c3af76e704926cf5915ad0e6af59a232e61Terry Jan Reedy 28789f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder; 288428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson // setup pass through color 289428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson varyingHandler->addPassThroughAttribute(egp.fInColor, args.fOutputColor); 290428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson 29189f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters // Setup position 2927544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov this->writeOutputPosition(vertBuilder, gpArgs, egp.fInPosition->fName); 29389f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters 29489f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters // emit transforms 295ba4af493a5bcece67bc6ae369bfea0592b10f9e5Christian Heimes this->emitTransforms(vertBuilder, 296ba4af493a5bcece67bc6ae369bfea0592b10f9e5Christian Heimes varyingHandler, 297ba4af493a5bcece67bc6ae369bfea0592b10f9e5Christian Heimes uniformHandler, 298ba4af493a5bcece67bc6ae369bfea0592b10f9e5Christian Heimes gpArgs->fPositionVar, 299ba4af493a5bcece67bc6ae369bfea0592b10f9e5Christian Heimes egp.fInPosition->fName, 300ba4af493a5bcece67bc6ae369bfea0592b10f9e5Christian Heimes egp.fLocalMatrix, 301ba4af493a5bcece67bc6ae369bfea0592b10f9e5Christian Heimes args.fFPCoordTransformHandler); 302ba4af493a5bcece67bc6ae369bfea0592b10f9e5Christian Heimes 30389f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters // for outer curve 30489f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters fragBuilder->codeAppendf("half2 scaledOffset = %s*%s.xy;", ellipseOffsets.fsIn(), 30589f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters ellipseRadii.fsIn()); 30689f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters fragBuilder->codeAppend("half test = dot(scaledOffset, scaledOffset) - 1.0;"); 30789f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters fragBuilder->codeAppendf("half2 grad = 2.0*scaledOffset*%s.xy;", ellipseRadii.fsIn()); 30889f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters fragBuilder->codeAppend("half grad_dot = dot(grad, grad);"); 30989f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters 31089f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters // avoid calling inversesqrt on zero. 31189f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);"); 31289f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters fragBuilder->codeAppend("half invlen = inversesqrt(grad_dot);"); 31389f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters fragBuilder->codeAppend("half edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);"); 31489f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters 31568c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger // for inner curve 316428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson if (egp.fStroke) { 31768c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger fragBuilder->codeAppendf("scaledOffset = %s*%s.zw;", ellipseOffsets.fsIn(), 31868c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger ellipseRadii.fsIn()); 319428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;"); 320428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson fragBuilder->codeAppendf("grad = 2.0*scaledOffset*%s.zw;", ellipseRadii.fsIn()); 32168c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));"); 32268c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);"); 32389f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters } 32489f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters 32589f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage); 32689f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters } 32789f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters 32868c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger static void GenKey(const GrGeometryProcessor& gp, 329ff8d0873aabe54009af533f9f6a76fa91392a80aBerker Peksag const GrShaderCaps&, 330ff8d0873aabe54009af533f9f6a76fa91392a80aBerker Peksag GrProcessorKeyBuilder* b) { 331428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson const EllipseGeometryProcessor& egp = gp.cast<EllipseGeometryProcessor>(); 33268c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger uint16_t key = egp.fStroke ? 0x1 : 0x0; 333428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson key |= egp.fLocalMatrix.hasPerspective() ? 0x2 : 0x0; 334428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson b->add32(key); 33568c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger } 33668c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger 33789f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc, 338428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson FPCoordTransformIter&& transformIter) override { 339428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson const EllipseGeometryProcessor& egp = primProc.cast<EllipseGeometryProcessor>(); 340428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson this->setTransformDataHelper(egp.fLocalMatrix, pdman, &transformIter); 341428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson } 34268c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger 343428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson private: 344d3afadaa4908df544e0181c11199e59b1bfb5c37Benjamin Peterson typedef GrGLSLGeometryProcessor INHERITED; 345d3afadaa4908df544e0181c11199e59b1bfb5c37Benjamin Peterson }; 346d3afadaa4908df544e0181c11199e59b1bfb5c37Benjamin Peterson 347d3afadaa4908df544e0181c11199e59b1bfb5c37Benjamin Peterson const Attribute* fInPosition; 348d3afadaa4908df544e0181c11199e59b1bfb5c37Benjamin Peterson const Attribute* fInColor; 349d3afadaa4908df544e0181c11199e59b1bfb5c37Benjamin Peterson const Attribute* fInEllipseOffset; 350d3afadaa4908df544e0181c11199e59b1bfb5c37Benjamin Peterson const Attribute* fInEllipseRadii; 351d3afadaa4908df544e0181c11199e59b1bfb5c37Benjamin Peterson SkMatrix fLocalMatrix; 352d3afadaa4908df544e0181c11199e59b1bfb5c37Benjamin Peterson bool fStroke; 353d3afadaa4908df544e0181c11199e59b1bfb5c37Benjamin Peterson 354d3afadaa4908df544e0181c11199e59b1bfb5c37Benjamin Peterson GR_DECLARE_GEOMETRY_PROCESSOR_TEST 355428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson 356d1fa3db52de5f337e9aae5f3baad16fe62da2d0fRaymond Hettinger typedef GrGeometryProcessor INHERITED; 357428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson}; 3584bcc796acc17f8ab7eeaa3f7faa6a61135b2c090Ezio Melotti 359428de65ca99492436130165bfbaeb56d6d1daec7Trent NelsonGR_DEFINE_GEOMETRY_PROCESSOR_TEST(EllipseGeometryProcessor); 360428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson 361428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson#if GR_TEST_UTILS 36243e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xiclunask_sp<GrGeometryProcessor> EllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) { 363428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson return sk_sp<GrGeometryProcessor>( 364428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson new EllipseGeometryProcessor(d->fRandom->nextBool(), GrTest::TestMatrix(d->fRandom))); 36543e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna} 36643e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna#endif 36743e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna 368689a55809818a846d2733241642572840d20570bBenjamin Peterson/////////////////////////////////////////////////////////////////////////////// 369428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson 370428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson/** 371428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson * The output of this effect is a modulation of the input color and coverage for an ellipse, 372c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon * specified as a 2D offset from center for both the outer and inner paths (if stroked). The 373c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon * implict equation used is for a unit circle (x^2 + y^2 - 1 = 0) and the edge corrected by 374c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon * using differentials. 375c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon * 376428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson * The result is device-independent and can be used with any affine matrix. 377428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson */ 378689a55809818a846d2733241642572840d20570bBenjamin Peterson 379428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelsonenum class DIEllipseStyle { kStroke = 0, kHairline, kFill }; 380428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson 381428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelsonclass DIEllipseGeometryProcessor : public GrGeometryProcessor { 382428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelsonpublic: 383428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson DIEllipseGeometryProcessor(const SkMatrix& viewMatrix, DIEllipseStyle style) 384428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson : fViewMatrix(viewMatrix) { 385428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson this->initClassID<DIEllipseGeometryProcessor>(); 386428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType, 38763674f4b52aa7c2832fec09a026e24cd521e491bMartin v. Löwis kHigh_GrSLPrecision); 38863674f4b52aa7c2832fec09a026e24cd521e491bMartin v. Löwis fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType); 38963674f4b52aa7c2832fec09a026e24cd521e491bMartin v. Löwis fInEllipseOffsets0 = &this->addVertexAttrib("inEllipseOffsets0", kVec2f_GrVertexAttribType); 39063674f4b52aa7c2832fec09a026e24cd521e491bMartin v. Löwis fInEllipseOffsets1 = &this->addVertexAttrib("inEllipseOffsets1", kVec2f_GrVertexAttribType); 391428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson fStyle = style; 392c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon } 393c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon 394c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon ~DIEllipseGeometryProcessor() override {} 395c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon 396433f32c3be3b23adc4ec389ff9e78f49c7288f3dBenjamin Peterson const char* name() const override { return "DIEllipseEdge"; } 397dafea851901fc1de278ad79727d3b44f46ba5a31Serhiy Storchaka 398dafea851901fc1de278ad79727d3b44f46ba5a31Serhiy Storchaka void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override { 399433f32c3be3b23adc4ec389ff9e78f49c7288f3dBenjamin Peterson GLSLProcessor::GenKey(*this, caps, b); 400dafea851901fc1de278ad79727d3b44f46ba5a31Serhiy Storchaka } 401433f32c3be3b23adc4ec389ff9e78f49c7288f3dBenjamin Peterson 402433f32c3be3b23adc4ec389ff9e78f49c7288f3dBenjamin Peterson GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override { 403433f32c3be3b23adc4ec389ff9e78f49c7288f3dBenjamin Peterson return new GLSLProcessor(); 404433f32c3be3b23adc4ec389ff9e78f49c7288f3dBenjamin Peterson } 405c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon 406c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannonprivate: 407c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon class GLSLProcessor : public GrGLSLGeometryProcessor { 408c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon public: 409c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon GLSLProcessor() : fViewMatrix(SkMatrix::InvalidMatrix()) {} 410c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon 411433f32c3be3b23adc4ec389ff9e78f49c7288f3dBenjamin Peterson void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { 4121613ed810801df8327ae6f55b7785fec3a9dc6bbBenjamin Peterson const DIEllipseGeometryProcessor& diegp = args.fGP.cast<DIEllipseGeometryProcessor>(); 41311f0b41e9de3805441ddd4142df9f6b7f4432ca7Florent Xicluna GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder; 4141613ed810801df8327ae6f55b7785fec3a9dc6bbBenjamin Peterson GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; 415c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; 416c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon 417c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon // emit attributes 418c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon varyingHandler->emitAttributes(diegp); 419c33f3f2339fd3217a0c6fe3df916616abab2fab4Brett Cannon 4201613ed810801df8327ae6f55b7785fec3a9dc6bbBenjamin Peterson GrGLSLVertToFrag offsets0(kHalf2_GrSLType); 421433f32c3be3b23adc4ec389ff9e78f49c7288f3dBenjamin Peterson varyingHandler->addVarying("EllipseOffsets0", &offsets0); 422428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson vertBuilder->codeAppendf("%s = %s;", offsets0.vsOut(), diegp.fInEllipseOffsets0->fName); 423428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson 424433f32c3be3b23adc4ec389ff9e78f49c7288f3dBenjamin Peterson GrGLSLVertToFrag offsets1(kHalf2_GrSLType); 425428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson varyingHandler->addVarying("EllipseOffsets1", &offsets1); 426428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson vertBuilder->codeAppendf("%s = %s;", offsets1.vsOut(), diegp.fInEllipseOffsets1->fName); 427689a55809818a846d2733241642572840d20570bBenjamin Peterson 428428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder; 429689a55809818a846d2733241642572840d20570bBenjamin Peterson varyingHandler->addPassThroughAttribute(diegp.fInColor, args.fOutputColor); 430428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson 431428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson // Setup position 432428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson this->writeOutputPosition(vertBuilder, 433428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson uniformHandler, 434768c16ce0273a74fa846cc388753280b17b02cfcSerhiy Storchaka gpArgs, 435768c16ce0273a74fa846cc388753280b17b02cfcSerhiy Storchaka diegp.fInPosition->fName, 436428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson diegp.fViewMatrix, 437428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson &fViewMatrixUniform); 438428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson 439689a55809818a846d2733241642572840d20570bBenjamin Peterson // emit transforms 440428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson this->emitTransforms(vertBuilder, 441428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson varyingHandler, 442428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson uniformHandler, 443428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson gpArgs->fPositionVar, 444428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson diegp.fInPosition->fName, 445689a55809818a846d2733241642572840d20570bBenjamin Peterson args.fFPCoordTransformHandler); 446428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson 447428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson // for outer curve 44858c0752a33253641c1423fac2d4ef3f623fbcb46Victor Stinner fragBuilder->codeAppendf("half2 scaledOffset = %s.xy;", offsets0.fsIn()); 44958c0752a33253641c1423fac2d4ef3f623fbcb46Victor Stinner fragBuilder->codeAppend("half test = dot(scaledOffset, scaledOffset) - 1.0;"); 45058c0752a33253641c1423fac2d4ef3f623fbcb46Victor Stinner fragBuilder->codeAppendf("half2 duvdx = dFdx(%s);", offsets0.fsIn()); 45158c0752a33253641c1423fac2d4ef3f623fbcb46Victor Stinner fragBuilder->codeAppendf("half2 duvdy = dFdy(%s);", offsets0.fsIn()); 452969175091c4556e5b7e128ba91ae39f0b80153afVictor Stinner fragBuilder->codeAppendf( 453387729e183365a366c48fce7a9abfcaf4ec6ff4eVictor Stinner "half2 grad = half2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y," 454387729e183365a366c48fce7a9abfcaf4ec6ff4eVictor Stinner " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);", 455387729e183365a366c48fce7a9abfcaf4ec6ff4eVictor Stinner offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn(), offsets0.fsIn()); 456387729e183365a366c48fce7a9abfcaf4ec6ff4eVictor Stinner 457387729e183365a366c48fce7a9abfcaf4ec6ff4eVictor Stinner fragBuilder->codeAppend("half grad_dot = dot(grad, grad);"); 458387729e183365a366c48fce7a9abfcaf4ec6ff4eVictor Stinner // avoid calling inversesqrt on zero. 459387729e183365a366c48fce7a9abfcaf4ec6ff4eVictor Stinner fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);"); 460387729e183365a366c48fce7a9abfcaf4ec6ff4eVictor Stinner fragBuilder->codeAppend("half invlen = inversesqrt(grad_dot);"); 461387729e183365a366c48fce7a9abfcaf4ec6ff4eVictor Stinner if (DIEllipseStyle::kHairline == diegp.fStyle) { 46258c0752a33253641c1423fac2d4ef3f623fbcb46Victor Stinner // can probably do this with one step 46358c0752a33253641c1423fac2d4ef3f623fbcb46Victor Stinner fragBuilder->codeAppend("half edgeAlpha = clamp(1.0-test*invlen, 0.0, 1.0);"); 464428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson fragBuilder->codeAppend("edgeAlpha *= clamp(1.0+test*invlen, 0.0, 1.0);"); 465428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson } else { 466ff8d0873aabe54009af533f9f6a76fa91392a80aBerker Peksag fragBuilder->codeAppend("half edgeAlpha = clamp(0.5-test*invlen, 0.0, 1.0);"); 467d1fa3db52de5f337e9aae5f3baad16fe62da2d0fRaymond Hettinger } 46843e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna 469ff8d0873aabe54009af533f9f6a76fa91392a80aBerker Peksag // for inner curve 47068c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger if (DIEllipseStyle::kStroke == diegp.fStyle) { 471428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson fragBuilder->codeAppendf("scaledOffset = %s.xy;", offsets1.fsIn()); 4728ac1495a6a1d18111a626cec0c7f2eb67df3edb3Tim Peters fragBuilder->codeAppend("test = dot(scaledOffset, scaledOffset) - 1.0;"); 473d1fa3db52de5f337e9aae5f3baad16fe62da2d0fRaymond Hettinger fragBuilder->codeAppendf("duvdx = dFdx(%s);", offsets1.fsIn()); 474d1fa3db52de5f337e9aae5f3baad16fe62da2d0fRaymond Hettinger fragBuilder->codeAppendf("duvdy = dFdy(%s);", offsets1.fsIn()); 475d1fa3db52de5f337e9aae5f3baad16fe62da2d0fRaymond Hettinger fragBuilder->codeAppendf( 476d1fa3db52de5f337e9aae5f3baad16fe62da2d0fRaymond Hettinger "grad = half2(2.0*%s.x*duvdx.x + 2.0*%s.y*duvdx.y," 47743e4ea1b17ac912e4f8e55e256b96be0c57a88eeFlorent Xicluna " 2.0*%s.x*duvdy.x + 2.0*%s.y*duvdy.y);", 4788ac1495a6a1d18111a626cec0c7f2eb67df3edb3Tim Peters offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn(), offsets1.fsIn()); 479428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson fragBuilder->codeAppend("invlen = inversesqrt(dot(grad, grad));"); 480428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson fragBuilder->codeAppend("edgeAlpha *= clamp(0.5+test*invlen, 0.0, 1.0);"); 481428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson } 482d1fa3db52de5f337e9aae5f3baad16fe62da2d0fRaymond Hettinger 48321db77e396c00c0490b6344a130bdbcef62bfa73Benjamin Peterson fragBuilder->codeAppendf("%s = half4(edgeAlpha);", args.fOutputCoverage); 48421db77e396c00c0490b6344a130bdbcef62bfa73Benjamin Peterson } 48581dd8b9594d88ff1d2c8f5efea687645bbc36d6fBenjamin Peterson 486428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson static void GenKey(const GrGeometryProcessor& gp, 48781dd8b9594d88ff1d2c8f5efea687645bbc36d6fBenjamin Peterson const GrShaderCaps&, 48881dd8b9594d88ff1d2c8f5efea687645bbc36d6fBenjamin Peterson GrProcessorKeyBuilder* b) { 48981dd8b9594d88ff1d2c8f5efea687645bbc36d6fBenjamin Peterson const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>(); 490428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson uint16_t key = static_cast<uint16_t>(diegp.fStyle); 491428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson key |= ComputePosKey(diegp.fViewMatrix) << 10; 492428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson b->add32(key); 4931aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum } 49433856de84d1115a18b699e0ca93c3b921bc6a1afBenjamin Peterson 495de65527e4b0925692f0d75f388116b7958a390bbGuido van Rossum void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& gp, 496a90c78b9186f5ba8d91d3be0e684f81f2068c771Guido van Rossum FPCoordTransformIter&& transformIter) override { 497fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum const DIEllipseGeometryProcessor& diegp = gp.cast<DIEllipseGeometryProcessor>(); 4981aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum 49996ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov if (!diegp.fViewMatrix.isIdentity() && !fViewMatrix.cheapEqualTo(diegp.fViewMatrix)) { 5007544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov fViewMatrix = diegp.fViewMatrix; 50196ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov float viewMatrix[3 * 3]; 50296ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov GrGLSLGetMatrix<3>(viewMatrix, fViewMatrix); 50396ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov pdman.setMatrix3f(fViewMatrixUniform, viewMatrix); 5047544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov } 505428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter); 506689a55809818a846d2733241642572840d20570bBenjamin Peterson } 507689a55809818a846d2733241642572840d20570bBenjamin Peterson 508689a55809818a846d2733241642572840d20570bBenjamin Peterson private: 509a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger SkMatrix fViewMatrix; 5100fe14383a8576ee5eb4a6aa83c96484281b360fdBenjamin Peterson UniformHandle fViewMatrixUniform; 51168c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger 51268c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger typedef GrGLSLGeometryProcessor INHERITED; 51368c04534182f2c09783b6506701a8bc25c98b4a9Raymond Hettinger }; 514428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson 515428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson const Attribute* fInPosition; 516428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson const Attribute* fInColor; 517428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson const Attribute* fInEllipseOffsets0; 518a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson const Attribute* fInEllipseOffsets1; 519fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum SkMatrix fViewMatrix; 520fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum DIEllipseStyle fStyle; 521fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum 522de65527e4b0925692f0d75f388116b7958a390bbGuido van Rossum GR_DECLARE_GEOMETRY_PROCESSOR_TEST 523ce36ad8a467d914eb5c91f33835b9eaea18ee93bCollin Winter 5243b631775b26b866e072cd3340f303bf5903af883Guido van Rossum typedef GrGeometryProcessor INHERITED; 5253b631775b26b866e072cd3340f303bf5903af883Guido van Rossum}; 5263b631775b26b866e072cd3340f303bf5903af883Guido van Rossum 527a48db39992aaf4d83759135e4c9a2c9757764e62Raymond HettingerGR_DEFINE_GEOMETRY_PROCESSOR_TEST(DIEllipseGeometryProcessor); 52889f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters 529de65527e4b0925692f0d75f388116b7958a390bbGuido van Rossum#if GR_TEST_UTILS 530a90c78b9186f5ba8d91d3be0e684f81f2068c771Guido van Rossumsk_sp<GrGeometryProcessor> DIEllipseGeometryProcessor::TestCreate(GrProcessorTestData* d) { 531de65527e4b0925692f0d75f388116b7958a390bbGuido van Rossum return sk_sp<GrGeometryProcessor>(new DIEllipseGeometryProcessor( 532a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger GrTest::TestMatrix(d->fRandom), (DIEllipseStyle)(d->fRandom->nextRangeU(0, 2)))); 533a90c78b9186f5ba8d91d3be0e684f81f2068c771Guido van Rossum} 534fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum#endif 535a90c78b9186f5ba8d91d3be0e684f81f2068c771Guido van Rossum 536de65527e4b0925692f0d75f388116b7958a390bbGuido van Rossum/////////////////////////////////////////////////////////////////////////////// 537fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum 538fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum// We have two possible cases for geometry for a circle: 539a90c78b9186f5ba8d91d3be0e684f81f2068c771Guido van Rossum 540fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum// In the case of a normal fill, we draw geometry for the circle as an octagon. 541fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossumstatic const uint16_t gFillCircleIndices[] = { 5421aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum // enter the octagon 543fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum // clang-format off 544fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum 0, 1, 8, 1, 2, 8, 5451aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum 2, 3, 8, 3, 4, 8, 546a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson 4, 5, 8, 5, 6, 8, 547a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson 6, 7, 8, 7, 0, 8 548a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson // clang-format on 549a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson}; 550a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson 551a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson// For stroked circles, we use two nested octagons. 552a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Petersonstatic const uint16_t gStrokeCircleIndices[] = { 553a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson // enter the octagon 554a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson // clang-format off 555a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson 0, 1, 9, 0, 9, 8, 556a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson 1, 2, 10, 1, 10, 9, 5571aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum 2, 3, 11, 2, 11, 10, 5581aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum 3, 4, 12, 3, 12, 11, 55989f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters 4, 5, 13, 4, 13, 12, 56089f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters 5, 6, 14, 5, 14, 13, 56189f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters 6, 7, 15, 6, 15, 14, 562a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger 7, 0, 8, 7, 8, 15, 56389f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters // clang-format on 564a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger}; 56589f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters 56689f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters 567a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettingerstatic const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices); 5681aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossumstatic const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices); 5691aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossumstatic const int kVertsPerStrokeCircle = 16; 570fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossumstatic const int kVertsPerFillCircle = 9; 571fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum 572fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossumstatic int circle_type_to_vert_count(bool stroked) { 573a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle; 574fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum} 575da99d1cbfeedafd41263ac2d1b397d57c14ab28eRaymond Hettinger 576da99d1cbfeedafd41263ac2d1b397d57c14ab28eRaymond Hettingerstatic int circle_type_to_index_count(bool stroked) { 57700ee7baf49430d8a6eed355a5fd7a05179325747Thomas Wouters return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle; 57800ee7baf49430d8a6eed355a5fd7a05179325747Thomas Wouters} 579fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum 5807544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanovstatic const uint16_t* circle_type_to_indices(bool stroked) { 58196ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov return stroked ? gStrokeCircleIndices : gFillCircleIndices; 58296ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov} 58396ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov 58496ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov/////////////////////////////////////////////////////////////////////////////// 5857544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov 586a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettingerclass CircleOp final : public GrMeshDrawOp { 587fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossumprivate: 58896ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov using Helper = GrSimpleMeshDrawOpHelper; 58996ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov 59096ec934e755355cfc5af036db8641646b7ddb45eYury Selivanovpublic: 59196ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov DEFINE_OP_CLASS_ID 59296ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov 593fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum /** Optional extra params to render a partial arc rather than a full circle. */ 594de65527e4b0925692f0d75f388116b7958a390bbGuido van Rossum struct ArcParams { 595ce36ad8a467d914eb5c91f33835b9eaea18ee93bCollin Winter SkScalar fStartAngleRadians; 596fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum SkScalar fSweepAngleRadians; 597fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum bool fUseCenter; 598fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum }; 59910a99b024df0d30911b198146d0206c8f6d0d6c7Antoine Pitrou 6003b631775b26b866e072cd3340f303bf5903af883Guido van Rossum static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix, 6013b631775b26b866e072cd3340f303bf5903af883Guido van Rossum SkPoint center, SkScalar radius, const GrStyle& style, 602de65527e4b0925692f0d75f388116b7958a390bbGuido van Rossum const ArcParams* arcParams = nullptr) { 6032cc3b4ba9ffa658784da03f14a0a068e2c61d1b3Ezio Melotti SkASSERT(circle_stays_circle(viewMatrix)); 6042cc3b4ba9ffa658784da03f14a0a068e2c61d1b3Ezio Melotti if (style.hasPathEffect()) { 6051aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum return nullptr; 606fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum } 607dde002899db8d04ac25d630fcc3a27e8bbf282eaGeorg Brandl const SkStrokeRec& stroke = style.strokeRec(); 608dde002899db8d04ac25d630fcc3a27e8bbf282eaGeorg Brandl SkStrokeRec::Style recStyle = stroke.getStyle(); 609a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger if (arcParams) { 6101aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum // Arc support depends on the style. 6117544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov switch (recStyle) { 6127544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov case SkStrokeRec::kStrokeAndFill_Style: 6137544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov // This produces a strange result that this op doesn't implement. 61496ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov return nullptr; 61596ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov case SkStrokeRec::kFill_Style: 61696ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov // This supports all fills. 61796ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov break; 61896ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov case SkStrokeRec::kStroke_Style: // fall through 61996ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov case SkStrokeRec::kHairline_Style: 62096ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov // Strokes that don't use the center point are supported with butt cap. 6211aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum if (arcParams->fUseCenter || stroke.getCap() != SkPaint::kButt_Cap) { 62289f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters return nullptr; 6237544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov } 6247544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov break; 6257544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov } 626a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger } 6271c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith return Helper::FactoryHelper<CircleOp>(std::move(paint), viewMatrix, center, radius, style, 6289d6897accc49f40414fbecafeb1c65562c6e4647Guido van Rossum arcParams); 62910a99b024df0d30911b198146d0206c8f6d0d6c7Antoine Pitrou } 6303b631775b26b866e072cd3340f303bf5903af883Guido van Rossum 6313b631775b26b866e072cd3340f303bf5903af883Guido van Rossum CircleOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix, 6323b631775b26b866e072cd3340f303bf5903af883Guido van Rossum SkPoint center, SkScalar radius, const GrStyle& style, const ArcParams* arcParams) 6331aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum : GrMeshDrawOp(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) { 634a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger const SkStrokeRec& stroke = style.strokeRec(); 635fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum SkStrokeRec::Style recStyle = stroke.getStyle(); 6361aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum 6371aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum viewMatrix.mapPoints(¢er, 1); 638a90c78b9186f5ba8d91d3be0e684f81f2068c771Guido van Rossum radius = viewMatrix.mapRadius(radius); 639fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum SkScalar strokeWidth = viewMatrix.mapRadius(stroke.getWidth()); 6401c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith 6411c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith bool isStrokeOnly = 6421c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith SkStrokeRec::kStroke_Style == recStyle || SkStrokeRec::kHairline_Style == recStyle; 6431c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == recStyle; 6441c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith 6451c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith SkScalar innerRadius = -SK_ScalarHalf; 6461c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith SkScalar outerRadius = radius; 6471c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith SkScalar halfWidth = 0; 6481c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith if (hasStroke) { 649a7161e7facdfa1d6f673beb16a95a647ce764b32Berker Peksag if (SkScalarNearlyZero(strokeWidth)) { 6501c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith halfWidth = SK_ScalarHalf; 6511c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith } else { 6521c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith halfWidth = SkScalarHalf(strokeWidth); 6531c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith } 654fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum 6551aec32363f25693e0c3ff81feddf620850b4955dGuido van Rossum outerRadius += halfWidth; 6561c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith if (isStrokeOnly) { 6571c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith innerRadius = radius - halfWidth; 6581c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith } 6591c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith } 6601c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith 6611c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith // The radii are outset for two reasons. First, it allows the shader to simply perform 6621c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith // simpler computation because the computed alpha is zero, rather than 50%, at the radius. 6631c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith // Second, the outer radius is used to compute the verts of the bounding box that is 6641c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith // rendered and the outset ensures the box will cover all partially covered by the circle. 665de65527e4b0925692f0d75f388116b7958a390bbGuido van Rossum outerRadius += SK_ScalarHalf; 666a90c78b9186f5ba8d91d3be0e684f81f2068c771Guido van Rossum innerRadius -= SK_ScalarHalf; 667fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum bool stroked = isStrokeOnly && innerRadius > 0.0f; 668fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum fViewMatrixIfUsingLocalCoords = viewMatrix; 669a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger 6701c8222c80a8a534bd9357aafc3fba7b6927efc15Eric V. Smith // This makes every point fully inside the intersection plane. 67133856de84d1115a18b699e0ca93c3b921bc6a1afBenjamin Peterson static constexpr SkScalar kUnusedIsectPlane[] = {0.f, 0.f, 1.f}; 6727544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov // This makes every point fully outside the union plane. 67396ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov static constexpr SkScalar kUnusedUnionPlane[] = {0.f, 0.f, 0.f}; 6747544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov SkRect devBounds = SkRect::MakeLTRB(center.fX - outerRadius, center.fY - outerRadius, 6757544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov center.fX + outerRadius, center.fY + outerRadius); 6767544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov if (arcParams) { 6777544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov // The shader operates in a space where the circle is translated to be centered at the 6787544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov // origin. Here we compute points on the unit circle at the starting and ending angles. 6797544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov SkPoint startPoint, stopPoint; 6807544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov startPoint.fY = SkScalarSinCos(arcParams->fStartAngleRadians, &startPoint.fX); 6817544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov SkScalar endAngle = arcParams->fStartAngleRadians + arcParams->fSweepAngleRadians; 6827544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov stopPoint.fY = SkScalarSinCos(endAngle, &stopPoint.fX); 6837544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov 6847544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov // Adjust the start and end points based on the view matrix (to handle rotated arcs) 6857544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov startPoint = viewMatrix.mapVector(startPoint.fX, startPoint.fY); 6867544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov stopPoint = viewMatrix.mapVector(stopPoint.fX, stopPoint.fY); 6877544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov startPoint.normalize(); 6887544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov stopPoint.normalize(); 68996ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov 69096ec934e755355cfc5af036db8641646b7ddb45eYury Selivanov // If the matrix included scale (on one axis) we need to swap our start and end points 6917544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov if ((viewMatrix.getScaleX() < 0) != (viewMatrix.getScaleY() < 0)) { 6927544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov SkTSwap(startPoint, stopPoint); 6937544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov } 6947544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov 6957544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov // Like a fill without useCenter, butt-cap stroke can be implemented by clipping against 6967544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov // radial lines. However, in both cases we have to be careful about the half-circle. 6977544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov // case. In that case the two radial lines are equal and so that edge gets clipped 6987544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov // twice. Since the shared edge goes through the center we fall back on the useCenter 6997544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov // case. 7007544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov bool useCenter = 7017544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov (arcParams->fUseCenter || isStrokeOnly) && 7023b631775b26b866e072cd3340f303bf5903af883Guido van Rossum !SkScalarNearlyEqual(SkScalarAbs(arcParams->fSweepAngleRadians), SK_ScalarPI); 7033b631775b26b866e072cd3340f303bf5903af883Guido van Rossum if (useCenter) { 704fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum SkVector norm0 = {startPoint.fY, -startPoint.fX}; 705a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson SkVector norm1 = {stopPoint.fY, -stopPoint.fX}; 706a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson if (arcParams->fSweepAngleRadians > 0) { 707a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson norm0.negate(); 708a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson } else { 7097544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov norm1.negate(); 7107544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov } 7117544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov fClipPlane = true; 712a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger if (SkScalarAbs(arcParams->fSweepAngleRadians) > SK_ScalarPI) { 713fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum fCircles.emplace_back(Circle{ 714a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger color, 715de65527e4b0925692f0d75f388116b7958a390bbGuido van Rossum innerRadius, 716a0dfa82eca0f4b9855b6e234f9b21e5d60c88a10Benjamin Peterson outerRadius, 717fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum {norm0.fX, norm0.fY, 0.5f}, 7187544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]}, 7197544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov {norm1.fX, norm1.fY, 0.5f}, 7207544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov devBounds, 7217544508f0245173bff5866aa1598c8f6cce1fc5fYury Selivanov stroked}); 722fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum fClipPlaneIsect = false; 723a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger fClipPlaneUnion = true; 724a48db39992aaf4d83759135e4c9a2c9757764e62Raymond Hettinger } else { 725fc6f5339a99d103928bce9eda605564f2a9e8477Guido van Rossum fCircles.emplace_back(Circle{ 726428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson color, 727428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson innerRadius, 728428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson outerRadius, 729428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson {norm0.fX, norm0.fY, 0.5f}, 730428de65ca99492436130165bfbaeb56d6d1daec7Trent Nelson {norm1.fX, norm1.fY, 0.5f}, 7316c60d099e5ed97ee0026687c1ec3401cca49c0c2Raymond Hettinger {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]}, 73214c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge devBounds, 73314c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge stroked}); 73414c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge fClipPlaneIsect = true; 73514c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge fClipPlaneUnion = false; 73614c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge } 73714c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge } else { 73814c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge // We clip to a secant of the original circle. 73914c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge startPoint.scale(radius); 74014c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge stopPoint.scale(radius); 74114c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge SkVector norm = {startPoint.fY - stopPoint.fY, stopPoint.fX - startPoint.fX}; 74214c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge norm.normalize(); 74314c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge if (arcParams->fSweepAngleRadians > 0) { 74414c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge norm.negate(); 74514c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge } 74614c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge SkScalar d = -norm.dot(startPoint) + 0.5f; 74714c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge 74814c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge fCircles.emplace_back( 74914c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge Circle{color, 75014c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge innerRadius, 75114c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge outerRadius, 75214c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge {norm.fX, norm.fY, d}, 75314c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]}, 75400c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]}, 75500c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge devBounds, 75614c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge stroked}); 75714c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge fClipPlane = true; 75814c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge fClipPlaneIsect = false; 75914c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge fClipPlaneUnion = false; 76014c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge } 76114c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge } else { 762969175091c4556e5b7e128ba91ae39f0b80153afVictor Stinner fCircles.emplace_back( 76314c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge Circle{color, 76414c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge innerRadius, 76514c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge outerRadius, 76614c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]}, 76714c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge {kUnusedIsectPlane[0], kUnusedIsectPlane[1], kUnusedIsectPlane[2]}, 76814c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge {kUnusedUnionPlane[0], kUnusedUnionPlane[1], kUnusedUnionPlane[2]}, 76914c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge devBounds, 77000c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge stroked}); 77100c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge fClipPlane = false; 77200c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge fClipPlaneIsect = false; 77314c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge fClipPlaneUnion = false; 77414c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge } 77500c7f85298b9803371b4a0019ce8732ed8a2dd3bMeador Inge // Use the original radius and stroke radius for the bounds so that it does not include the 77614c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge // AA bloat. 77714c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge radius += halfWidth; 77814c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge this->setBounds( 77914c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge {center.fX - radius, center.fY - radius, center.fX + radius, center.fY + radius}, 78014c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge HasAABloat::kYes, IsZeroArea::kNo); 78114c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge fVertCount = circle_type_to_vert_count(stroked); 78214c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge fIndexCount = circle_type_to_index_count(stroked); 78314c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge fAllFill = !stroked; 784f7a17b48d748e1835bcf9df86fb7fb318bb020f8Andrew Svetlov } 78514c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge 78614c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge const char* name() const override { return "CircleOp"; } 78714c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge 78814c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge SkString dumpInfo() const override { 78914c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge SkString string; 79014c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge for (int i = 0; i < fCircles.count(); ++i) { 79114c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge string.appendf( 7926c60d099e5ed97ee0026687c1ec3401cca49c0c2Raymond Hettinger "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]," 79314c0f03b587e3ec9679cf19a0c5f598c45157429Meador Inge "InnerRad: %.2f, OuterRad: %.2f\n", 794 fCircles[i].fColor, fCircles[i].fDevBounds.fLeft, fCircles[i].fDevBounds.fTop, 795 fCircles[i].fDevBounds.fRight, fCircles[i].fDevBounds.fBottom, 796 fCircles[i].fInnerRadius, fCircles[i].fOuterRadius); 797 } 798 string += fHelper.dumpInfo(); 799 string += INHERITED::dumpInfo(); 800 return string; 801 } 802 803 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override { 804 GrColor* color = &fCircles.front().fColor; 805 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel, 806 color); 807 } 808 809 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } 810 811private: 812 void onPrepareDraws(Target* target) override { 813 SkMatrix localMatrix; 814 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) { 815 return; 816 } 817 818 // Setup geometry processor 819 sk_sp<GrGeometryProcessor> gp(new CircleGeometryProcessor( 820 !fAllFill, fClipPlane, fClipPlaneIsect, fClipPlaneUnion, localMatrix)); 821 822 struct CircleVertex { 823 SkPoint fPos; 824 GrColor fColor; 825 SkPoint fOffset; 826 SkScalar fOuterRadius; 827 SkScalar fInnerRadius; 828 // These planes may or may not be present in the vertex buffer. 829 SkScalar fHalfPlanes[3][3]; 830 }; 831 832 size_t vertexStride = gp->getVertexStride(); 833 SkASSERT(vertexStride == 834 sizeof(CircleVertex) - (fClipPlane ? 0 : 3 * sizeof(SkScalar)) - 835 (fClipPlaneIsect ? 0 : 3 * sizeof(SkScalar)) - 836 (fClipPlaneUnion ? 0 : 3 * sizeof(SkScalar))); 837 838 const GrBuffer* vertexBuffer; 839 int firstVertex; 840 char* vertices = (char*)target->makeVertexSpace(vertexStride, fVertCount, &vertexBuffer, 841 &firstVertex); 842 if (!vertices) { 843 SkDebugf("Could not allocate vertices\n"); 844 return; 845 } 846 847 const GrBuffer* indexBuffer = nullptr; 848 int firstIndex = 0; 849 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex); 850 if (!indices) { 851 SkDebugf("Could not allocate indices\n"); 852 return; 853 } 854 855 int currStartVertex = 0; 856 for (const auto& circle : fCircles) { 857 SkScalar innerRadius = circle.fInnerRadius; 858 SkScalar outerRadius = circle.fOuterRadius; 859 GrColor color = circle.fColor; 860 const SkRect& bounds = circle.fDevBounds; 861 862 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 0 * vertexStride); 863 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 1 * vertexStride); 864 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 2 * vertexStride); 865 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 3 * vertexStride); 866 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 4 * vertexStride); 867 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 5 * vertexStride); 868 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 6 * vertexStride); 869 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 7 * vertexStride); 870 871 // The inner radius in the vertex data must be specified in normalized space. 872 innerRadius = innerRadius / outerRadius; 873 874 SkPoint center = SkPoint::Make(bounds.centerX(), bounds.centerY()); 875 SkScalar halfWidth = 0.5f * bounds.width(); 876 SkScalar octOffset = 0.41421356237f; // sqrt(2) - 1 877 878 v0->fPos = center + SkPoint::Make(-octOffset * halfWidth, -halfWidth); 879 v0->fColor = color; 880 v0->fOffset = SkPoint::Make(-octOffset, -1); 881 v0->fOuterRadius = outerRadius; 882 v0->fInnerRadius = innerRadius; 883 884 v1->fPos = center + SkPoint::Make(octOffset * halfWidth, -halfWidth); 885 v1->fColor = color; 886 v1->fOffset = SkPoint::Make(octOffset, -1); 887 v1->fOuterRadius = outerRadius; 888 v1->fInnerRadius = innerRadius; 889 890 v2->fPos = center + SkPoint::Make(halfWidth, -octOffset * halfWidth); 891 v2->fColor = color; 892 v2->fOffset = SkPoint::Make(1, -octOffset); 893 v2->fOuterRadius = outerRadius; 894 v2->fInnerRadius = innerRadius; 895 896 v3->fPos = center + SkPoint::Make(halfWidth, octOffset * halfWidth); 897 v3->fColor = color; 898 v3->fOffset = SkPoint::Make(1, octOffset); 899 v3->fOuterRadius = outerRadius; 900 v3->fInnerRadius = innerRadius; 901 902 v4->fPos = center + SkPoint::Make(octOffset * halfWidth, halfWidth); 903 v4->fColor = color; 904 v4->fOffset = SkPoint::Make(octOffset, 1); 905 v4->fOuterRadius = outerRadius; 906 v4->fInnerRadius = innerRadius; 907 908 v5->fPos = center + SkPoint::Make(-octOffset * halfWidth, halfWidth); 909 v5->fColor = color; 910 v5->fOffset = SkPoint::Make(-octOffset, 1); 911 v5->fOuterRadius = outerRadius; 912 v5->fInnerRadius = innerRadius; 913 914 v6->fPos = center + SkPoint::Make(-halfWidth, octOffset * halfWidth); 915 v6->fColor = color; 916 v6->fOffset = SkPoint::Make(-1, octOffset); 917 v6->fOuterRadius = outerRadius; 918 v6->fInnerRadius = innerRadius; 919 920 v7->fPos = center + SkPoint::Make(-halfWidth, -octOffset * halfWidth); 921 v7->fColor = color; 922 v7->fOffset = SkPoint::Make(-1, -octOffset); 923 v7->fOuterRadius = outerRadius; 924 v7->fInnerRadius = innerRadius; 925 926 if (fClipPlane) { 927 memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 928 memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 929 memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 930 memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 931 memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 932 memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 933 memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 934 memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 935 } 936 int unionIdx = 1; 937 if (fClipPlaneIsect) { 938 memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 939 memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 940 memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 941 memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 942 memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 943 memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 944 memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 945 memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 946 unionIdx = 2; 947 } 948 if (fClipPlaneUnion) { 949 memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 950 memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 951 memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 952 memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 953 memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 954 memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 955 memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 956 memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 957 } 958 959 if (circle.fStroked) { 960 // compute the inner ring 961 CircleVertex* v0 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride); 962 CircleVertex* v1 = reinterpret_cast<CircleVertex*>(vertices + 9 * vertexStride); 963 CircleVertex* v2 = reinterpret_cast<CircleVertex*>(vertices + 10 * vertexStride); 964 CircleVertex* v3 = reinterpret_cast<CircleVertex*>(vertices + 11 * vertexStride); 965 CircleVertex* v4 = reinterpret_cast<CircleVertex*>(vertices + 12 * vertexStride); 966 CircleVertex* v5 = reinterpret_cast<CircleVertex*>(vertices + 13 * vertexStride); 967 CircleVertex* v6 = reinterpret_cast<CircleVertex*>(vertices + 14 * vertexStride); 968 CircleVertex* v7 = reinterpret_cast<CircleVertex*>(vertices + 15 * vertexStride); 969 970 // cosine and sine of pi/8 971 SkScalar c = 0.923579533f; 972 SkScalar s = 0.382683432f; 973 SkScalar r = circle.fInnerRadius; 974 975 v0->fPos = center + SkPoint::Make(-s * r, -c * r); 976 v0->fColor = color; 977 v0->fOffset = SkPoint::Make(-s * innerRadius, -c * innerRadius); 978 v0->fOuterRadius = outerRadius; 979 v0->fInnerRadius = innerRadius; 980 981 v1->fPos = center + SkPoint::Make(s * r, -c * r); 982 v1->fColor = color; 983 v1->fOffset = SkPoint::Make(s * innerRadius, -c * innerRadius); 984 v1->fOuterRadius = outerRadius; 985 v1->fInnerRadius = innerRadius; 986 987 v2->fPos = center + SkPoint::Make(c * r, -s * r); 988 v2->fColor = color; 989 v2->fOffset = SkPoint::Make(c * innerRadius, -s * innerRadius); 990 v2->fOuterRadius = outerRadius; 991 v2->fInnerRadius = innerRadius; 992 993 v3->fPos = center + SkPoint::Make(c * r, s * r); 994 v3->fColor = color; 995 v3->fOffset = SkPoint::Make(c * innerRadius, s * innerRadius); 996 v3->fOuterRadius = outerRadius; 997 v3->fInnerRadius = innerRadius; 998 999 v4->fPos = center + SkPoint::Make(s * r, c * r); 1000 v4->fColor = color; 1001 v4->fOffset = SkPoint::Make(s * innerRadius, c * innerRadius); 1002 v4->fOuterRadius = outerRadius; 1003 v4->fInnerRadius = innerRadius; 1004 1005 v5->fPos = center + SkPoint::Make(-s * r, c * r); 1006 v5->fColor = color; 1007 v5->fOffset = SkPoint::Make(-s * innerRadius, c * innerRadius); 1008 v5->fOuterRadius = outerRadius; 1009 v5->fInnerRadius = innerRadius; 1010 1011 v6->fPos = center + SkPoint::Make(-c * r, s * r); 1012 v6->fColor = color; 1013 v6->fOffset = SkPoint::Make(-c * innerRadius, s * innerRadius); 1014 v6->fOuterRadius = outerRadius; 1015 v6->fInnerRadius = innerRadius; 1016 1017 v7->fPos = center + SkPoint::Make(-c * r, -s * r); 1018 v7->fColor = color; 1019 v7->fOffset = SkPoint::Make(-c * innerRadius, -s * innerRadius); 1020 v7->fOuterRadius = outerRadius; 1021 v7->fInnerRadius = innerRadius; 1022 1023 if (fClipPlane) { 1024 memcpy(v0->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1025 memcpy(v1->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1026 memcpy(v2->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1027 memcpy(v3->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1028 memcpy(v4->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1029 memcpy(v5->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1030 memcpy(v6->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1031 memcpy(v7->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1032 } 1033 int unionIdx = 1; 1034 if (fClipPlaneIsect) { 1035 memcpy(v0->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1036 memcpy(v1->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1037 memcpy(v2->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1038 memcpy(v3->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1039 memcpy(v4->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1040 memcpy(v5->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1041 memcpy(v6->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1042 memcpy(v7->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1043 unionIdx = 2; 1044 } 1045 if (fClipPlaneUnion) { 1046 memcpy(v0->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1047 memcpy(v1->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1048 memcpy(v2->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1049 memcpy(v3->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1050 memcpy(v4->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1051 memcpy(v5->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1052 memcpy(v6->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1053 memcpy(v7->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1054 } 1055 } else { 1056 // filled 1057 CircleVertex* v8 = reinterpret_cast<CircleVertex*>(vertices + 8 * vertexStride); 1058 v8->fPos = center; 1059 v8->fColor = color; 1060 v8->fOffset = SkPoint::Make(0, 0); 1061 v8->fOuterRadius = outerRadius; 1062 v8->fInnerRadius = innerRadius; 1063 if (fClipPlane) { 1064 memcpy(v8->fHalfPlanes[0], circle.fClipPlane, 3 * sizeof(SkScalar)); 1065 } 1066 int unionIdx = 1; 1067 if (fClipPlaneIsect) { 1068 memcpy(v8->fHalfPlanes[1], circle.fIsectPlane, 3 * sizeof(SkScalar)); 1069 unionIdx = 2; 1070 } 1071 if (fClipPlaneUnion) { 1072 memcpy(v8->fHalfPlanes[unionIdx], circle.fUnionPlane, 3 * sizeof(SkScalar)); 1073 } 1074 } 1075 1076 const uint16_t* primIndices = circle_type_to_indices(circle.fStroked); 1077 const int primIndexCount = circle_type_to_index_count(circle.fStroked); 1078 for (int i = 0; i < primIndexCount; ++i) { 1079 *indices++ = primIndices[i] + currStartVertex; 1080 } 1081 1082 currStartVertex += circle_type_to_vert_count(circle.fStroked); 1083 vertices += circle_type_to_vert_count(circle.fStroked) * vertexStride; 1084 } 1085 1086 GrMesh mesh(GrPrimitiveType::kTriangles); 1087 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1); 1088 mesh.setVertexData(vertexBuffer, firstVertex); 1089 target->draw(gp.get(), fHelper.makePipeline(target), mesh); 1090 } 1091 1092 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 1093 CircleOp* that = t->cast<CircleOp>(); 1094 1095 // can only represent 65535 unique vertices with 16-bit indices 1096 if (fVertCount + that->fVertCount > 65536) { 1097 return false; 1098 } 1099 1100 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { 1101 return false; 1102 } 1103 1104 if (fHelper.usesLocalCoords() && 1105 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) { 1106 return false; 1107 } 1108 1109 // Because we've set up the ops that don't use the planes with noop values 1110 // we can just accumulate used planes by later ops. 1111 fClipPlane |= that->fClipPlane; 1112 fClipPlaneIsect |= that->fClipPlaneIsect; 1113 fClipPlaneUnion |= that->fClipPlaneUnion; 1114 1115 fCircles.push_back_n(that->fCircles.count(), that->fCircles.begin()); 1116 this->joinBounds(*that); 1117 fVertCount += that->fVertCount; 1118 fIndexCount += that->fIndexCount; 1119 fAllFill = fAllFill && that->fAllFill; 1120 return true; 1121 } 1122 1123 struct Circle { 1124 GrColor fColor; 1125 SkScalar fInnerRadius; 1126 SkScalar fOuterRadius; 1127 SkScalar fClipPlane[3]; 1128 SkScalar fIsectPlane[3]; 1129 SkScalar fUnionPlane[3]; 1130 SkRect fDevBounds; 1131 bool fStroked; 1132 }; 1133 1134 SkMatrix fViewMatrixIfUsingLocalCoords; 1135 Helper fHelper; 1136 SkSTArray<1, Circle, true> fCircles; 1137 int fVertCount; 1138 int fIndexCount; 1139 bool fAllFill; 1140 bool fClipPlane; 1141 bool fClipPlaneIsect; 1142 bool fClipPlaneUnion; 1143 1144 typedef GrMeshDrawOp INHERITED; 1145}; 1146 1147/////////////////////////////////////////////////////////////////////////////// 1148 1149class EllipseOp : public GrMeshDrawOp { 1150private: 1151 using Helper = GrSimpleMeshDrawOpHelper; 1152 1153 struct DeviceSpaceParams { 1154 SkPoint fCenter; 1155 SkScalar fXRadius; 1156 SkScalar fYRadius; 1157 SkScalar fInnerXRadius; 1158 SkScalar fInnerYRadius; 1159 }; 1160 1161public: 1162 DEFINE_OP_CLASS_ID 1163 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix, 1164 const SkRect& ellipse, const SkStrokeRec& stroke) { 1165 DeviceSpaceParams params; 1166 // do any matrix crunching before we reset the draw state for device coords 1167 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY()); 1168 viewMatrix.mapPoints(¶ms.fCenter, 1); 1169 SkScalar ellipseXRadius = SkScalarHalf(ellipse.width()); 1170 SkScalar ellipseYRadius = SkScalarHalf(ellipse.height()); 1171 params.fXRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * ellipseXRadius + 1172 viewMatrix[SkMatrix::kMSkewX] * ellipseYRadius); 1173 params.fYRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewY] * ellipseXRadius + 1174 viewMatrix[SkMatrix::kMScaleY] * ellipseYRadius); 1175 1176 // do (potentially) anisotropic mapping of stroke 1177 SkVector scaledStroke; 1178 SkScalar strokeWidth = stroke.getWidth(); 1179 scaledStroke.fX = SkScalarAbs( 1180 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY])); 1181 scaledStroke.fY = SkScalarAbs( 1182 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY])); 1183 1184 SkStrokeRec::Style style = stroke.getStyle(); 1185 bool isStrokeOnly = 1186 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style; 1187 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style; 1188 1189 params.fInnerXRadius = 0; 1190 params.fInnerYRadius = 0; 1191 if (hasStroke) { 1192 if (SkScalarNearlyZero(scaledStroke.length())) { 1193 scaledStroke.set(SK_ScalarHalf, SK_ScalarHalf); 1194 } else { 1195 scaledStroke.scale(SK_ScalarHalf); 1196 } 1197 1198 // we only handle thick strokes for near-circular ellipses 1199 if (scaledStroke.length() > SK_ScalarHalf && 1200 (0.5f * params.fXRadius > params.fYRadius || 1201 0.5f * params.fYRadius > params.fXRadius)) { 1202 return nullptr; 1203 } 1204 1205 // we don't handle it if curvature of the stroke is less than curvature of the ellipse 1206 if (scaledStroke.fX * (params.fXRadius * params.fYRadius) < 1207 (scaledStroke.fY * scaledStroke.fY) * params.fXRadius || 1208 scaledStroke.fY * (params.fXRadius * params.fXRadius) < 1209 (scaledStroke.fX * scaledStroke.fX) * params.fYRadius) { 1210 return nullptr; 1211 } 1212 1213 // this is legit only if scale & translation (which should be the case at the moment) 1214 if (isStrokeOnly) { 1215 params.fInnerXRadius = params.fXRadius - scaledStroke.fX; 1216 params.fInnerYRadius = params.fYRadius - scaledStroke.fY; 1217 } 1218 1219 params.fXRadius += scaledStroke.fX; 1220 params.fYRadius += scaledStroke.fY; 1221 } 1222 return Helper::FactoryHelper<EllipseOp>(std::move(paint), viewMatrix, params, stroke); 1223 } 1224 1225 EllipseOp(const Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix, 1226 const DeviceSpaceParams& params, const SkStrokeRec& stroke) 1227 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) { 1228 SkStrokeRec::Style style = stroke.getStyle(); 1229 bool isStrokeOnly = 1230 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style; 1231 1232 fEllipses.emplace_back(Ellipse{color, params.fXRadius, params.fYRadius, 1233 params.fInnerXRadius, params.fInnerYRadius, 1234 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius, 1235 params.fCenter.fY - params.fYRadius, 1236 params.fCenter.fX + params.fXRadius, 1237 params.fCenter.fY + params.fYRadius)}); 1238 1239 this->setBounds(fEllipses.back().fDevBounds, HasAABloat::kYes, IsZeroArea::kNo); 1240 1241 // Outset bounds to include half-pixel width antialiasing. 1242 fEllipses[0].fDevBounds.outset(SK_ScalarHalf, SK_ScalarHalf); 1243 1244 fStroked = isStrokeOnly && params.fInnerXRadius > 0 && params.fInnerYRadius > 0; 1245 fViewMatrixIfUsingLocalCoords = viewMatrix; 1246 } 1247 1248 const char* name() const override { return "EllipseOp"; } 1249 1250 SkString dumpInfo() const override { 1251 SkString string; 1252 string.appendf("Stroked: %d\n", fStroked); 1253 for (const auto& geo : fEllipses) { 1254 string.appendf( 1255 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], " 1256 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n", 1257 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight, 1258 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius, 1259 geo.fInnerYRadius); 1260 } 1261 string += fHelper.dumpInfo(); 1262 string += INHERITED::dumpInfo(); 1263 return string; 1264 } 1265 1266 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override { 1267 GrColor* color = &fEllipses.front().fColor; 1268 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel, 1269 color); 1270 } 1271 1272 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } 1273 1274private: 1275 void onPrepareDraws(Target* target) override { 1276 SkMatrix localMatrix; 1277 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) { 1278 return; 1279 } 1280 1281 // Setup geometry processor 1282 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix)); 1283 1284 QuadHelper helper; 1285 size_t vertexStride = gp->getVertexStride(); 1286 SkASSERT(vertexStride == sizeof(EllipseVertex)); 1287 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>( 1288 helper.init(target, vertexStride, fEllipses.count())); 1289 if (!verts) { 1290 return; 1291 } 1292 1293 for (const auto& ellipse : fEllipses) { 1294 GrColor color = ellipse.fColor; 1295 SkScalar xRadius = ellipse.fXRadius; 1296 SkScalar yRadius = ellipse.fYRadius; 1297 1298 // Compute the reciprocals of the radii here to save time in the shader 1299 SkScalar xRadRecip = SkScalarInvert(xRadius); 1300 SkScalar yRadRecip = SkScalarInvert(yRadius); 1301 SkScalar xInnerRadRecip = SkScalarInvert(ellipse.fInnerXRadius); 1302 SkScalar yInnerRadRecip = SkScalarInvert(ellipse.fInnerYRadius); 1303 1304 // fOffsets are expanded from xyRadii to include the half-pixel antialiasing width. 1305 SkScalar xMaxOffset = xRadius + SK_ScalarHalf; 1306 SkScalar yMaxOffset = yRadius + SK_ScalarHalf; 1307 1308 // The inner radius in the vertex data must be specified in normalized space. 1309 verts[0].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fTop); 1310 verts[0].fColor = color; 1311 verts[0].fOffset = SkPoint::Make(-xMaxOffset, -yMaxOffset); 1312 verts[0].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 1313 verts[0].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 1314 1315 verts[1].fPos = SkPoint::Make(ellipse.fDevBounds.fLeft, ellipse.fDevBounds.fBottom); 1316 verts[1].fColor = color; 1317 verts[1].fOffset = SkPoint::Make(-xMaxOffset, yMaxOffset); 1318 verts[1].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 1319 verts[1].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 1320 1321 verts[2].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fBottom); 1322 verts[2].fColor = color; 1323 verts[2].fOffset = SkPoint::Make(xMaxOffset, yMaxOffset); 1324 verts[2].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 1325 verts[2].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 1326 1327 verts[3].fPos = SkPoint::Make(ellipse.fDevBounds.fRight, ellipse.fDevBounds.fTop); 1328 verts[3].fColor = color; 1329 verts[3].fOffset = SkPoint::Make(xMaxOffset, -yMaxOffset); 1330 verts[3].fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 1331 verts[3].fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 1332 1333 verts += kVerticesPerQuad; 1334 } 1335 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target)); 1336 } 1337 1338 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 1339 EllipseOp* that = t->cast<EllipseOp>(); 1340 1341 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { 1342 return false; 1343 } 1344 1345 if (fStroked != that->fStroked) { 1346 return false; 1347 } 1348 1349 if (fHelper.usesLocalCoords() && 1350 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) { 1351 return false; 1352 } 1353 1354 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin()); 1355 this->joinBounds(*that); 1356 return true; 1357 } 1358 1359 struct Ellipse { 1360 GrColor fColor; 1361 SkScalar fXRadius; 1362 SkScalar fYRadius; 1363 SkScalar fInnerXRadius; 1364 SkScalar fInnerYRadius; 1365 SkRect fDevBounds; 1366 }; 1367 1368 SkMatrix fViewMatrixIfUsingLocalCoords; 1369 Helper fHelper; 1370 bool fStroked; 1371 SkSTArray<1, Ellipse, true> fEllipses; 1372 1373 typedef GrMeshDrawOp INHERITED; 1374}; 1375 1376///////////////////////////////////////////////////////////////////////////////////////////////// 1377 1378class DIEllipseOp : public GrMeshDrawOp { 1379private: 1380 using Helper = GrSimpleMeshDrawOpHelper; 1381 1382 struct DeviceSpaceParams { 1383 SkPoint fCenter; 1384 SkScalar fXRadius; 1385 SkScalar fYRadius; 1386 SkScalar fInnerXRadius; 1387 SkScalar fInnerYRadius; 1388 DIEllipseStyle fStyle; 1389 }; 1390 1391public: 1392 DEFINE_OP_CLASS_ID 1393 1394 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix, 1395 const SkRect& ellipse, const SkStrokeRec& stroke) { 1396 DeviceSpaceParams params; 1397 params.fCenter = SkPoint::Make(ellipse.centerX(), ellipse.centerY()); 1398 params.fXRadius = SkScalarHalf(ellipse.width()); 1399 params.fYRadius = SkScalarHalf(ellipse.height()); 1400 1401 SkStrokeRec::Style style = stroke.getStyle(); 1402 params.fStyle = (SkStrokeRec::kStroke_Style == style) 1403 ? DIEllipseStyle::kStroke 1404 : (SkStrokeRec::kHairline_Style == style) 1405 ? DIEllipseStyle::kHairline 1406 : DIEllipseStyle::kFill; 1407 1408 params.fInnerXRadius = 0; 1409 params.fInnerYRadius = 0; 1410 if (SkStrokeRec::kFill_Style != style && SkStrokeRec::kHairline_Style != style) { 1411 SkScalar strokeWidth = stroke.getWidth(); 1412 1413 if (SkScalarNearlyZero(strokeWidth)) { 1414 strokeWidth = SK_ScalarHalf; 1415 } else { 1416 strokeWidth *= SK_ScalarHalf; 1417 } 1418 1419 // we only handle thick strokes for near-circular ellipses 1420 if (strokeWidth > SK_ScalarHalf && 1421 (SK_ScalarHalf * params.fXRadius > params.fYRadius || 1422 SK_ScalarHalf * params.fYRadius > params.fXRadius)) { 1423 return nullptr; 1424 } 1425 1426 // we don't handle it if curvature of the stroke is less than curvature of the ellipse 1427 if (strokeWidth * (params.fYRadius * params.fYRadius) < 1428 (strokeWidth * strokeWidth) * params.fXRadius) { 1429 return nullptr; 1430 } 1431 if (strokeWidth * (params.fXRadius * params.fXRadius) < 1432 (strokeWidth * strokeWidth) * params.fYRadius) { 1433 return nullptr; 1434 } 1435 1436 // set inner radius (if needed) 1437 if (SkStrokeRec::kStroke_Style == style) { 1438 params.fInnerXRadius = params.fXRadius - strokeWidth; 1439 params.fInnerYRadius = params.fYRadius - strokeWidth; 1440 } 1441 1442 params.fXRadius += strokeWidth; 1443 params.fYRadius += strokeWidth; 1444 } 1445 if (DIEllipseStyle::kStroke == params.fStyle && 1446 (params.fInnerXRadius <= 0 || params.fInnerYRadius <= 0)) { 1447 params.fStyle = DIEllipseStyle::kFill; 1448 } 1449 return Helper::FactoryHelper<DIEllipseOp>(std::move(paint), params, viewMatrix); 1450 } 1451 1452 DIEllipseOp(Helper::MakeArgs& helperArgs, GrColor color, const DeviceSpaceParams& params, 1453 const SkMatrix& viewMatrix) 1454 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) { 1455 // This expands the outer rect so that after CTM we end up with a half-pixel border 1456 SkScalar a = viewMatrix[SkMatrix::kMScaleX]; 1457 SkScalar b = viewMatrix[SkMatrix::kMSkewX]; 1458 SkScalar c = viewMatrix[SkMatrix::kMSkewY]; 1459 SkScalar d = viewMatrix[SkMatrix::kMScaleY]; 1460 SkScalar geoDx = SK_ScalarHalf / SkScalarSqrt(a * a + c * c); 1461 SkScalar geoDy = SK_ScalarHalf / SkScalarSqrt(b * b + d * d); 1462 1463 fEllipses.emplace_back( 1464 Ellipse{viewMatrix, color, params.fXRadius, params.fYRadius, params.fInnerXRadius, 1465 params.fInnerYRadius, geoDx, geoDy, params.fStyle, 1466 SkRect::MakeLTRB(params.fCenter.fX - params.fXRadius - geoDx, 1467 params.fCenter.fY - params.fYRadius - geoDy, 1468 params.fCenter.fX + params.fXRadius + geoDx, 1469 params.fCenter.fY + params.fYRadius + geoDy)}); 1470 this->setTransformedBounds(fEllipses[0].fBounds, viewMatrix, HasAABloat::kYes, 1471 IsZeroArea::kNo); 1472 } 1473 1474 const char* name() const override { return "DIEllipseOp"; } 1475 1476 SkString dumpInfo() const override { 1477 SkString string; 1478 for (const auto& geo : fEllipses) { 1479 string.appendf( 1480 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], XRad: %.2f, " 1481 "YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f, GeoDX: %.2f, " 1482 "GeoDY: %.2f\n", 1483 geo.fColor, geo.fBounds.fLeft, geo.fBounds.fTop, geo.fBounds.fRight, 1484 geo.fBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius, 1485 geo.fInnerYRadius, geo.fGeoDx, geo.fGeoDy); 1486 } 1487 string += fHelper.dumpInfo(); 1488 string += INHERITED::dumpInfo(); 1489 return string; 1490 } 1491 1492 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override { 1493 GrColor* color = &fEllipses.front().fColor; 1494 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel, 1495 color); 1496 } 1497 1498 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } 1499 1500private: 1501 void onPrepareDraws(Target* target) override { 1502 // Setup geometry processor 1503 sk_sp<GrGeometryProcessor> gp( 1504 new DIEllipseGeometryProcessor(this->viewMatrix(), this->style())); 1505 1506 size_t vertexStride = gp->getVertexStride(); 1507 SkASSERT(vertexStride == sizeof(DIEllipseVertex)); 1508 QuadHelper helper; 1509 DIEllipseVertex* verts = reinterpret_cast<DIEllipseVertex*>( 1510 helper.init(target, vertexStride, fEllipses.count())); 1511 if (!verts) { 1512 return; 1513 } 1514 1515 for (const auto& ellipse : fEllipses) { 1516 GrColor color = ellipse.fColor; 1517 SkScalar xRadius = ellipse.fXRadius; 1518 SkScalar yRadius = ellipse.fYRadius; 1519 1520 const SkRect& bounds = ellipse.fBounds; 1521 1522 // This adjusts the "radius" to include the half-pixel border 1523 SkScalar offsetDx = ellipse.fGeoDx / xRadius; 1524 SkScalar offsetDy = ellipse.fGeoDy / yRadius; 1525 1526 SkScalar innerRatioX = xRadius / ellipse.fInnerXRadius; 1527 SkScalar innerRatioY = yRadius / ellipse.fInnerYRadius; 1528 1529 verts[0].fPos = SkPoint::Make(bounds.fLeft, bounds.fTop); 1530 verts[0].fColor = color; 1531 verts[0].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, -1.0f - offsetDy); 1532 verts[0].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, -innerRatioY - offsetDy); 1533 1534 verts[1].fPos = SkPoint::Make(bounds.fLeft, bounds.fBottom); 1535 verts[1].fColor = color; 1536 verts[1].fOuterOffset = SkPoint::Make(-1.0f - offsetDx, 1.0f + offsetDy); 1537 verts[1].fInnerOffset = SkPoint::Make(-innerRatioX - offsetDx, innerRatioY + offsetDy); 1538 1539 verts[2].fPos = SkPoint::Make(bounds.fRight, bounds.fBottom); 1540 verts[2].fColor = color; 1541 verts[2].fOuterOffset = SkPoint::Make(1.0f + offsetDx, 1.0f + offsetDy); 1542 verts[2].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, innerRatioY + offsetDy); 1543 1544 verts[3].fPos = SkPoint::Make(bounds.fRight, bounds.fTop); 1545 verts[3].fColor = color; 1546 verts[3].fOuterOffset = SkPoint::Make(1.0f + offsetDx, -1.0f - offsetDy); 1547 verts[3].fInnerOffset = SkPoint::Make(innerRatioX + offsetDx, -innerRatioY - offsetDy); 1548 1549 verts += kVerticesPerQuad; 1550 } 1551 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target)); 1552 } 1553 1554 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 1555 DIEllipseOp* that = t->cast<DIEllipseOp>(); 1556 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { 1557 return false; 1558 } 1559 1560 if (this->style() != that->style()) { 1561 return false; 1562 } 1563 1564 // TODO rewrite to allow positioning on CPU 1565 if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) { 1566 return false; 1567 } 1568 1569 fEllipses.push_back_n(that->fEllipses.count(), that->fEllipses.begin()); 1570 this->joinBounds(*that); 1571 return true; 1572 } 1573 1574 const SkMatrix& viewMatrix() const { return fEllipses[0].fViewMatrix; } 1575 DIEllipseStyle style() const { return fEllipses[0].fStyle; } 1576 1577 struct Ellipse { 1578 SkMatrix fViewMatrix; 1579 GrColor fColor; 1580 SkScalar fXRadius; 1581 SkScalar fYRadius; 1582 SkScalar fInnerXRadius; 1583 SkScalar fInnerYRadius; 1584 SkScalar fGeoDx; 1585 SkScalar fGeoDy; 1586 DIEllipseStyle fStyle; 1587 SkRect fBounds; 1588 }; 1589 1590 Helper fHelper; 1591 SkSTArray<1, Ellipse, true> fEllipses; 1592 1593 typedef GrMeshDrawOp INHERITED; 1594}; 1595 1596/////////////////////////////////////////////////////////////////////////////// 1597 1598// We have three possible cases for geometry for a roundrect. 1599// 1600// In the case of a normal fill or a stroke, we draw the roundrect as a 9-patch: 1601// ____________ 1602// |_|________|_| 1603// | | | | 1604// | | | | 1605// | | | | 1606// |_|________|_| 1607// |_|________|_| 1608// 1609// For strokes, we don't draw the center quad. 1610// 1611// For circular roundrects, in the case where the stroke width is greater than twice 1612// the corner radius (overstroke), we add additional geometry to mark out the rectangle 1613// in the center. The shared vertices are duplicated so we can set a different outer radius 1614// for the fill calculation. 1615// ____________ 1616// |_|________|_| 1617// | |\ ____ /| | 1618// | | | | | | 1619// | | |____| | | 1620// |_|/______\|_| 1621// |_|________|_| 1622// 1623// We don't draw the center quad from the fill rect in this case. 1624// 1625// For filled rrects that need to provide a distance vector we resuse the overstroke 1626// geometry but make the inner rect degenerate (either a point or a horizontal or 1627// vertical line). 1628 1629static const uint16_t gOverstrokeRRectIndices[] = { 1630 // clang-format off 1631 // overstroke quads 1632 // we place this at the beginning so that we can skip these indices when rendering normally 1633 16, 17, 19, 16, 19, 18, 1634 19, 17, 23, 19, 23, 21, 1635 21, 23, 22, 21, 22, 20, 1636 22, 16, 18, 22, 18, 20, 1637 1638 // corners 1639 0, 1, 5, 0, 5, 4, 1640 2, 3, 7, 2, 7, 6, 1641 8, 9, 13, 8, 13, 12, 1642 10, 11, 15, 10, 15, 14, 1643 1644 // edges 1645 1, 2, 6, 1, 6, 5, 1646 4, 5, 9, 4, 9, 8, 1647 6, 7, 11, 6, 11, 10, 1648 9, 10, 14, 9, 14, 13, 1649 1650 // center 1651 // we place this at the end so that we can ignore these indices when not rendering as filled 1652 5, 6, 10, 5, 10, 9, 1653 // clang-format on 1654}; 1655 1656// fill and standard stroke indices skip the overstroke "ring" 1657static const uint16_t* gStandardRRectIndices = gOverstrokeRRectIndices + 6 * 4; 1658 1659// overstroke count is arraysize minus the center indices 1660static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gOverstrokeRRectIndices) - 6; 1661// fill count skips overstroke indices and includes center 1662static const int kIndicesPerFillRRect = kIndicesPerOverstrokeRRect - 6 * 4 + 6; 1663// stroke count is fill count minus center indices 1664static const int kIndicesPerStrokeRRect = kIndicesPerFillRRect - 6; 1665static const int kVertsPerStandardRRect = 16; 1666static const int kVertsPerOverstrokeRRect = 24; 1667 1668enum RRectType { 1669 kFill_RRectType, 1670 kStroke_RRectType, 1671 kOverstroke_RRectType, 1672}; 1673 1674static int rrect_type_to_vert_count(RRectType type) { 1675 switch (type) { 1676 case kFill_RRectType: 1677 case kStroke_RRectType: 1678 return kVertsPerStandardRRect; 1679 case kOverstroke_RRectType: 1680 return kVertsPerOverstrokeRRect; 1681 } 1682 SK_ABORT("Invalid type"); 1683 return 0; 1684} 1685 1686static int rrect_type_to_index_count(RRectType type) { 1687 switch (type) { 1688 case kFill_RRectType: 1689 return kIndicesPerFillRRect; 1690 case kStroke_RRectType: 1691 return kIndicesPerStrokeRRect; 1692 case kOverstroke_RRectType: 1693 return kIndicesPerOverstrokeRRect; 1694 } 1695 SK_ABORT("Invalid type"); 1696 return 0; 1697} 1698 1699static const uint16_t* rrect_type_to_indices(RRectType type) { 1700 switch (type) { 1701 case kFill_RRectType: 1702 case kStroke_RRectType: 1703 return gStandardRRectIndices; 1704 case kOverstroke_RRectType: 1705 return gOverstrokeRRectIndices; 1706 } 1707 SK_ABORT("Invalid type"); 1708 return 0; 1709} 1710 1711/////////////////////////////////////////////////////////////////////////////////////////////////// 1712 1713// For distance computations in the interior of filled rrects we: 1714// 1715// add a interior degenerate (point or line) rect 1716// each vertex of that rect gets -outerRad as its radius 1717// this makes the computation of the distance to the outer edge be negative 1718// negative values are caught and then handled differently in the GP's onEmitCode 1719// each vertex is also given the normalized x & y distance from the interior rect's edge 1720// the GP takes the min of those depths +1 to get the normalized distance to the outer edge 1721 1722class CircularRRectOp : public GrMeshDrawOp { 1723private: 1724 using Helper = GrSimpleMeshDrawOpHelper; 1725 1726public: 1727 DEFINE_OP_CLASS_ID 1728 1729 // A devStrokeWidth <= 0 indicates a fill only. If devStrokeWidth > 0 then strokeOnly indicates 1730 // whether the rrect is only stroked or stroked and filled. 1731 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix, 1732 const SkRect& devRect, float devRadius, 1733 float devStrokeWidth, bool strokeOnly) { 1734 return Helper::FactoryHelper<CircularRRectOp>(std::move(paint), viewMatrix, devRect, 1735 devRadius, devStrokeWidth, strokeOnly); 1736 } 1737 CircularRRectOp(Helper::MakeArgs& helperArgs, GrColor color, const SkMatrix& viewMatrix, 1738 const SkRect& devRect, float devRadius, float devStrokeWidth, bool strokeOnly) 1739 : INHERITED(ClassID()) 1740 , fViewMatrixIfUsingLocalCoords(viewMatrix) 1741 , fHelper(helperArgs, GrAAType::kCoverage) { 1742 SkRect bounds = devRect; 1743 SkASSERT(!(devStrokeWidth <= 0 && strokeOnly)); 1744 SkScalar innerRadius = 0.0f; 1745 SkScalar outerRadius = devRadius; 1746 SkScalar halfWidth = 0; 1747 RRectType type = kFill_RRectType; 1748 if (devStrokeWidth > 0) { 1749 if (SkScalarNearlyZero(devStrokeWidth)) { 1750 halfWidth = SK_ScalarHalf; 1751 } else { 1752 halfWidth = SkScalarHalf(devStrokeWidth); 1753 } 1754 1755 if (strokeOnly) { 1756 // Outset stroke by 1/4 pixel 1757 devStrokeWidth += 0.25f; 1758 // If stroke is greater than width or height, this is still a fill 1759 // Otherwise we compute stroke params 1760 if (devStrokeWidth <= devRect.width() && devStrokeWidth <= devRect.height()) { 1761 innerRadius = devRadius - halfWidth; 1762 type = (innerRadius >= 0) ? kStroke_RRectType : kOverstroke_RRectType; 1763 } 1764 } 1765 outerRadius += halfWidth; 1766 bounds.outset(halfWidth, halfWidth); 1767 } 1768 1769 // The radii are outset for two reasons. First, it allows the shader to simply perform 1770 // simpler computation because the computed alpha is zero, rather than 50%, at the radius. 1771 // Second, the outer radius is used to compute the verts of the bounding box that is 1772 // rendered and the outset ensures the box will cover all partially covered by the rrect 1773 // corners. 1774 outerRadius += SK_ScalarHalf; 1775 innerRadius -= SK_ScalarHalf; 1776 1777 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo); 1778 1779 // Expand the rect for aa to generate correct vertices. 1780 bounds.outset(SK_ScalarHalf, SK_ScalarHalf); 1781 1782 fRRects.emplace_back(RRect{color, innerRadius, outerRadius, bounds, type}); 1783 fVertCount = rrect_type_to_vert_count(type); 1784 fIndexCount = rrect_type_to_index_count(type); 1785 fAllFill = (kFill_RRectType == type); 1786 } 1787 1788 const char* name() const override { return "CircularRRectOp"; } 1789 1790 SkString dumpInfo() const override { 1791 SkString string; 1792 for (int i = 0; i < fRRects.count(); ++i) { 1793 string.appendf( 1794 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f]," 1795 "InnerRad: %.2f, OuterRad: %.2f\n", 1796 fRRects[i].fColor, fRRects[i].fDevBounds.fLeft, fRRects[i].fDevBounds.fTop, 1797 fRRects[i].fDevBounds.fRight, fRRects[i].fDevBounds.fBottom, 1798 fRRects[i].fInnerRadius, fRRects[i].fOuterRadius); 1799 } 1800 string += fHelper.dumpInfo(); 1801 string += INHERITED::dumpInfo(); 1802 return string; 1803 } 1804 1805 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override { 1806 GrColor* color = &fRRects.front().fColor; 1807 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel, 1808 color); 1809 } 1810 1811 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } 1812 1813private: 1814 struct CircleVertex { 1815 SkPoint fPos; 1816 GrColor fColor; 1817 SkPoint fOffset; 1818 SkScalar fOuterRadius; 1819 SkScalar fInnerRadius; 1820 // No half plane, we don't use it here. 1821 }; 1822 1823 static void FillInOverstrokeVerts(CircleVertex** verts, const SkRect& bounds, SkScalar smInset, 1824 SkScalar bigInset, SkScalar xOffset, SkScalar outerRadius, 1825 SkScalar innerRadius, GrColor color) { 1826 SkASSERT(smInset < bigInset); 1827 1828 // TL 1829 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fTop + smInset); 1830 (*verts)->fColor = color; 1831 (*verts)->fOffset = SkPoint::Make(xOffset, 0); 1832 (*verts)->fOuterRadius = outerRadius; 1833 (*verts)->fInnerRadius = innerRadius; 1834 (*verts)++; 1835 1836 // TR 1837 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fTop + smInset); 1838 (*verts)->fColor = color; 1839 (*verts)->fOffset = SkPoint::Make(xOffset, 0); 1840 (*verts)->fOuterRadius = outerRadius; 1841 (*verts)->fInnerRadius = innerRadius; 1842 (*verts)++; 1843 1844 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fTop + bigInset); 1845 (*verts)->fColor = color; 1846 (*verts)->fOffset = SkPoint::Make(0, 0); 1847 (*verts)->fOuterRadius = outerRadius; 1848 (*verts)->fInnerRadius = innerRadius; 1849 (*verts)++; 1850 1851 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fTop + bigInset); 1852 (*verts)->fColor = color; 1853 (*verts)->fOffset = SkPoint::Make(0, 0); 1854 (*verts)->fOuterRadius = outerRadius; 1855 (*verts)->fInnerRadius = innerRadius; 1856 (*verts)++; 1857 1858 (*verts)->fPos = SkPoint::Make(bounds.fLeft + bigInset, bounds.fBottom - bigInset); 1859 (*verts)->fColor = color; 1860 (*verts)->fOffset = SkPoint::Make(0, 0); 1861 (*verts)->fOuterRadius = outerRadius; 1862 (*verts)->fInnerRadius = innerRadius; 1863 (*verts)++; 1864 1865 (*verts)->fPos = SkPoint::Make(bounds.fRight - bigInset, bounds.fBottom - bigInset); 1866 (*verts)->fColor = color; 1867 (*verts)->fOffset = SkPoint::Make(0, 0); 1868 (*verts)->fOuterRadius = outerRadius; 1869 (*verts)->fInnerRadius = innerRadius; 1870 (*verts)++; 1871 1872 // BL 1873 (*verts)->fPos = SkPoint::Make(bounds.fLeft + smInset, bounds.fBottom - smInset); 1874 (*verts)->fColor = color; 1875 (*verts)->fOffset = SkPoint::Make(xOffset, 0); 1876 (*verts)->fOuterRadius = outerRadius; 1877 (*verts)->fInnerRadius = innerRadius; 1878 (*verts)++; 1879 1880 // BR 1881 (*verts)->fPos = SkPoint::Make(bounds.fRight - smInset, bounds.fBottom - smInset); 1882 (*verts)->fColor = color; 1883 (*verts)->fOffset = SkPoint::Make(xOffset, 0); 1884 (*verts)->fOuterRadius = outerRadius; 1885 (*verts)->fInnerRadius = innerRadius; 1886 (*verts)++; 1887 } 1888 1889 void onPrepareDraws(Target* target) override { 1890 // Invert the view matrix as a local matrix (if any other processors require coords). 1891 SkMatrix localMatrix; 1892 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) { 1893 return; 1894 } 1895 1896 // Setup geometry processor 1897 sk_sp<GrGeometryProcessor> gp( 1898 new CircleGeometryProcessor(!fAllFill, false, false, false, localMatrix)); 1899 1900 size_t vertexStride = gp->getVertexStride(); 1901 SkASSERT(sizeof(CircleVertex) == vertexStride); 1902 1903 const GrBuffer* vertexBuffer; 1904 int firstVertex; 1905 1906 CircleVertex* verts = (CircleVertex*)target->makeVertexSpace(vertexStride, fVertCount, 1907 &vertexBuffer, &firstVertex); 1908 if (!verts) { 1909 SkDebugf("Could not allocate vertices\n"); 1910 return; 1911 } 1912 1913 const GrBuffer* indexBuffer = nullptr; 1914 int firstIndex = 0; 1915 uint16_t* indices = target->makeIndexSpace(fIndexCount, &indexBuffer, &firstIndex); 1916 if (!indices) { 1917 SkDebugf("Could not allocate indices\n"); 1918 return; 1919 } 1920 1921 int currStartVertex = 0; 1922 for (const auto& rrect : fRRects) { 1923 GrColor color = rrect.fColor; 1924 SkScalar outerRadius = rrect.fOuterRadius; 1925 const SkRect& bounds = rrect.fDevBounds; 1926 1927 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + outerRadius, 1928 bounds.fBottom - outerRadius, bounds.fBottom}; 1929 1930 SkScalar yOuterRadii[4] = {-1, 0, 0, 1}; 1931 // The inner radius in the vertex data must be specified in normalized space. 1932 // For fills, specifying -1/outerRadius guarantees an alpha of 1.0 at the inner radius. 1933 SkScalar innerRadius = rrect.fType != kFill_RRectType 1934 ? rrect.fInnerRadius / rrect.fOuterRadius 1935 : -1.0f / rrect.fOuterRadius; 1936 for (int i = 0; i < 4; ++i) { 1937 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]); 1938 verts->fColor = color; 1939 verts->fOffset = SkPoint::Make(-1, yOuterRadii[i]); 1940 verts->fOuterRadius = outerRadius; 1941 verts->fInnerRadius = innerRadius; 1942 verts++; 1943 1944 verts->fPos = SkPoint::Make(bounds.fLeft + outerRadius, yCoords[i]); 1945 verts->fColor = color; 1946 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]); 1947 verts->fOuterRadius = outerRadius; 1948 verts->fInnerRadius = innerRadius; 1949 verts++; 1950 1951 verts->fPos = SkPoint::Make(bounds.fRight - outerRadius, yCoords[i]); 1952 verts->fColor = color; 1953 verts->fOffset = SkPoint::Make(0, yOuterRadii[i]); 1954 verts->fOuterRadius = outerRadius; 1955 verts->fInnerRadius = innerRadius; 1956 verts++; 1957 1958 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]); 1959 verts->fColor = color; 1960 verts->fOffset = SkPoint::Make(1, yOuterRadii[i]); 1961 verts->fOuterRadius = outerRadius; 1962 verts->fInnerRadius = innerRadius; 1963 verts++; 1964 } 1965 // Add the additional vertices for overstroked rrects. 1966 // Effectively this is an additional stroked rrect, with its 1967 // outer radius = outerRadius - innerRadius, and inner radius = 0. 1968 // This will give us correct AA in the center and the correct 1969 // distance to the outer edge. 1970 // 1971 // Also, the outer offset is a constant vector pointing to the right, which 1972 // guarantees that the distance value along the outer rectangle is constant. 1973 if (kOverstroke_RRectType == rrect.fType) { 1974 SkASSERT(rrect.fInnerRadius <= 0.0f); 1975 1976 SkScalar overstrokeOuterRadius = outerRadius - rrect.fInnerRadius; 1977 // this is the normalized distance from the outer rectangle of this 1978 // geometry to the outer edge 1979 SkScalar maxOffset = -rrect.fInnerRadius / overstrokeOuterRadius; 1980 1981 FillInOverstrokeVerts(&verts, bounds, outerRadius, overstrokeOuterRadius, maxOffset, 1982 overstrokeOuterRadius, 0.0f, rrect.fColor); 1983 } 1984 1985 const uint16_t* primIndices = rrect_type_to_indices(rrect.fType); 1986 const int primIndexCount = rrect_type_to_index_count(rrect.fType); 1987 for (int i = 0; i < primIndexCount; ++i) { 1988 *indices++ = primIndices[i] + currStartVertex; 1989 } 1990 1991 currStartVertex += rrect_type_to_vert_count(rrect.fType); 1992 } 1993 1994 GrMesh mesh(GrPrimitiveType::kTriangles); 1995 mesh.setIndexed(indexBuffer, fIndexCount, firstIndex, 0, fVertCount - 1); 1996 mesh.setVertexData(vertexBuffer, firstVertex); 1997 target->draw(gp.get(), fHelper.makePipeline(target), mesh); 1998 } 1999 2000 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 2001 CircularRRectOp* that = t->cast<CircularRRectOp>(); 2002 2003 // can only represent 65535 unique vertices with 16-bit indices 2004 if (fVertCount + that->fVertCount > 65536) { 2005 return false; 2006 } 2007 2008 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { 2009 return false; 2010 } 2011 2012 if (fHelper.usesLocalCoords() && 2013 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) { 2014 return false; 2015 } 2016 2017 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin()); 2018 this->joinBounds(*that); 2019 fVertCount += that->fVertCount; 2020 fIndexCount += that->fIndexCount; 2021 fAllFill = fAllFill && that->fAllFill; 2022 return true; 2023 } 2024 2025 struct RRect { 2026 GrColor fColor; 2027 SkScalar fInnerRadius; 2028 SkScalar fOuterRadius; 2029 SkRect fDevBounds; 2030 RRectType fType; 2031 }; 2032 2033 SkMatrix fViewMatrixIfUsingLocalCoords; 2034 Helper fHelper; 2035 int fVertCount; 2036 int fIndexCount; 2037 bool fAllFill; 2038 SkSTArray<1, RRect, true> fRRects; 2039 2040 typedef GrMeshDrawOp INHERITED; 2041}; 2042 2043static const int kNumRRectsInIndexBuffer = 256; 2044 2045GR_DECLARE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey); 2046GR_DECLARE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey); 2047static const GrBuffer* ref_rrect_index_buffer(RRectType type, 2048 GrResourceProvider* resourceProvider) { 2049 GR_DEFINE_STATIC_UNIQUE_KEY(gStrokeRRectOnlyIndexBufferKey); 2050 GR_DEFINE_STATIC_UNIQUE_KEY(gRRectOnlyIndexBufferKey); 2051 switch (type) { 2052 case kFill_RRectType: 2053 return resourceProvider->findOrCreatePatternedIndexBuffer( 2054 gStandardRRectIndices, kIndicesPerFillRRect, kNumRRectsInIndexBuffer, 2055 kVertsPerStandardRRect, gRRectOnlyIndexBufferKey); 2056 case kStroke_RRectType: 2057 return resourceProvider->findOrCreatePatternedIndexBuffer( 2058 gStandardRRectIndices, kIndicesPerStrokeRRect, kNumRRectsInIndexBuffer, 2059 kVertsPerStandardRRect, gStrokeRRectOnlyIndexBufferKey); 2060 default: 2061 SkASSERT(false); 2062 return nullptr; 2063 }; 2064} 2065 2066class EllipticalRRectOp : public GrMeshDrawOp { 2067private: 2068 using Helper = GrSimpleMeshDrawOpHelper; 2069 2070public: 2071 DEFINE_OP_CLASS_ID 2072 2073 // If devStrokeWidths values are <= 0 indicates then fill only. Otherwise, strokeOnly indicates 2074 // whether the rrect is only stroked or stroked and filled. 2075 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, const SkMatrix& viewMatrix, 2076 const SkRect& devRect, float devXRadius, float devYRadius, 2077 SkVector devStrokeWidths, bool strokeOnly) { 2078 SkASSERT(devXRadius > 0.5); 2079 SkASSERT(devYRadius > 0.5); 2080 SkASSERT((devStrokeWidths.fX > 0) == (devStrokeWidths.fY > 0)); 2081 SkASSERT(!(strokeOnly && devStrokeWidths.fX <= 0)); 2082 if (devStrokeWidths.fX > 0) { 2083 if (SkScalarNearlyZero(devStrokeWidths.length())) { 2084 devStrokeWidths.set(SK_ScalarHalf, SK_ScalarHalf); 2085 } else { 2086 devStrokeWidths.scale(SK_ScalarHalf); 2087 } 2088 2089 // we only handle thick strokes for near-circular ellipses 2090 if (devStrokeWidths.length() > SK_ScalarHalf && 2091 (SK_ScalarHalf * devXRadius > devYRadius || 2092 SK_ScalarHalf * devYRadius > devXRadius)) { 2093 return nullptr; 2094 } 2095 2096 // we don't handle it if curvature of the stroke is less than curvature of the ellipse 2097 if (devStrokeWidths.fX * (devYRadius * devYRadius) < 2098 (devStrokeWidths.fY * devStrokeWidths.fY) * devXRadius) { 2099 return nullptr; 2100 } 2101 if (devStrokeWidths.fY * (devXRadius * devXRadius) < 2102 (devStrokeWidths.fX * devStrokeWidths.fX) * devYRadius) { 2103 return nullptr; 2104 } 2105 } 2106 return Helper::FactoryHelper<EllipticalRRectOp>(std::move(paint), viewMatrix, devRect, 2107 devXRadius, devYRadius, devStrokeWidths, 2108 strokeOnly); 2109 } 2110 2111 EllipticalRRectOp(Helper::MakeArgs helperArgs, GrColor color, const SkMatrix& viewMatrix, 2112 const SkRect& devRect, float devXRadius, float devYRadius, 2113 SkVector devStrokeHalfWidths, bool strokeOnly) 2114 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage) { 2115 SkScalar innerXRadius = 0.0f; 2116 SkScalar innerYRadius = 0.0f; 2117 SkRect bounds = devRect; 2118 bool stroked = false; 2119 if (devStrokeHalfWidths.fX > 0) { 2120 // this is legit only if scale & translation (which should be the case at the moment) 2121 if (strokeOnly) { 2122 innerXRadius = devXRadius - devStrokeHalfWidths.fX; 2123 innerYRadius = devYRadius - devStrokeHalfWidths.fY; 2124 stroked = (innerXRadius >= 0 && innerYRadius >= 0); 2125 } 2126 2127 devXRadius += devStrokeHalfWidths.fX; 2128 devYRadius += devStrokeHalfWidths.fY; 2129 bounds.outset(devStrokeHalfWidths.fX, devStrokeHalfWidths.fY); 2130 } 2131 2132 fStroked = stroked; 2133 fViewMatrixIfUsingLocalCoords = viewMatrix; 2134 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo); 2135 // Expand the rect for aa in order to generate the correct vertices. 2136 bounds.outset(SK_ScalarHalf, SK_ScalarHalf); 2137 fRRects.emplace_back( 2138 RRect{color, devXRadius, devYRadius, innerXRadius, innerYRadius, bounds}); 2139 } 2140 2141 const char* name() const override { return "EllipticalRRectOp"; } 2142 2143 SkString dumpInfo() const override { 2144 SkString string; 2145 string.appendf("Stroked: %d\n", fStroked); 2146 for (const auto& geo : fRRects) { 2147 string.appendf( 2148 "Color: 0x%08x Rect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], " 2149 "XRad: %.2f, YRad: %.2f, InnerXRad: %.2f, InnerYRad: %.2f\n", 2150 geo.fColor, geo.fDevBounds.fLeft, geo.fDevBounds.fTop, geo.fDevBounds.fRight, 2151 geo.fDevBounds.fBottom, geo.fXRadius, geo.fYRadius, geo.fInnerXRadius, 2152 geo.fInnerYRadius); 2153 } 2154 string += fHelper.dumpInfo(); 2155 string += INHERITED::dumpInfo(); 2156 return string; 2157 } 2158 2159 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip) override { 2160 GrColor* color = &fRRects.front().fColor; 2161 return fHelper.xpRequiresDstTexture(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel, 2162 color); 2163 } 2164 2165 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } 2166 2167private: 2168 void onPrepareDraws(Target* target) override { 2169 SkMatrix localMatrix; 2170 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) { 2171 return; 2172 } 2173 2174 // Setup geometry processor 2175 sk_sp<GrGeometryProcessor> gp(new EllipseGeometryProcessor(fStroked, localMatrix)); 2176 2177 size_t vertexStride = gp->getVertexStride(); 2178 SkASSERT(vertexStride == sizeof(EllipseVertex)); 2179 2180 // drop out the middle quad if we're stroked 2181 int indicesPerInstance = fStroked ? kIndicesPerStrokeRRect : kIndicesPerFillRRect; 2182 sk_sp<const GrBuffer> indexBuffer(ref_rrect_index_buffer( 2183 fStroked ? kStroke_RRectType : kFill_RRectType, target->resourceProvider())); 2184 2185 PatternHelper helper(GrPrimitiveType::kTriangles); 2186 EllipseVertex* verts = reinterpret_cast<EllipseVertex*>( 2187 helper.init(target, vertexStride, indexBuffer.get(), kVertsPerStandardRRect, 2188 indicesPerInstance, fRRects.count())); 2189 if (!verts || !indexBuffer) { 2190 SkDebugf("Could not allocate vertices\n"); 2191 return; 2192 } 2193 2194 for (const auto& rrect : fRRects) { 2195 GrColor color = rrect.fColor; 2196 // Compute the reciprocals of the radii here to save time in the shader 2197 SkScalar xRadRecip = SkScalarInvert(rrect.fXRadius); 2198 SkScalar yRadRecip = SkScalarInvert(rrect.fYRadius); 2199 SkScalar xInnerRadRecip = SkScalarInvert(rrect.fInnerXRadius); 2200 SkScalar yInnerRadRecip = SkScalarInvert(rrect.fInnerYRadius); 2201 2202 // Extend the radii out half a pixel to antialias. 2203 SkScalar xOuterRadius = rrect.fXRadius + SK_ScalarHalf; 2204 SkScalar yOuterRadius = rrect.fYRadius + SK_ScalarHalf; 2205 2206 const SkRect& bounds = rrect.fDevBounds; 2207 2208 SkScalar yCoords[4] = {bounds.fTop, bounds.fTop + yOuterRadius, 2209 bounds.fBottom - yOuterRadius, bounds.fBottom}; 2210 SkScalar yOuterOffsets[4] = {yOuterRadius, 2211 SK_ScalarNearlyZero, // we're using inversesqrt() in 2212 // shader, so can't be exactly 0 2213 SK_ScalarNearlyZero, yOuterRadius}; 2214 2215 for (int i = 0; i < 4; ++i) { 2216 verts->fPos = SkPoint::Make(bounds.fLeft, yCoords[i]); 2217 verts->fColor = color; 2218 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]); 2219 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 2220 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 2221 verts++; 2222 2223 verts->fPos = SkPoint::Make(bounds.fLeft + xOuterRadius, yCoords[i]); 2224 verts->fColor = color; 2225 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]); 2226 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 2227 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 2228 verts++; 2229 2230 verts->fPos = SkPoint::Make(bounds.fRight - xOuterRadius, yCoords[i]); 2231 verts->fColor = color; 2232 verts->fOffset = SkPoint::Make(SK_ScalarNearlyZero, yOuterOffsets[i]); 2233 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 2234 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 2235 verts++; 2236 2237 verts->fPos = SkPoint::Make(bounds.fRight, yCoords[i]); 2238 verts->fColor = color; 2239 verts->fOffset = SkPoint::Make(xOuterRadius, yOuterOffsets[i]); 2240 verts->fOuterRadii = SkPoint::Make(xRadRecip, yRadRecip); 2241 verts->fInnerRadii = SkPoint::Make(xInnerRadRecip, yInnerRadRecip); 2242 verts++; 2243 } 2244 } 2245 helper.recordDraw(target, gp.get(), fHelper.makePipeline(target)); 2246 } 2247 2248 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 2249 EllipticalRRectOp* that = t->cast<EllipticalRRectOp>(); 2250 2251 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { 2252 return false; 2253 } 2254 2255 if (fStroked != that->fStroked) { 2256 return false; 2257 } 2258 2259 if (fHelper.usesLocalCoords() && 2260 !fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) { 2261 return false; 2262 } 2263 2264 fRRects.push_back_n(that->fRRects.count(), that->fRRects.begin()); 2265 this->joinBounds(*that); 2266 return true; 2267 } 2268 2269 struct RRect { 2270 GrColor fColor; 2271 SkScalar fXRadius; 2272 SkScalar fYRadius; 2273 SkScalar fInnerXRadius; 2274 SkScalar fInnerYRadius; 2275 SkRect fDevBounds; 2276 }; 2277 2278 SkMatrix fViewMatrixIfUsingLocalCoords; 2279 Helper fHelper; 2280 bool fStroked; 2281 SkSTArray<1, RRect, true> fRRects; 2282 2283 typedef GrMeshDrawOp INHERITED; 2284}; 2285 2286static std::unique_ptr<GrDrawOp> make_rrect_op(GrPaint&& paint, 2287 const SkMatrix& viewMatrix, 2288 const SkRRect& rrect, 2289 const SkStrokeRec& stroke) { 2290 SkASSERT(viewMatrix.rectStaysRect()); 2291 SkASSERT(rrect.isSimple()); 2292 SkASSERT(!rrect.isOval()); 2293 2294 // RRect ops only handle simple, but not too simple, rrects. 2295 // Do any matrix crunching before we reset the draw state for device coords. 2296 const SkRect& rrectBounds = rrect.getBounds(); 2297 SkRect bounds; 2298 viewMatrix.mapRect(&bounds, rrectBounds); 2299 2300 SkVector radii = rrect.getSimpleRadii(); 2301 SkScalar xRadius = SkScalarAbs(viewMatrix[SkMatrix::kMScaleX] * radii.fX + 2302 viewMatrix[SkMatrix::kMSkewY] * radii.fY); 2303 SkScalar yRadius = SkScalarAbs(viewMatrix[SkMatrix::kMSkewX] * radii.fX + 2304 viewMatrix[SkMatrix::kMScaleY] * radii.fY); 2305 2306 SkStrokeRec::Style style = stroke.getStyle(); 2307 2308 // Do (potentially) anisotropic mapping of stroke. Use -1s to indicate fill-only draws. 2309 SkVector scaledStroke = {-1, -1}; 2310 SkScalar strokeWidth = stroke.getWidth(); 2311 2312 bool isStrokeOnly = 2313 SkStrokeRec::kStroke_Style == style || SkStrokeRec::kHairline_Style == style; 2314 bool hasStroke = isStrokeOnly || SkStrokeRec::kStrokeAndFill_Style == style; 2315 2316 bool isCircular = (xRadius == yRadius); 2317 if (hasStroke) { 2318 if (SkStrokeRec::kHairline_Style == style) { 2319 scaledStroke.set(1, 1); 2320 } else { 2321 scaledStroke.fX = SkScalarAbs( 2322 strokeWidth * (viewMatrix[SkMatrix::kMScaleX] + viewMatrix[SkMatrix::kMSkewY])); 2323 scaledStroke.fY = SkScalarAbs( 2324 strokeWidth * (viewMatrix[SkMatrix::kMSkewX] + viewMatrix[SkMatrix::kMScaleY])); 2325 } 2326 2327 isCircular = isCircular && scaledStroke.fX == scaledStroke.fY; 2328 // for non-circular rrects, if half of strokewidth is greater than radius, 2329 // we don't handle that right now 2330 if (!isCircular && (SK_ScalarHalf * scaledStroke.fX > xRadius || 2331 SK_ScalarHalf * scaledStroke.fY > yRadius)) { 2332 return nullptr; 2333 } 2334 } 2335 2336 // The way the effect interpolates the offset-to-ellipse/circle-center attribute only works on 2337 // the interior of the rrect if the radii are >= 0.5. Otherwise, the inner rect of the nine- 2338 // patch will have fractional coverage. This only matters when the interior is actually filled. 2339 // We could consider falling back to rect rendering here, since a tiny radius is 2340 // indistinguishable from a square corner. 2341 if (!isStrokeOnly && (SK_ScalarHalf > xRadius || SK_ScalarHalf > yRadius)) { 2342 return nullptr; 2343 } 2344 2345 // if the corners are circles, use the circle renderer 2346 if (isCircular) { 2347 return CircularRRectOp::Make(std::move(paint), viewMatrix, bounds, xRadius, scaledStroke.fX, 2348 isStrokeOnly); 2349 // otherwise we use the ellipse renderer 2350 } else { 2351 return EllipticalRRectOp::Make(std::move(paint), viewMatrix, bounds, xRadius, yRadius, 2352 scaledStroke, isStrokeOnly); 2353 } 2354} 2355 2356std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeRRectOp(GrPaint&& paint, 2357 const SkMatrix& viewMatrix, 2358 const SkRRect& rrect, 2359 const SkStrokeRec& stroke, 2360 const GrShaderCaps* shaderCaps) { 2361 if (rrect.isOval()) { 2362 return MakeOvalOp(std::move(paint), viewMatrix, rrect.getBounds(), stroke, shaderCaps); 2363 } 2364 2365 if (!viewMatrix.rectStaysRect() || !rrect.isSimple()) { 2366 return nullptr; 2367 } 2368 2369 return make_rrect_op(std::move(paint), viewMatrix, rrect, stroke); 2370} 2371 2372/////////////////////////////////////////////////////////////////////////////// 2373 2374std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeOvalOp(GrPaint&& paint, 2375 const SkMatrix& viewMatrix, 2376 const SkRect& oval, 2377 const SkStrokeRec& stroke, 2378 const GrShaderCaps* shaderCaps) { 2379 // we can draw circles 2380 SkScalar width = oval.width(); 2381 if (width > SK_ScalarNearlyZero && SkScalarNearlyEqual(width, oval.height()) && 2382 circle_stays_circle(viewMatrix)) { 2383 SkPoint center = {oval.centerX(), oval.centerY()}; 2384 return CircleOp::Make(std::move(paint), viewMatrix, center, width / 2.f, 2385 GrStyle(stroke, nullptr)); 2386 } 2387 2388 // prefer the device space ellipse op for batchability 2389 if (viewMatrix.rectStaysRect()) { 2390 return EllipseOp::Make(std::move(paint), viewMatrix, oval, stroke); 2391 } 2392 2393 // Otherwise, if we have shader derivative support, render as device-independent 2394 if (shaderCaps->shaderDerivativeSupport()) { 2395 return DIEllipseOp::Make(std::move(paint), viewMatrix, oval, stroke); 2396 } 2397 2398 return nullptr; 2399} 2400 2401/////////////////////////////////////////////////////////////////////////////// 2402 2403std::unique_ptr<GrDrawOp> GrOvalOpFactory::MakeArcOp(GrPaint&& paint, const SkMatrix& viewMatrix, 2404 const SkRect& oval, SkScalar startAngle, 2405 SkScalar sweepAngle, bool useCenter, 2406 const GrStyle& style, 2407 const GrShaderCaps* shaderCaps) { 2408 SkASSERT(!oval.isEmpty()); 2409 SkASSERT(sweepAngle); 2410 SkScalar width = oval.width(); 2411 if (SkScalarAbs(sweepAngle) >= 360.f) { 2412 return nullptr; 2413 } 2414 if (!SkScalarNearlyEqual(width, oval.height()) || !circle_stays_circle(viewMatrix)) { 2415 return nullptr; 2416 } 2417 SkPoint center = {oval.centerX(), oval.centerY()}; 2418 CircleOp::ArcParams arcParams = {SkDegreesToRadians(startAngle), SkDegreesToRadians(sweepAngle), 2419 useCenter}; 2420 return CircleOp::Make(std::move(paint), viewMatrix, center, width / 2.f, style, &arcParams); 2421} 2422 2423/////////////////////////////////////////////////////////////////////////////// 2424 2425#if GR_TEST_UTILS 2426 2427GR_DRAW_OP_TEST_DEFINE(CircleOp) { 2428 do { 2429 SkScalar rotate = random->nextSScalar1() * 360.f; 2430 SkScalar translateX = random->nextSScalar1() * 1000.f; 2431 SkScalar translateY = random->nextSScalar1() * 1000.f; 2432 SkScalar scale = random->nextSScalar1() * 100.f; 2433 SkMatrix viewMatrix; 2434 viewMatrix.setRotate(rotate); 2435 viewMatrix.postTranslate(translateX, translateY); 2436 viewMatrix.postScale(scale, scale); 2437 SkRect circle = GrTest::TestSquare(random); 2438 SkPoint center = {circle.centerX(), circle.centerY()}; 2439 SkScalar radius = circle.width() / 2.f; 2440 SkStrokeRec stroke = GrTest::TestStrokeRec(random); 2441 CircleOp::ArcParams arcParamsTmp; 2442 const CircleOp::ArcParams* arcParams = nullptr; 2443 if (random->nextBool()) { 2444 arcParamsTmp.fStartAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2; 2445 arcParamsTmp.fSweepAngleRadians = random->nextSScalar1() * SK_ScalarPI * 2 - .01f; 2446 arcParamsTmp.fUseCenter = random->nextBool(); 2447 arcParams = &arcParamsTmp; 2448 } 2449 std::unique_ptr<GrDrawOp> op = CircleOp::Make(std::move(paint), viewMatrix, center, radius, 2450 GrStyle(stroke, nullptr), arcParams); 2451 if (op) { 2452 return op; 2453 } 2454 } while (true); 2455} 2456 2457GR_DRAW_OP_TEST_DEFINE(EllipseOp) { 2458 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random); 2459 SkRect ellipse = GrTest::TestSquare(random); 2460 return EllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random)); 2461} 2462 2463GR_DRAW_OP_TEST_DEFINE(DIEllipseOp) { 2464 SkMatrix viewMatrix = GrTest::TestMatrix(random); 2465 SkRect ellipse = GrTest::TestSquare(random); 2466 return DIEllipseOp::Make(std::move(paint), viewMatrix, ellipse, GrTest::TestStrokeRec(random)); 2467} 2468 2469GR_DRAW_OP_TEST_DEFINE(RRectOp) { 2470 SkMatrix viewMatrix = GrTest::TestMatrixRectStaysRect(random); 2471 const SkRRect& rrect = GrTest::TestRRectSimple(random); 2472 return make_rrect_op(std::move(paint), viewMatrix, rrect, GrTest::TestStrokeRec(random)); 2473} 2474 2475#endif 2476