mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
320 lines
10 KiB
GLSL
320 lines
10 KiB
GLSL
#line 1
|
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#ifdef WR_FEATURE_TRANSFORM
|
|
|
|
#else
|
|
// draw a circle at position aDesiredPos with a aRadius
|
|
vec4 drawCircle(vec2 aPixel, vec2 aDesiredPos, float aRadius, vec3 aColor) {
|
|
float farFromCenter = length(aDesiredPos - aPixel) - aRadius;
|
|
float pixelInCircle = 1.00 - clamp(farFromCenter, 0.0, 1.0);
|
|
return vec4(aColor, pixelInCircle);
|
|
}
|
|
|
|
// Draw a rectangle at aRect fill it with aColor. Only works on non-rotated
|
|
// rects.
|
|
vec4 drawRect(vec2 aPixel, vec4 aRect, vec3 aColor) {
|
|
// GLSL origin is bottom left, positive Y is up
|
|
bool inRect = (aRect.x <= aPixel.x) && (aPixel.x <= aRect.x + aRect.z) &&
|
|
(aPixel.y >= aRect.y) && (aPixel.y <= aRect.y + aRect.w);
|
|
return vec4(aColor, float(inRect));
|
|
}
|
|
|
|
vec4 draw_dotted_edge() {
|
|
// Everything here should be in device pixels.
|
|
// We want the dot to be roughly the size of the whole border spacing
|
|
float border_spacing = min(vBorders.w, vBorders.z);
|
|
float radius = floor(border_spacing / 2.0);
|
|
float diameter = radius * 2.0;
|
|
// The amount of space between dots. 2.2 was chosen because it looks kind of
|
|
// like firefox.
|
|
float circleSpacing = diameter * 2.2;
|
|
|
|
vec2 size = vBorders.zw;
|
|
// Get our position within this specific segment
|
|
vec2 position = vDevicePos - vBorders.xy;
|
|
|
|
// Break our position into square tiles with circles in them.
|
|
vec2 circleCount = floor(size / circleSpacing);
|
|
circleCount = max(circleCount, 1.0);
|
|
|
|
vec2 distBetweenCircles = size / circleCount;
|
|
vec2 circleCenter = distBetweenCircles / 2.0;
|
|
|
|
// Find out which tile this pixel belongs to.
|
|
vec2 destTile = floor(position / distBetweenCircles);
|
|
destTile = destTile * distBetweenCircles;
|
|
|
|
// Where we want to draw the actual circle.
|
|
vec2 tileCenter = destTile + circleCenter;
|
|
|
|
// Find the position within the tile
|
|
vec2 positionInTile = mod(position, distBetweenCircles);
|
|
vec2 finalPosition = positionInTile + destTile;
|
|
|
|
vec4 white = vec4(1.0, 1.0, 1.0, 1.0);
|
|
// See if we should draw a circle or not
|
|
vec4 circleColor = drawCircle(finalPosition, tileCenter, radius, vVerticalColor.xyz);
|
|
return mix(white, circleColor, circleColor.a);
|
|
}
|
|
|
|
vec4 draw_double_edge(float pos, float len) {
|
|
// Devided border to 3 parts, draw color on first and third part,
|
|
// leave second part blank.
|
|
float one_third_len = len / 3.0;
|
|
|
|
float in_first_part = step(pos, one_third_len);
|
|
float in_third_part = step(len - one_third_len, pos);
|
|
|
|
// The result of this should be 1.0 if we're in the 1st or 3rd part.
|
|
// And 0.0 for the blank part.
|
|
float should_fill = in_first_part + in_third_part;
|
|
|
|
float color_weight = step(0.0, vF);
|
|
vec4 color = mix(vHorizontalColor, vVerticalColor, color_weight);
|
|
|
|
vec4 white = vec4(1.0, 1.0, 1.0, 1.0);
|
|
return mix(white, color, should_fill);
|
|
}
|
|
|
|
vec4 draw_double_edge_vertical() {
|
|
// Get our position within this specific segment
|
|
float position = vLocalPos.x - vLocalRect.x;
|
|
return draw_double_edge(position, vLocalRect.z);
|
|
}
|
|
|
|
vec4 draw_double_edge_horizontal() {
|
|
// Get our position within this specific segment
|
|
float position = vLocalPos.y - vLocalRect.y;
|
|
return draw_double_edge(position, vLocalRect.w);
|
|
}
|
|
|
|
vec4 draw_double_edge_with_radius() {
|
|
// Get our position within this specific segment
|
|
float position = distance(vRefPoint, vLocalPos) - vRadii.z;
|
|
float len = vRadii.x - vRadii.z;
|
|
return draw_double_edge(position, len);
|
|
}
|
|
|
|
vec4 draw_double_edge_corner() {
|
|
if (vRadii.x > 0) {
|
|
return draw_double_edge_with_radius();
|
|
}
|
|
|
|
bool is_vertical = (vBorderPart == PST_TOP_LEFT) ? vF < 0 : vF >= 0;
|
|
if (is_vertical) {
|
|
return draw_double_edge_vertical();
|
|
} else {
|
|
return draw_double_edge_horizontal();
|
|
}
|
|
}
|
|
|
|
// Our current edge calculation is based only on
|
|
// the size of the border-size, but we need to draw
|
|
// the dashes in the center of the segment we're drawing.
|
|
// This calculates how much to nudge and which axis to nudge on.
|
|
vec2 get_dashed_nudge_factor(vec2 dash_size, bool is_corner) {
|
|
if (is_corner) {
|
|
return vec2(0.0, 0.0);
|
|
}
|
|
|
|
bool xAxisFudge = vBorders.z > vBorders.w;
|
|
if (xAxisFudge) {
|
|
return vec2(dash_size.x / 2.0, 0);
|
|
}
|
|
|
|
return vec2(0.0, dash_size.y / 2.0);
|
|
}
|
|
|
|
vec4 draw_dashed_edge(bool is_corner) {
|
|
// Everything here should be in device pixels.
|
|
// We want the dot to be roughly the size of the whole border spacing
|
|
// 5.5 here isn't a magic number, it's just what mostly looks like FF/Chrome
|
|
// TODO: Investigate exactly what FF does.
|
|
float dash_interval = min(vBorders.w, vBorders.z) * 5.5;
|
|
vec2 edge_size = vec2(vBorders.z, vBorders.w);
|
|
vec2 dash_size = vec2(dash_interval / 2.0, dash_interval / 2.0);
|
|
vec2 position = vDevicePos - vBorders.xy;
|
|
|
|
vec2 dash_count = floor(edge_size/ dash_interval);
|
|
vec2 dist_between_dashes = edge_size / dash_count;
|
|
|
|
vec2 target_rect_index = floor(position / dist_between_dashes);
|
|
vec2 target_rect_loc = target_rect_index * dist_between_dashes;
|
|
target_rect_loc += get_dashed_nudge_factor(dash_size, is_corner);
|
|
vec4 target_rect = vec4(target_rect_loc, dash_size);
|
|
|
|
vec4 white = vec4(1.0, 1.0, 1.0, 1.0);
|
|
vec4 target_colored_rect = drawRect(position, target_rect, vVerticalColor.xyz);
|
|
return mix(white, target_colored_rect, target_colored_rect.a);
|
|
}
|
|
|
|
void draw_dotted_border(void) {
|
|
switch (vBorderPart) {
|
|
// These are the layer tile part PrimitivePart as uploaded by the tiling.rs
|
|
case PST_TOP_LEFT:
|
|
case PST_TOP_RIGHT:
|
|
case PST_BOTTOM_LEFT:
|
|
case PST_BOTTOM_RIGHT:
|
|
{
|
|
// TODO: Fix for corners with a border-radius
|
|
oFragColor = draw_dotted_edge();
|
|
break;
|
|
}
|
|
case PST_BOTTOM:
|
|
case PST_TOP:
|
|
case PST_LEFT:
|
|
case PST_RIGHT:
|
|
{
|
|
oFragColor = draw_dotted_edge();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void draw_dashed_border(void) {
|
|
switch (vBorderPart) {
|
|
// These are the layer tile part PrimitivePart as uploaded by the tiling.rs
|
|
case PST_TOP_LEFT:
|
|
case PST_TOP_RIGHT:
|
|
case PST_BOTTOM_LEFT:
|
|
case PST_BOTTOM_RIGHT:
|
|
{
|
|
// TODO: Fix for corners with a border-radius
|
|
bool is_corner = true;
|
|
oFragColor = draw_dashed_edge(is_corner);
|
|
break;
|
|
}
|
|
case PST_BOTTOM:
|
|
case PST_TOP:
|
|
case PST_LEFT:
|
|
case PST_RIGHT:
|
|
{
|
|
bool is_corner = false;
|
|
oFragColor = draw_dashed_edge(is_corner);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void draw_double_border(void) {
|
|
switch (vBorderPart) {
|
|
// These are the layer tile part PrimitivePart as uploaded by the tiling.rs
|
|
case PST_TOP_LEFT:
|
|
case PST_TOP_RIGHT:
|
|
case PST_BOTTOM_LEFT:
|
|
case PST_BOTTOM_RIGHT:
|
|
{
|
|
oFragColor = draw_double_edge_corner();
|
|
break;
|
|
}
|
|
case PST_BOTTOM:
|
|
case PST_TOP:
|
|
{
|
|
oFragColor = draw_double_edge_horizontal();
|
|
break;
|
|
}
|
|
case PST_LEFT:
|
|
case PST_RIGHT:
|
|
{
|
|
oFragColor = draw_double_edge_vertical();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void discard_pixels_in_rounded_borders(vec2 local_pos) {
|
|
float distanceFromRef = distance(vRefPoint, local_pos);
|
|
if (vRadii.x > 0.0 && (distanceFromRef > vRadii.x || distanceFromRef < vRadii.z)) {
|
|
discard;
|
|
}
|
|
}
|
|
|
|
void draw_antialiased_solid_border_corner(vec2 local_pos) {
|
|
if (vRadii.x <= 0.0) {
|
|
return;
|
|
}
|
|
|
|
// This is the conversion factor for transformations and device pixel scaling.
|
|
float pixelsPerFragment = length(fwidth(local_pos.xy));
|
|
|
|
float distanceFromRef = distance(vRefPoint, local_pos);
|
|
|
|
// We want to start anti-aliasing one pixel in from the border.
|
|
float nudge = 1 * pixelsPerFragment;
|
|
float innerRadius = vRadii.z + nudge;
|
|
float outerRadius = vRadii.x - nudge;
|
|
|
|
if (vRadii.x > 0.0 && (distanceFromRef > outerRadius || distanceFromRef < innerRadius)) {
|
|
float distanceFromBorder = max(distanceFromRef - outerRadius,
|
|
innerRadius - distanceFromRef);
|
|
// Move the distance back into pixels.
|
|
distanceFromBorder /= pixelsPerFragment;
|
|
|
|
// Apply a more gradual fade out to transparent.
|
|
distanceFromBorder -= 0.5;
|
|
|
|
oFragColor = oFragColor * vec4(1, 1, 1, smoothstep(1.0, 0, distanceFromBorder));
|
|
}
|
|
}
|
|
|
|
// TODO: Investigate performance of this shader and see
|
|
// if it's worthwhile splitting it / removing branches etc.
|
|
void main(void) {
|
|
#ifdef WR_FEATURE_TRANSFORM
|
|
float alpha = 0;
|
|
vec2 local_pos = init_transform_fs(vLocalPos, vLocalRect, alpha);
|
|
#else
|
|
vec2 local_pos = vLocalPos;
|
|
#endif
|
|
|
|
#ifdef WR_FEATURE_TRANSFORM
|
|
// TODO(gw): Support other border styles for transformed elements.
|
|
discard_pixels_in_rounded_borders(local_pos);
|
|
float f = (local_pos.x - vSizeInfo.x) * vSizeInfo.w - (local_pos.y - vSizeInfo.y) * vSizeInfo.z;
|
|
oFragColor = vec4(1, 1, 1, alpha) * mix(vHorizontalColor, vVerticalColor, step(0.0, f));
|
|
#else
|
|
switch (vBorderStyle) {
|
|
case BORDER_STYLE_DASHED:
|
|
discard_pixels_in_rounded_borders(local_pos);
|
|
draw_dashed_border();
|
|
break;
|
|
case BORDER_STYLE_DOTTED:
|
|
discard_pixels_in_rounded_borders(local_pos);
|
|
draw_dotted_border();
|
|
break;
|
|
case BORDER_STYLE_OUTSET:
|
|
case BORDER_STYLE_INSET:
|
|
discard_pixels_in_rounded_borders(local_pos);
|
|
oFragColor = mix(vVerticalColor, vHorizontalColor, step(0.0, vF));
|
|
break;
|
|
case BORDER_STYLE_SOLID:
|
|
oFragColor = mix(vHorizontalColor, vVerticalColor, step(0.0, vF));
|
|
switch (vBorderPart) {
|
|
case PST_TOP_LEFT:
|
|
case PST_TOP_RIGHT:
|
|
case PST_BOTTOM_LEFT:
|
|
case PST_BOTTOM_RIGHT:
|
|
draw_antialiased_solid_border_corner(local_pos);
|
|
break;
|
|
default:
|
|
discard_pixels_in_rounded_borders(local_pos);
|
|
}
|
|
break;
|
|
case BORDER_STYLE_NONE:
|
|
discard_pixels_in_rounded_borders(local_pos);
|
|
oFragColor = mix(vHorizontalColor, vVerticalColor, step(0.0, vF));
|
|
break;
|
|
case BORDER_STYLE_DOUBLE:
|
|
discard_pixels_in_rounded_borders(local_pos);
|
|
draw_double_border();
|
|
break;
|
|
default:
|
|
discard;
|
|
}
|
|
#endif
|
|
}
|