我想要显示黄色“背后”的东西。
编辑 1:但是,如果我在“白色”上绘图,我希望标记颜色保持其纯黄色。
编辑 2:@Kevin 的答案可能是正确的,我将其标记为正确,即使我没有对其进行编码。在我的代码中,我使用 Color.FromArgb 来解决 @Guffa 的答案。
编辑 3:我发布了性能不错的代码。是的,减去蓝色是基本思想,但是你不能用高级 API 来做,而且 SetPixel 太慢了。性能好的方案使用Bitmap.LockBits、UnlockBits。
我想要显示黄色“背后”的东西。
编辑 1:但是,如果我在“白色”上绘图,我希望标记颜色保持其纯黄色。
编辑 2:@Kevin 的答案可能是正确的,我将其标记为正确,即使我没有对其进行编码。在我的代码中,我使用 Color.FromArgb 来解决 @Guffa 的答案。
编辑 3:我发布了性能不错的代码。是的,减去蓝色是基本思想,但是你不能用高级 API 来做,而且 SetPixel 太慢了。性能好的方案使用Bitmap.LockBits、UnlockBits。
荧光笔是颜料,所以它本质上是减色的——你想把白色变成黄色,而不是把黑色变成黄色。我不知道 .NET,但你想要的是一个非默认的“混合模式”,特别是减法。具体来说,将混合模式设置为减去,将颜色设置为纯蓝色(要减去的颜色,留下黄色)。黑色将被单独留下,因为没有蓝色可以减去,白色会变成黄色。
不幸的是,许多现代绘图界面都忽略了除 alpha 之外的混合模式,看起来这可能就是其中之一。如果您可以访问位图,您可以自己实现它——获取每个像素值并将蓝色分量设置为零。或者,如果您要突出显示的区域很复杂,那么:复制您的图像,在副本中突出显示的区域上绘制黑色,然后将原始的红色和绿色通道以及副本中的蓝色通道组合到最终结果图像。
您使用半透明颜色,例如 50% 的 alpha 不透明度:
Color.FromArgb(128, Color.Yellow)
使用较低的 alpha 值,将显示更多的背景。
这是您需要的代码:
protected override void OnPaint( PaintEventArgs e )
{
using ( var bmp = new Bitmap( 100, 100 ) )
using ( var g = Graphics.FromImage( bmp ) )
using ( var ia = new ImageAttributes() )
{
// 1. create a sample bitmap
g.Clear( Color.White );
var p = Point.Empty;
foreach ( var color in new Color[] { Color.Black, Color.Gray, Color.LightBlue, Color.Green, Color.Red, Color.Magenta } )
using ( var brush = new SolidBrush( color ) )
{
g.DrawString( "Some sample text", SystemFonts.DefaultFont, brush, p );
p.Offset( 0, 16 );
}
// 2. transfer the bitmap on screen
e.Graphics.DrawImage( bmp, Point.Empty );
// 3. transfer a part of the bitmap on screen again, this time removing all blue
ia.SetColorMatrix( new ColorMatrix( new float[][] {
new float[] {1, 0, 0, 0, 0},
new float[] {0, 1, 0, 0, 0},
new float[] {0, 0, 0, 0, 0},
new float[] {0, 0, 0, 1, 0},
new float[] {0, 0, 0, 0, 1}} ) );
e.Graphics.DrawImage(
bmp,
new Rectangle( 30, 0, 40, 100 ),
30, 0, 40, 100,
GraphicsUnit.Pixel,
ia );
}
}
此代码有效。它消除了我想要黄色的每个 RGB 的蓝色分量。起初我尝试使用 Bitmap.GetPixel/SetPixel,但这非常慢。使用锁定/解锁来获取原始位的速度足够快。
using (Bitmap tempBitmap = new Bitmap(bitmap.Width, bitmap.Height))
{
using (Graphics tempG = Graphics.FromImage(tempBitmap))
{
tempG.DrawLines(penYellowHighlighter, stroke.points.ToArray());
// get the raw bits of the source and target and remove the blue from every
// bit of the target where there is a yellow bit of the source
Rectangle rect = new Rectangle(0, 0, bitmapWithStrokes.Width, bitmapWithStrokes.Height);
// lock
System.Drawing.Imaging.BitmapData sourceData =
tempBitmap.LockBits(
rect,
System.Drawing.Imaging.ImageLockMode.ReadOnly,
tempBitmap.PixelFormat);
System.Drawing.Imaging.BitmapData targetData =
bitmapWithStrokes.LockBits(
rect,
System.Drawing.Imaging.ImageLockMode.ReadWrite,
bitmapWithStrokes.PixelFormat);
// Get the address of the first line.
IntPtr sourcePtr = sourceData.Scan0;
IntPtr targetPtr = targetData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int numberOfBytes = Math.Abs(sourceData.Stride) * tempBitmap.Height;
byte[] sourceRgbValues = new byte[numberOfBytes];
byte[] targetRgbValues = new byte[numberOfBytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(sourcePtr, sourceRgbValues, 0, numberOfBytes);
System.Runtime.InteropServices.Marshal.Copy(targetPtr, targetRgbValues, 0, numberOfBytes);
for (int p = 0; p < numberOfBytes; p += 4)
{
// if the source's red is yellows's red
if (sourceRgbValues[p + 2] == yellowsRedComponent)
{
// wipe out the target's blue
targetRgbValues[p] = 0;
}
}
// Copy the RGB values back to the bitmap
System.Runtime.InteropServices.Marshal.Copy(targetRgbValues, 0, targetPtr, numberOfBytes);
// Unlock the bits.
tempBitmap.UnlockBits(sourceData);
bitmapWithStrokes.UnlockBits(targetData);
尝试使用具有小于 255 的 alpha 值的SolidBrush
初始化Color
值。这应该会创建一个您使用的颜色的半透明画笔。