Код, опубликованный Рэй Бернс, не сработал для меня, но привел меня на правильный путь.После некоторых исследований и экспериментов я обнаружил, что проблемы заключаются в реализации bitmap.Render (...) и используемом им Viewbox.
Примечание: я использую .Net 3.5 и WPF, так что, возможно, его код работает вдругие версии .Net.
Здесь намеренно оставлены комментарии, чтобы помочь объяснить код.
Как вы можете видеть, Viewbox должен быть нормализован относительно исходной высоты и ширины Visual.
DrawingVisual должен быть нарисован с использованием DrawingContext, прежде чем его можно будет визуализировать.
В методе RenderTargetBitmap я попытался использовать PixelFormats.Default и PixelFormats.Pbgra32.Мои результаты тестирования были одинаковыми с обоими из них.
Вот код.
public static Color GetPixelColor(Visual visual, Point pt)
{
Point ptDpi = getScreenDPI(visual);
Size srcSize = VisualTreeHelper.GetDescendantBounds(visual).Size;
//Viewbox uses values between 0 & 1 so normalize the Rect with respect to the visual's Height & Width
Rect percentSrcRec = new Rect(pt.X / srcSize.Width, pt.Y / srcSize.Height,
1 / srcSize.Width, 1 / srcSize.Height);
//var bmpOut = new RenderTargetBitmap(1, 1, 96d, 96d, PixelFormats.Pbgra32); //assumes 96 dpi
var bmpOut = new RenderTargetBitmap((int)(ptDpi.X / 96d),
(int)(ptDpi.Y / 96d),
ptDpi.X, ptDpi.Y, PixelFormats.Default); //generalized for monitors with different dpi
DrawingVisual dv = new DrawingVisual();
using (DrawingContext dc = dv.RenderOpen())
{
dc.DrawRectangle(new VisualBrush { Visual = visual, Viewbox = percentSrcRec },
null, //no Pen
new Rect(0, 0, 1d, 1d) );
}
bmpOut.Render(dv);
var bytes = new byte[4];
int iStride = 4; // = 4 * bmpOut.Width (for 32 bit graphics with 4 bytes per pixel -- 4 * 8 bits per byte = 32)
bmpOut.CopyPixels(bytes, iStride, 0);
return Color.FromArgb(bytes[0], bytes[1], bytes[2], bytes[3]);
}
Если вас интересует функция getScreenDPI (), код:
public static Point getScreenDPI(Visual v)
{
//System.Windows.SystemParameters
PresentationSource source = PresentationSource.FromVisual( v );
Point ptDpi;
if (source != null)
{
ptDpi = new Point( 96.0 * source.CompositionTarget.TransformToDevice.M11,
96.0 * source.CompositionTarget.TransformToDevice.M22 );
}
else
ptDpi = new Point(96d, 96d); //default value.
return ptDpi;
}
И использование похоже на Рэя.Я показываю это здесь для MouseDown на холсте.
private void cvsTest_MouseDown(object sender, MouseButtonEventArgs e)
{
Point ptClicked = e.GetPosition(cvsTest);
if (e.LeftButton.Equals(MouseButtonState.Pressed))
{
Color pxlColor = ImagingTools.GetPixelColor(cvsTest, ptClicked);
MessageBox.Show("Color String = " + pxlColor.ToString());
}
}
К вашему сведению, ImagingTools - это класс, в котором я храню статические методы, связанные с отображением.