Hi! Firstly, VERY GOOD WORK!
I've been using your code for 5 days only. So able to inform you for one bug only.
When you are just moving a mouse over the picture, it's taking 100% of the processor time. It's not good I think. For example because of this, the selection ants stop their moving and the top ruler indent are showed not correctly (it's jumping). I think that you should change the Value Set Property of the Ruler Class. Instead of forcing full redrawing of the rulers, place something like this:
Graphics aGraphics = this.CreateGraphics();
IntPtr hDC = aGraphics.GetHdc();
PointF pt = scaleFactor.ScalePointJustX(new PointF(ClientRectangle.Left + aOldVal - Offset, ClientRectangle.Top));
SizeF size = new SizeF(Math.Max(1, scaleFactor.ScaleScalar(1.0f)), ClientRectangle.Height);
pt.X -= 0.5f;
Rectangle aRect = Rectangle.Truncate(new RectangleF(pt, size));
API32CSharp.RECT aRECT;
aRECT.left = aRect.X;
aRECT.top = aRect.Y;
aRECT.right = aRect.Right;
aRECT.bottom = aRect.Bottom;
API32CSharp.Gdi.InvertRect(hDC, ref aRECT);
pt = scaleFactor.ScalePointJustX(new PointF(ClientRectangle.Left + this.value - Offset, ClientRectangle.Top));
size = new SizeF(Math.Max(1, scaleFactor.ScaleScalar(1.0f)), ClientRectangle.Height);
pt.X -= 0.5f;
aRect = Rectangle.Truncate(new RectangleF(pt, size));
aRECT.left = aRect.X;
aRECT.top = aRect.Y;
aRECT.right = aRect.Right;
aRECT.bottom = aRect.Bottom;
API32CSharp.Gdi.InvertRect(hDC, ref aRECT);
aGraphics.ReleaseHdc(hDC);
aGraphics.Dispose();
Again thanks and with best regards
Sorry for my English