Photoshop可以创建若干个图层,在某一个图层上修改或添加图像时,不会影响其它图层上的图像。
   我们一般绘图在窗体的Paint()中完成,当要绘图时,使用Invalidate()方法触发Paint事件,但即使给Invalidate()指定刷新区域,但在该区域有多个绘图叠加时,总是不靠控制。譬如编写象棋程序,当鼠标带着棋子移动,由于频繁调用Paint(),会导致移动棋子时,整个绘图区域闪动,我想Photoshop的图层技术可很好的解决这些问题,不知这种编程技术如何实现?

解决方案 »

  1.   

    http://www.eecs.wsu.edu/paint.net/
    参考Paint.net
      

  2.   

    我到你说的网站上去把源代码给下载下来了,用VS2003打开时,总有几个工程加载不了,如Setup_Config等,不知为何
      

  3.   

    我看了一下Paint.NET程序,在绘图那块,还是使用的Windows API,而不是GDI+,例如:
      public static void DrawBitmap(Graphics dst, Rectangle dstRect, Matrix dstMatrix, Bitmap srcBitmap, Point srcOffset)
            {
                Point[] points = new Point[] { dstRect.Location };
                dstMatrix.TransformPoints(points);
                dstRect.Location = points[0];            IntPtr hdc = IntPtr.Zero;
                IntPtr hbitmap = IntPtr.Zero;
                IntPtr chdc = IntPtr.Zero;
                IntPtr old = IntPtr.Zero;            try
                {
                    hdc = dst.GetHdc();
                    hbitmap = srcBitmap.GetHbitmap();
                    chdc = SafeNativeMethods.CreateCompatibleDC(hdc);
                    old = SafeNativeMethods.SelectObject(chdc, hbitmap);
                    SafeNativeMethods.BitBlt(hdc, dstRect.Left, dstRect.Top, dstRect.Width, dstRect.Height, chdc, srcOffset.X, srcOffset.Y, NativeConstants.SRCCOPY);
                }            finally
                {
                    if (old != IntPtr.Zero)
                    {
                        SafeNativeMethods.SelectObject(chdc, old);
                        old = IntPtr.Zero;
                    }                if (chdc != IntPtr.Zero)
                    {
                        SafeNativeMethods.DeleteDC(chdc);
                        chdc = IntPtr.Zero;
                    }

                    if (hbitmap != IntPtr.Zero)
                    {
                        SafeNativeMethods.DeleteObject(hbitmap);
                        hbitmap = IntPtr.Zero;
                    }                if (hdc != IntPtr.Zero)
                    {
                        dst.ReleaseHdc(hdc);
                        hdc = IntPtr.Zero;
                    }
                }
            }
    这是典型的VC编程(幸好我还学过几个月的VC编程),老大,我想问得是怎样通过GDI+实现,不想作过多的解释,给一个例子也行啊!vb.net 、c#的例子均可。
       急需!!!
      

  4.   

    用几个镂空的image不就是图层了嘛
    叠加显示即可,按照Z轴顺序
      

  5.   

    这个可不行,如果我想在几个图层之间切换,难道不断变换坐标吗,更何况,GDI+编程只能是2D编程(x,y坐标),难道不成要使用DirectX?
        我现在空闲时在把一个用VC++编写的象棋程序转换成C#程序,现在的难题就出在用鼠标拖动棋子(棋子用的是图片)时,由于不断重绘,导致棋面闪烁,即使只重绘局部。而用VC++实现起来非常简单,不知用GDI+如何实现,请不要告诉我使用Windows API实现。
       另一个问题就是使用ImageList类或者ImageList控件装入硬盘中的图片后,程序中再把这些图片显示出来时,这些图片就会变形,我本来在这个象棋程序中想使用ImageList对象管理种棋子图片,但是由于变形,我不敢用,而只好在窗体的初始化事件中创建这些棋子的位图对象。
        请高手指点指点!
      

  6.   

    试试这样,
    当你用鼠标拖动棋子重绘之后,把窗口上的图全部存在一张图片上,再调用  
    protected override void OnPaintBackground(object sender,PaintEventArgs e)
        {
          Graphics g=this.CreatedGraphics();
          g.DrawImage(theImage,this.ClientRectangle);
         }
    这样你就把图象都绘到了背景上,
    当你再拖动鼠标,刷新,由于背景图象和刚才的图象重合,就不会出现闪烁的了,至于第二个问题,我也不太懂,用了几次ImageList,图象模糊,分辨率降低,...不知道是微软的极度设计失误,还是我的极度不会用.
      

  7.   

    你这种方法也许可行,也许不可行,我没试过,但即使可行,也会大大降低程序的性能。
       至于不行的原因,那就是在鼠标移动事件中,我调用了Invalidate()方法,强制窗体重新绘制图形,因为若不使用它,则拖动的棋子会留下一长串的图形,而不会丢失。Invalidate()方法首先强制窗口把一定区域内的内容给抹掉,然后再触发Paint()事件,当然也可能包括你上面使用的PaintBackground事件,这样窗口就曾在一个从有图形----〉无图形----〉有图形的变化,闪烁就不可避免。这是我在Paint()中写的一个方法,大家看有没有好方法:
      //响应PAINT 消息,绘制棋盘,棋子
    private void frmChess_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
    {   
       int i,j;
      Point pt;
      Graphics dc=Graphics.FromImage(bmp);
     //绘制棋子到画布
        for(i=0;i<10;i++)
           for(j=0;j<9;j++)
    {  if(m_ChessBoard[i,j]==ChessData.NOCHESS)
       continue;
    pt=new Point(j*GRILLEWIDTH+5,i*GRILLEHEIGHT+5);
    Rectangle rect=new Rectangle(pt.X,pt.Y,42,42);
            dc.DrawImage(this.chessBmp[m_ChessBoard[i,j]-1],rect); }

                //输出到设备
    Graphics g=e.Graphics;
    g.DrawImageUnscaled(bmp,e.ClipRectangle);

    //绘制用户正在拖动的棋子
    if(m_MoveChess.nChessID!=ChessData.NOCHESS)
    {
                  g.DrawImage(this.chessBmp[m_MoveChess.nChessID-1],m_MoveChess.ptMovePoint);
    }

    }
      

  8.   

    你好象没看懂paintbackground的含义,
    -------------------------------------
    这样窗口就曾在一个从有图形----〉无图形----〉有图形的变化,闪烁就不可避免
    -----------------------------------------
    之所以会出现你这样的的闪烁,是因为.
    有图形----〉你看到的是你在窗口上绘的图象
    无图形----〉你调用Invalidate()方法后,窗口上的图象没了,此时你看到的是窗口背景图象(浅灰色)
    有图形----> 此时你看到的是你再一次在窗口上绘的图象在这个过程中你看见了3幅图象,中间那张和俩边的图象几乎截然不同,所以你有了闪烁的感觉但是如果你已经调用paintbackground()把中间那张背景图象绘成和第一张一样的,那么这3张图象几乎一样的,你就不会有闪烁的感觉了,
    至于程序性能,可能会降吧,但是你一个象棋游戏,很耗系统资源么?这里有一个棋类游戏,C#写的,我也没细看,或许对你有帮助吧.
    http://www.tiantiansoft.com/bbs/w1sev_2004-3/20042323132675074.rar
      

  9.   

    我没有说错,默认情况下,Invalidate()方法会将窗口背景也抹去,即将Paint()中呈现的任何图形给抹掉,所以必然会曾在有图形----〉无图形----〉有图形的变化,我昨天尝试使用如下编程:
      this.SetStyle(ControlStyles.Opaque,true);即设定让窗体在调用Invalidate()方法时不会抹去背景,该方法很管用,当拖动棋子时,棋盘没任何闪烁。但它会导致另一个问题,即当窗口被遮挡后,当窗口重现时,背景不会被重绘,因此在编程时我在棋子拖动完成时,加了一条语句:
       this.SetStyle(ControlStyles.Opaque,false);
    但它好像不起丝毫作用,即不会让窗体恢复重绘背景的功能。
       同时,我也测试了一下你提供的方法,
     protected override void OnPaintBackground ( System.Windows.Forms.PaintEventArgs pevent )
    {
     }
    该方法中若没有任何代码时,作用与
        this.SetStyle(ControlStyles.Opaque,true);完全相同,当在其中按你说的加入代码后
    protected override void OnPaintBackground ( System.Windows.Forms.PaintEventArgs pevent )
    {
    //输出到设备
    Graphics g=pevent.Graphics;
    //将背景图重画
    g.DrawImageUnscaled(bmp,0,0);
    }  其结果如上面使用的this.SetStyle(ControlStyles.Opaque,false);  没有任何变化,即不会让窗体恢复重绘背景的功能。
      sorry,你提供的位置----黑白棋程序不存在过高的刷新频率,因此不会有闪烁。
      难道我只能使用Windows API吗?