Well, after a few hours, I managed to come up with something that works decently. Took quite some time to come up with the right algorithm to preserve the alpha values properly.
uniform sampler2D tex;
uniform vec2 texSize;
uniform vec4 outlineColor;
void main(void)
{
vec2 off = 1.0 / texSize;
vec2 tc = gl_TexCoord[0].st;
vec4 c = texture2D(tex, tc);
vec4 n = texture2D(tex, vec2(tc.x, tc.y - off.y));
vec4 e = texture2D(tex, vec2(tc.x + off.x, tc.y));
vec4 s = texture2D(tex, vec2(tc.x, tc.y + off.y));
vec4 w = texture2D(tex, vec2(tc.x - off.x, tc.y));
vec4 origColor = c * gl_Color;
float ua = 0.0;
ua = mix(ua, 1.0, c.a);
ua = mix(ua, 1.0, n.a);
ua = mix(ua, 1.0, e.a);
ua = mix(ua, 1.0, s.a);
ua = mix(ua, 1.0, w.a);
vec4 underColor = outlineColor * vec4(ua);
gl_FragColor = underColor;
gl_FragColor = mix(gl_FragColor, origColor, origColor.a);
if (gl_FragColor.a > 0.0)
gl_FragColor.a = 1.0;
}
There is a little issue with the display of the first character. It seems SFML adds a little white block to the top-left corner of a font bitmap, so that ends up making a little artifact display for the first character. In theory, it'll also show artifacts if there are any font characters without padding between them in the font texture. Could get rid of this by just clamping the texture coordinates, but don't know of any way of finding the specified coordinates for the draw call without passing them as a parameter (which would require modifying SFML to update that parameter before each character is drawn).
Edit: Updated the shader. Kinda disappointed in myself for not realizing to take this approach initially. Was too stuck on the idea of recognizing when the edge has been reached.