ilmenite 0.14.0

A rust library for shaping, placing, and rasterizing text primarily for Basalt.
Documentation
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) 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 gain( float x, float k ) {
  x = clamp(x, 0.0, 1.0);
  float s = sign(x-0.5);
  float o = (1.0+s)/2.0;
  return o - 0.5*s*pow(2.0*(o-s*x),k);
}

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;
		}
	}
	
	float value = fill_amt_sum / float(com.sample_count);

	if(value < 0.02) {
		return 0.0;
	} else {
		return gain(value + 0.1, 2.5);
	}
}

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;
	float left = get_value(vec2(-1.0 / 6.0, 0.0), ray_len);
	float r = get_value(vec2(1.0 / 6.0, 0.0), ray_len);
	float g = get_value(vec2(3.0 / 6.0, 0.0), ray_len);
	float b = get_value(vec2(5.0 / 6.0, 0.0), ray_len);
	float right = get_value(vec2(7.0 / 6.0, 0.0), ray_len);

	vec3 color = vec3(
		(left * (1.0 / 3.0)) + (r * (1.0 / 3.0)) + (g * (1.0 / 3.0)),
		(r * (1.0 / 3.0)) + (g * (1.0 / 3.0)) + (b * (1.0 / 3.0)),
		(g * (1.0 / 3.0)) + (b * (1.0 / 3.0)) + (right * (1.0 / 3.0))
	);

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