#if defined(GLES2_RENDERER) #define float_t mediump float #define color_t mediump vec4 #define FRAG_COLOR gl_FragColor varying color_t color; #else #define float_t float #define color_t vec4 out vec4 FragColor; #define FRAG_COLOR FragColor flat in color_t color; #endif uniform float_t cellWidth; uniform float_t cellHeight; uniform float_t paddingY; uniform float_t paddingX; uniform float_t underlinePosition; uniform float_t underlineThickness; uniform float_t undercurlPosition; #define PI 3.1415926538 #if defined(DRAW_UNDERCURL) color_t draw_undercurl(float_t x, float_t y) { // We use `undercurlPosition` as an amplitude, since it's half of the descent // value. // // The `x` represents the left bound of pixel we should add `1/2` to it, so // we compute the undercurl position for the center of the pixel. float_t undercurl = undercurlPosition / 2. * cos((x + 0.5) * 2. * PI / cellWidth) + undercurlPosition - 1.; float_t undercurlTop = undercurl + max((underlineThickness - 1.), 0.) / 2.; float_t undercurlBottom = undercurl - max((underlineThickness - 1.), 0.) / 2.; // The distance to the curve boundary is always positive when it should // be used for AA. When both `y - undercurlTop` and `undercurlBottom - y` // expressions are negative, it means that the point is inside the curve // and we should just use alpha 1. To do so, we max one value with 0 // so it'll use the alpha 1 in the end. float_t dst = max(y - undercurlTop, max(undercurlBottom - y, 0.)); // Doing proper SDF is complicated for this shader, so just make AA // stronger by 1/x^2, which renders preserving underline thickness and // being bold enough. float_t alpha = 1. - dst * dst; // The result is an alpha mask on a rect, which leaves only curve opaque. return vec4(color.rgb, alpha); } #endif #if defined(DRAW_DOTTED) // When the dot size increases we can use AA to make spacing look even and the // dots rounded. color_t draw_dotted_aliased(float_t x, float_t y) { float_t dotNumber = floor(x / underlineThickness); float_t radius = underlineThickness / 2.; float_t centerY = underlinePosition - 1.; float_t leftCenter = (dotNumber - mod(dotNumber, 2.)) * underlineThickness + radius; float_t rightCenter = leftCenter + 2. * underlineThickness; float_t distanceLeft = sqrt(pow(x - leftCenter, 2.) + pow(y - centerY, 2.)); float_t distanceRight = sqrt(pow(x - rightCenter, 2.) + pow(y - centerY, 2.)); float_t alpha = max(1. - (min(distanceLeft, distanceRight) - radius), 0.); return vec4(color.rgb, alpha); } /// Draw dotted line when dot is just a single pixel. color_t draw_dotted(float_t x, float_t y) { float_t cellEven = 0.; // Since the size of the dot and its gap combined is 2px we should ensure that // spacing will be even. If the cellWidth is even it'll work since we start // with dot and end with gap. However if cellWidth is odd, the cell will start // and end with a dot, creating a dash. To resolve this issue, we invert the // pattern every two cells. if (int(mod(cellWidth, 2.)) != 0) { cellEven = mod((gl_FragCoord.x - paddingX) / cellWidth, 2.); } // Since we use the entire descent area for dotted underlines, we limit its // height to a single pixel so we don't draw bars instead of dots. float_t alpha = 1. - abs(floor(underlinePosition) - y); if (int(mod(x, 2.)) != int(cellEven)) { alpha = 0.; } return vec4(color.rgb, alpha); } #endif #if defined(DRAW_DASHED) color_t draw_dashed(float_t x) { // Since dashes of adjacent cells connect with each other our dash length is // half of the desired total length. float_t halfDashLen = floor(cellWidth / 4. + 0.5); float_t alpha = 1.; // Check if `x` coordinate is where we should draw gap. if (x > halfDashLen - 1. && x < cellWidth - halfDashLen) { alpha = 0.; } return vec4(color.rgb, alpha); } #endif void main() { float_t x = floor(mod(gl_FragCoord.x - paddingX, cellWidth)); float_t y = floor(mod(gl_FragCoord.y - paddingY, cellHeight)); #if defined(DRAW_UNDERCURL) FRAG_COLOR = draw_undercurl(x, y); #elif defined(DRAW_DOTTED) if (underlineThickness < 2.) { FRAG_COLOR = draw_dotted(x, y); } else { FRAG_COLOR = draw_dotted_aliased(x, y); } #elif defined(DRAW_DASHED) FRAG_COLOR = draw_dashed(x); #else FRAG_COLOR = color; #endif }