1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
pub mod glyph_cs {
	vulkano_shaders::shader!{
		ty: "compute",
		vulkan_version: "1.1",
		spirv_version: "1.5",
		src: "
#version 450

layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;

layout(set = 0, binding = 0) readonly uniform Common {
	vec4 samples_and_rays[25];
	uint sample_count;
	uint ray_count;
} com;

layout(set = 0, binding = 1) readonly uniform Glyph {
	float scaler;
	uint width;
	uint height;
	uint line_count;
	vec4 bounds;
	vec2 offset;
} glyph;

layout(set = 0, binding = 2, rgba8) writeonly uniform image2D bitmap;

layout(set = 0, binding = 3) readonly buffer Line {
	vec4 line[];
} lines;

bool ray_intersects(vec2 l1p1, vec2 l1p2, vec2 l2p1, vec2 l2p2, out vec2 point) {
	vec2 r = l1p2 - l1p1;
	vec2 s = l2p2 - l2p1;
	float det = r.x * s.y - r.y * s.x;
	float u = ((l2p1.x - l1p1.x) * r.y - (l2p1.y - l1p1.y) * r.x) / det;
	float t = ((l2p1.x - l1p1.x) * s.y - (l2p1.y - l1p1.y) * s.x) / det;
	
	if ((t >= 0. && t <= 1.) && (u >= 0. && u <= 1.)) {
		point = l1p1 + r * t;
		return true;
	} else {
		return false;
	}
}

bool sample_filled(vec2 ray_src, float ray_len, out float fill_amt) {
	vec2 intersect_point = vec2(0.0);
	int rays_filled = 0;
	float ray_fill_amt = 0.0;
	float cell_height = (glyph.scaler / sqrt(com.sample_count));
	float cell_width = cell_height / 3.0;
	
	for(uint ray_dir_i = 0; ray_dir_i < com.ray_count; ray_dir_i++) {
		int hits = 0;
		vec2 ray_dest = ray_src + (com.samples_and_rays[ray_dir_i].zw * ray_len);
		float ray_angle = atan(com.samples_and_rays[ray_dir_i].w / com.samples_and_rays[ray_dir_i].z);
		float ray_max_dist = (cell_width / 2.0) / cos(ray_angle);

		if(ray_max_dist > (cell_height / 2.0)) {
			ray_max_dist = (cell_height / 2.0) / cos(1.570796327 - ray_angle);
		}
		
		float ray_min_dist = ray_max_dist;
		
		for(uint line_i = 0; line_i < glyph.line_count; line_i ++) {
			if(ray_intersects(ray_src, ray_dest, lines.line[line_i].xy, lines.line[line_i].zw, intersect_point)) {
				float dist = distance(ray_src, intersect_point);
				
				if(dist < ray_min_dist) {
					ray_min_dist = dist;
				}
				
				hits++;
			}
		}

		if(hits % 2 != 0) {
			rays_filled++;
			ray_fill_amt += ray_min_dist / ray_max_dist;
		}
	}

	if(rays_filled >= com.ray_count / 2) {
		fill_amt = ray_fill_amt / float(rays_filled);
		return true;
	} else {
		return false;
	}
}

vec2 transform_coords(uint offset_i, vec2 offset) {
	vec2 coords = vec2(float(gl_GlobalInvocationID.x), float(gl_GlobalInvocationID.y) * -1.0);
	coords -= glyph.offset;
	// Apply the pixel offset for sampling
	coords += com.samples_and_rays[offset_i].xy;
	coords += offset;
	// Convert to font units
	coords /= glyph.scaler;
	// Bearing adjustment
	coords += vec2(glyph.bounds.x, glyph.bounds.w);
	return coords;
}

float get_value(vec2 offset, float ray_len) {
	float fill_amt = 0.0;
	float fill_amt_sum = 0.0;
	
	for(uint i = 0; i < com.sample_count; i++) {
		if(sample_filled(transform_coords(i, offset), ray_len, fill_amt)) {
			fill_amt_sum += fill_amt;
		}
	}
	
	return fill_amt_sum / float(com.sample_count);
}

void main() {
	float ray_len = sqrt(
		pow(float(glyph.width) / glyph.scaler, 2)
			+ pow(float(glyph.height) / glyph.scaler, 2)
	);
	
	uint rindex = ((gl_GlobalInvocationID.y * glyph.width) + gl_GlobalInvocationID.x) * 4;
	vec3 color = vec3(
		get_value(vec2(1.0 / 6.0, 0.0), ray_len),
		get_value(vec2(3.0 / 6.0, 0.0), ray_len),
		get_value(vec2(5.0 / 6.0, 0.0), ray_len)
	);

	float alpha = (color.r + color.g + color.b) / 3.0;
	color /= alpha;
	imageStore(bitmap, ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y), vec4(color, alpha));
}
	"}
}