2012-01-27 123 views
1

正如标题所说,我想用XOR模式绘制某些内容,因为我想在一段时间后清理它。在C#中调用DrawRectangle方法时,如何使用异或模式

我使用C#(窗口形式)与Visual Studio 2010

任何人可以帮助我吗?

+1

向我们展示你已经尝试了什么。 – gabsferreira 2012-01-27 13:45:40

+3

XOR操作完全从GDI +中删除。剩下一个后门:ControlPaint.DrawReversibleFrame()。 – 2012-01-27 13:54:05

回答

1

我为.NET一个很好的扩展:

public static class XorDrawing 
{ 

    [DllImport("gdi32.dll", EntryPoint = "SetROP2", CallingConvention = CallingConvention.StdCall)] 
    private extern static int SetROP2(IntPtr hdc, int fnDrawMode); 

    [DllImport("gdi32.dll", EntryPoint = "MoveToEx", CallingConvention = CallingConvention.StdCall)] 
    private extern static bool MoveToEx(IntPtr hdc, int x, int y, IntPtr lpPoint); 

    [DllImport("gdi32.dll", EntryPoint = "LineTo", CallingConvention = CallingConvention.StdCall)] 
    private extern static bool LineTo(IntPtr hdc, int x, int y); 

    [DllImport("gdi32.dll", SetLastError = true)] 
    static extern IntPtr CreateCompatibleDC(IntPtr hdc); 

    [DllImport("gdi32.dll", EntryPoint = "SelectObject")] 
    public static extern IntPtr SelectObject([In] IntPtr hdc, [In] IntPtr hgdiobj); 

    [DllImport("gdi32.dll")] 
    static extern bool DeleteObject(IntPtr target); 

    [DllImport("gdi32.dll")] 
    static extern IntPtr CreatePen(PenStyle fnPenStyle, int nWidth, uint crColor); 

    [DllImport("gdi32.dll")] 
    static extern bool SetWorldTransform(IntPtr hdc, [In] ref XFORM lpXform); 

    [DllImport("gdi32.dll")] 
    public static extern int SetGraphicsMode(IntPtr hdc, int iMode); 

    /// <summary> 
    /// The XFORM structure specifies a world-space to page-space transformation. 
    /// </summary> 
    [StructLayout(LayoutKind.Sequential)] 
    public struct XFORM 
    { 
     public float eM11; 
     public float eM12; 
     public float eM21; 
     public float eM22; 
     public float eDx; 
     public float eDy; 

     public XFORM(float eM11, float eM12, float eM21, float eM22, float eDx, float eDy) 
     { 
      this.eM11 = eM11; 
      this.eM12 = eM12; 
      this.eM21 = eM21; 
      this.eM22 = eM22; 
      this.eDx = eDx; 
      this.eDy = eDy; 
     } 

     /// <summary> 
     /// Allows implicit converstion to a managed transformation matrix. 
     /// </summary> 
     public static implicit operator System.Drawing.Drawing2D.Matrix(XFORM xf) 
     { 
      return new System.Drawing.Drawing2D.Matrix(xf.eM11, xf.eM12, xf.eM21, xf.eM22, xf.eDx, xf.eDy); 
     } 

     /// <summary> 
     /// Allows implicit converstion from a managed transformation matrix. 
     /// </summary> 
     public static implicit operator XFORM(System.Drawing.Drawing2D.Matrix m) 
     { 
      float[] elems = m.Elements; 
      return new XFORM(elems[0], elems[1], elems[2], elems[3], elems[4], elems[5]); 
     } 
    } 

    public enum BinaryRasterOperations 
    { 
     R2_BLACK = 1, 
     R2_NOTMERGEPEN = 2, 
     R2_MASKNOTPEN = 3, 
     R2_NOTCOPYPEN = 4, 
     R2_MASKPENNOT = 5, 
     R2_NOT = 6, 
     R2_XORPEN = 7, 
     R2_NOTMASKPEN = 8, 
     R2_MASKPEN = 9, 
     R2_NOTXORPEN = 10, 
     R2_NOP = 11, 
     R2_MERGENOTPEN = 12, 
     R2_COPYPEN = 13, 
     R2_MERGEPENNOT = 14, 
     R2_MERGEPEN = 15, 
     R2_WHITE = 16 
    } 

    private enum PenStyle : int 
    { 
     PS_SOLID = 0, //The pen is solid. 
     PS_DASH = 1, //The pen is dashed. 
     PS_DOT = 2, //The pen is dotted. 
     PS_DASHDOT = 3, //The pen has alternating dashes and dots. 
     PS_DASHDOTDOT = 4, //The pen has alternating dashes and double dots. 
     PS_NULL = 5, //The pen is invisible. 
     PS_INSIDEFRAME = 6,// Normally when the edge is drawn, it’s centred on the outer edge meaning that half the width of the pen is drawn 
     // outside the shape’s edge, half is inside the shape’s edge. When PS_INSIDEFRAME is specified the edge is drawn 
     //completely inside the outer edge of the shape. 
     PS_USERSTYLE = 7, 
     PS_ALTERNATE = 8, 
     PS_STYLE_MASK = 0x0000000F, 

     PS_ENDCAP_ROUND = 0x00000000, 
     PS_ENDCAP_SQUARE = 0x00000100, 
     PS_ENDCAP_FLAT = 0x00000200, 
     PS_ENDCAP_MASK = 0x00000F00, 

     PS_JOIN_ROUND = 0x00000000, 
     PS_JOIN_BEVEL = 0x00001000, 
     PS_JOIN_MITER = 0x00002000, 
     PS_JOIN_MASK = 0x0000F000, 

     PS_COSMETIC = 0x00000000, 
     PS_GEOMETRIC = 0x00010000, 
     PS_TYPE_MASK = 0x000F0000 
    }; 

    public enum GraphicsMode : int 
    { 
     GM_COMPATIBLE = 1, 
     GM_ADVANCED = 2, 
    } 

    private static IntPtr BeginDraw(System.Drawing.Bitmap bmp, System.Drawing.Graphics graphics, int x1, int y1, int x2, int y2, bool dash, out int oldRop, out IntPtr img, out IntPtr oldpen) 
    { 
     var gHdc = graphics.GetHdc(); 
     var hdc = CreateCompatibleDC(gHdc); 
     graphics.ReleaseHdc(hdc); 

     img = bmp.GetHbitmap(); 
     SelectObject(hdc, img); 

     oldpen = IntPtr.Zero; 
     if (dash) 
     { 
      var pen = CreatePen(PenStyle.PS_DASH, 1, 0); 
      oldpen = SelectObject(hdc, pen); 
     } 
     oldRop = SetROP2(hdc, (int)BinaryRasterOperations.R2_NOTXORPEN); // Switch to inverted mode. (XOR) 

     SetGraphicsMode(hdc, (int)GraphicsMode.GM_ADVANCED); 
     XFORM transform = graphics.Transform; 
     SetWorldTransform(hdc, ref transform); 

     return hdc; 
    } 


    private static void FinishDraw(System.Drawing.Bitmap bmp, System.Drawing.Graphics graphics, IntPtr hdc, IntPtr oldpen, int oldRop, IntPtr img, bool dash) 
    { 
     SetROP2(hdc, oldRop); 

     var transform = graphics.Transform; 
     graphics.ResetTransform(); //in case there is transform 
     var outBmp = System.Drawing.Image.FromHbitmap(img); 
     //CopyChannel(bmp, outBmp, ChannelARGB.Alpha, ChannelARGB.Alpha); 
     graphics.Clear(Color.Transparent); 
     graphics.DrawImage(outBmp, 0, 0); //draw the xored image on the bitmap 
     graphics.Transform = transform; 

     if (dash) DeleteObject(SelectObject(hdc, oldpen)); //delete new pen (switch to oldpen) 
     DeleteObject(img); // Delete the GDI bitmap (important). 
     DeleteObject(hdc); 
    } 

    public static void DrawXorLine(this System.Drawing.Graphics graphics, System.Drawing.Bitmap bmp, int x1, int y1, int x2, int y2, bool dash = true) 
    { 
     int oldRop; 
     IntPtr oldpen, img; 
     var hdc = BeginDraw(bmp, graphics, x1, y1, x2, y2, dash, out oldRop, out img, out oldpen); 

     MoveToEx(hdc, x1, y1, IntPtr.Zero); 
     LineTo(hdc, x2, y2); 

     FinishDraw(bmp, graphics, hdc, oldpen, oldRop, img, dash); 
    } 

    public static void DrawXorRectangle(this System.Drawing.Graphics graphics, System.Drawing.Bitmap bmp, int x1, int y1, int x2, int y2, bool dash = true) 
    { 
     int oldRop; 
     IntPtr oldpen, img; 
     var hdc = BeginDraw(bmp, graphics, x1, y1, x2, y2, dash, out oldRop, out img, out oldpen); 

     MoveToEx(hdc, x1, y1, IntPtr.Zero); //clockwise 
     LineTo(hdc, x2, y1); 
     LineTo(hdc, x2, y2); 
     LineTo(hdc, x1, y2); 
     LineTo(hdc, x1, y1); 

     FinishDraw(bmp, graphics, hdc, oldpen, oldRop, img, dash); 
    } 
} 

使用

g.DrawXorRectangle(bmp, outRect.Left, outRect.Top, outRect.Left + outRect.Width, outRect.Top + outRect.Height); 

或者

g.DrawXorLine(bmp, x1, y1, x2, y2); 
0

您应该更具体地了解您正在使用的内容,但我假设GDI +和Windows窗体。

Region region = new Region(); 
region.MakeEmpty(); 
region.Xor(rectangle1); 
region.Xor(rectangle2); 
e.Graphics.FillRegion(Brushes.Black, region); // use e.Graphics if in Paint event 
+0

你好,我正在使用窗体。我已经覆盖了类PictureBox的OnPaint方法来绘制一个矩形 – 2012-01-27 13:56:15

+1

然后代码应该为你工作 – 2012-01-27 14:02:21

+2

这创建了一个区域,并且与OP的问题无关。 – TaW 2014-09-16 06:54:51

2

您可以使用Windows API函数。我将这些导入包装在静态类Win32中。

public static class Win32 
{ 
    [DllImport("gdi32.dll", EntryPoint = "SetROP2", CallingConvention = CallingConvention.StdCall)] 
    public extern static int SetROP2(IntPtr hdc, int fnDrawMode); 

    [DllImport("user32.dll", EntryPoint = "GetDC", CallingConvention = CallingConvention.StdCall)] 
    public extern static IntPtr GetDC(IntPtr hWnd); 

    [DllImport("user32.dll", EntryPoint = "ReleaseDC", CallingConvention = CallingConvention.StdCall)] 
    public extern static IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC); 

    [DllImport("gdi32.dll", EntryPoint = "MoveToEx", CallingConvention = CallingConvention.StdCall)] 
    public extern static bool MoveToEx(IntPtr hdc, int x, int y, IntPtr lpPoint); 

    [DllImport("gdi32.dll", EntryPoint = "LineTo", CallingConvention = CallingConvention.StdCall)] 
    public extern static bool LineTo(IntPtr hdc, int x, int y); 

    public const int R2_NOT = 6; // Inverted drawing mode 

} 

使用这些定义可以得出这样的

IntPtr hdc = Win32.GetDC(IntPtr.Zero); // Get device context. 
Win32.SetROP2(hdc, Win32.R2_NOT); // Switch to inverted mode. (XOR) 
Win32.MoveToEx(hdc, x1, y1, IntPtr.Zero); 
Win32.LineTo(hdc, x2, y2); 
Win32.ReleaseDC(IntPtr.Zero, hdc); // Release device context. 

注意,通过Graphics对象通过.NET提供的标准的绘图功能不倒模式下工作。您必须使用API​​的功能,这里以MoveToExLineTo为例。


我从我的代码项目文章Drag-and-Drop ListBox中提取了这些示例。

0
/// <summary> 
/// Wrapper class for the gdi32.dll. 
/// </summary> 
public class Gdi32 
{ 
    public enum DrawingMode 
    { 
     R2_NOTXORPEN = 10 
    } 

    [DllImport("gdi32.dll")] 
    public static extern bool Rectangle(IntPtr hDC, int left, int top, int right, int bottom); 

    [DllImport("gdi32.dll")] 
    public static extern int SetROP2(IntPtr hDC, int fnDrawMode); 

    [DllImport("gdi32.dll")] 
    public static extern bool MoveToEx(IntPtr hDC, int x, int y, ref Point p); 

    [DllImport("gdi32.dll")] 
    public static extern bool LineTo(IntPtr hdc, int x, int y); 

    [DllImport("gdi32.dll")] 
    public static extern IntPtr CreatePen(int fnPenStyle, int nWidth, int crColor); 

    [DllImport("gdi32.dll")] 
    public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObj); 

    [DllImport("gdi32.dll")] 
    public static extern bool DeleteObject(IntPtr hObj); 

} 

/// <summary> 
/// Provides utilities directly accessing the gdi32.dll 
/// </summary> 
public class GDI 
{ 
    static private Point nullPoint = new Point(0,0); 

    // Convert the Argb from .NET to a gdi32 RGB 
    static private int ArgbToRGB(int rgb) 
    { 
     return ((rgb >> 16 & 0x0000FF)| (rgb & 0x00FF00) | (rgb << 16 & 0xFF0000)); 
    } 
    static public void DrawXORRectangle(Graphics graphics, Pen pen, Rectangle rectangle) 
    { 
     IntPtr hDC = graphics.GetHdc(); 
     IntPtr hPen = Gdi32.CreatePen(0, (int)pen.Width, ArgbToRGB(pen.Color.ToArgb())); 
     Gdi32.SelectObject(hDC, hPen); 
     Gdi32.SetROP2(hDC, (int)Gdi32.DrawingMode.R2_NOTXORPEN); 
     Gdi32.Rectangle(hDC, rectangle.Left, rectangle.Top, rectangle.Right,rectangle.Bottom); 
     Gdi32.DeleteObject(hPen); 
     graphics.ReleaseHdc(hDC); 
    } 

    static public void DrawXORLine(Graphics graphics, Pen pen, int x1, int y1, int x2, int y2) 
    { 
     IntPtr hDC = graphics.GetHdc(); 
     IntPtr hPen = Gdi32.CreatePen(0, (int)pen.Width, ArgbToRGB(pen.Color.ToArgb())); 
     Gdi32.SelectObject(hDC, hPen); 
     Gdi32.SetROP2(hDC, (int)Gdi32.DrawingMode.R2_NOTXORPEN); 
     Gdi32.MoveToEx(hDC, x1, y1, ref nullPoint); 
     Gdi32.LineTo(hDC, x2, y2); 
     Gdi32.DeleteObject(hPen); 
     graphics.ReleaseHdc(hDC); 
    } 
}