First off, looks awesome!
I would try doing it per character that overlaps the light.
First, draw the character sprite into a new sprite with a purple background, so you can pick the transparent parts.
Determine the visible part of the character box (V).
Then find the angle range that V spans - this could be 360 degrees in the worst case.
When casting the ray, determine the start and end of the line that overlaps V.
Do your raycast as before, but you don't need to get and release the drawing surface in the inner loop since you're doing it per character.
Optimize that inner loop a bit: don't need to check if (col < 0), don't call GetRFromColor (inline the code), might be faster to pull out lights[light].X/Y, Maths.Sin/Cos(a), c.x - charSprites[c.ID].Width/2 etc into loop invariant constants.
float sa = Maths.Sin(a);
float ca = Maths.Cos(a);
int lx = lights[light].X;
int ly = lights[light].Y;
int cx = c.x - charSprites[c.ID].Width/2;
int cy = c.y - charSprites[c.ID].Height;
int lcx = lx - cx;
int lcy = ly - cy;
int x, y, col;
// these are calculated from the box intersection
d = startD;
str = startStr;
while (d < endD && str > 0)
{
x = lcx + FloatToInt(sa * d);
y = lcy + FloatToInt(ca * d);
if (charMaskSurf.GetPixel(x, y) != PURPLE)
{
col = cds.GetPixel(x, y) & 31; // or whatever GetRFromColor did
col += str / 4;
if (col > 255) col = 255;
cds.DrawingColor = Game.GetColorFromRGB(col, col, col);
cds.DrawPixel(x, y);
str -= 50; // reduce strength from character hit to simulate rimlight
}
d += 1.0; // advance distance
str --; // linear falloff
}
[EDIT] Rolled up lx and cx into loop invariant lcx. Also pulled variable declarations out of inner loop (might save a few cycles).