简体   繁体   English

如何绘制透明矩形?

[英]How do I draw a transparent rectangle?

I am nearly there with this ... :) 我几乎在那里... :)

I have implemented my own double buffer ... 我已经实现了自己的双缓冲区...

So I create a bitmap: 所以我创建一个位图:

if (_Bitmap != null) _Bitmap.Dispose();
if (_Graphics != null) _Graphics.Dispose();

_Bitmap = new Bitmap(Bounds.Width, Bounds.Height);
_Bitmap.MakeTransparent(Color.Transparent);
_Graphics = Graphics.FromImage(_Bitmap);
_Graphics.FillRectangle(Brushes.Transparent, Bounds);

I thought that I might have to manually set the bitmap as transparent. 我以为我可能必须手动将位图设置为透明。

In my handlers OnPaint method it does this: 在我的处理程序的OnPaint方法中,它执行以下操作:

protected override void OnPaint(PaintEventArgs e)
{
    if (_pDevice != null)
    {
        try
        {
            _pDevice.update();
            _Graphics.ReleaseHdc();

            if (_bZoomWindow)
            {
                //_Graphics.DrawRectangle(_selectionPen, _rcRubberBand);
                using (GraphicsPath gp = new GraphicsPath())
                {
                    gp.AddRectangle(_rcRubberBand);
                    gp.Widen(_selectionPen);
                    _Graphics.FillPath(Brushes.WhiteSmoke, gp);
                }
            }

            OdRxDictionary Properties = _graphicsDevice.properties();
            //if (helperDevice.UnderlyingDevice.Properties.Contains("WindowHDC"))
            //    helperDevice.UnderlyingDevice.Properties.AtPut("WindowHDC", new RxVariant((Int32)graphics.GetHdc()));
            if (Properties.ContainsKey("WindowHDC"))
                Properties.putAt("WindowHDC", new OdRxVariantValue(_Graphics.GetHdc().ToInt32())); // hWnd necessary for DirectX device
        }
        catch (System.Exception ex)
        {
            _Graphics.DrawString(ex.ToString(), new Font("Arial", 16), new SolidBrush(Color.Black), new PointF(150.0F, 150.0F));
        }

        e.Graphics.DrawImageUnscaled(_Bitmap, 0, 0);
    }
}

The problem is that the rectangle is drawing with a black background. 问题在于矩形正在绘制黑色背景。 So it is obliterating the drawing underneath that is on the bitmap: 因此,它消除了位图下方的图形:

缩放视窗

How do I draw just the rectangle? 如何仅绘制矩形? What am I missing? 我想念什么? I am sorry if this is a dumb question! 抱歉,这是一个愚蠢的问题!

Painting with transparency is unfortunately only supported in one way: By applying the RGB channels in the strenght of the alpha value. 不幸的是,仅以一种方式支持使用透明绘画:通过以alpha值的强度应用RGB通道。

With alpha = 0 nothing happens. alpha = 0 没有任何反应

Other modes are desirable but not supported in GDI+ drawing. 其他模式是理想的,但GDI+图形中不支持。

One simple workaround is to turn off antialiasing, draw in a weird color you don't need and then call MakeTransparent . 一种简单的解决方法是关闭抗锯齿,绘制不需要的怪异颜色,然后调用MakeTransparent

Bitmap bmp = new Bitmap(244, 244, PixelFormat.Format32bppArgb);
Color funnyColor = Color.FromArgb(255, 123, 45, 67);

using (Graphics g = Graphics.FromImage(bmp))
{
    g.Clear(Color.LawnGreen);
    using (SolidBrush br = new SolidBrush(funnyColor ))
    {
        // no anti-aliased pixels!
        g.SmoothingMode = SmoothingMode.None;
        // draw your stuff..
        g.FillEllipse( br , 14, 14, 88, 88);
    }       
    bmp.MakeTransparent(funnyColor );
    // do what you want..
    bmp.Save(someFileName, ImageFormat.Png);
}

Of course you can use all DrawXXX methods including FillPath or DrawRectangle . 当然,您可以使用所有DrawXXX方法,包括FillPathDrawRectangle

The result is a green bitmap with a transparent hole in it. 结果是一个绿色的位图,其中有一个透明的孔。 Here it is in Paint.Net: 这是在Paint.Net中:

例

For other modes, that maybe would copy the alpha channel or mix it with the previous value you would have to write routines of your own or find a lib that has it, but I think this should be all you need atm. 对于其他模式,可能会复制alpha通道或将其与先前的值混合,则您必须编写自己的例程或找到具有该通道的库,但是我认为这应该是您需要的atm。


Edit by Andrew Truckle 安德鲁·特拉克(Andrew Truckle)编辑

The proposed answer is really good. 建议的答案确实很好。 However, since I was using Teigha.Net as the basis of the application, in the end I went with this code: 但是,由于我使用Teigha.Net作为应用程序的基础,所以最终我使用了以下代码:

protected override void OnMouseMove(MouseEventArgs e)
{
    if (_bZoomWindowing)
        UpdateRubberBandRectangle(e.Location);

    if (_bPanWindowMode)
        UpdateRubberBandLine(e.Location);

    base.OnMouseMove(e);
}

private void UpdateRubberBandRectangle(Point Location)
{
    // Do we need to erase the old one?
    if (!_rcLastRubberBand.IsEmpty)
    {
        using (Region r = new Region(Rectangle.Inflate(_rcLastRubberBand, 2, 2)))
        {
            r.Exclude(Rectangle.Inflate(_rcLastRubberBand, -2, -2));

            _pDevice.invalidate(new OdGsDCRect(_rcLastRubberBand.Left - 2, _rcLastRubberBand.Right + 2,
                                           _rcLastRubberBand.Top - 2, _rcLastRubberBand.Bottom + 2));
            Invalidate(r);
        }
    }

    // Draw the new one
    if (!_selectionStart.IsEmpty && !_selectionEnd.IsEmpty && _selectionEnd != Location)
    {
        _rcLastRubberBand = _rcRubberBand;

        _selectionEnd = Location;
        _rcRubberBand = GetSelectionRectangle(_selectionStart, _selectionEnd);

        using (Region r = new Region(Rectangle.Inflate(_rcRubberBand, 2, 2)))
        {
            r.Exclude(Rectangle.Inflate(_rcRubberBand, -2, -2));

            _pDevice.invalidate(new OdGsDCRect(_rcRubberBand.Left - 2, _rcRubberBand.Right + 2,
                                           _rcRubberBand.Top - 2, _rcRubberBand.Bottom + 2));
            Invalidate(r);
        }
    }
}

private void UpdateRubberBandLine(Point Location)
{
    // Do we need to erase the last rubber band line? (Rectangle already expanded by 2 pixels)
    if (!_rcLastRubberBandLine.IsEmpty)
    {
        using (Region r = new Region(_rcLastRubberBandLine))
        {
            _pDevice.invalidate(new OdGsDCRect(_rcLastRubberBandLine.Left, _rcLastRubberBandLine.Right,
                                               _rcLastRubberBandLine.Top, _rcLastRubberBandLine.Bottom));
            Invalidate(r);
        }

    }

    // Draw the new one now
    _RubberLineEnd = Location;
    _rcLastRubberBandLine = GetSelectionRectangle(_RubberLineStart, _RubberLineEnd);
    _rcLastRubberBandLine.Inflate(2, 2);
    using (Region r = new Region(_rcLastRubberBandLine))
    {
        _pDevice.invalidate(new OdGsDCRect(_rcLastRubberBandLine.Left, _rcLastRubberBandLine.Right,
                                           _rcLastRubberBandLine.Top, _rcLastRubberBandLine.Bottom));
        Invalidate(r);
    }
}

Notice that I am making use of Region objects. 请注意,我正在使用Region对象。 Also, the invalidating is being handled by OdGsDevice _pDevice which is a Teigha object. 同样,无效化也由作为Teigha对象的OdGsDevice _pDevice处理。 In my situation this worked fabulously. 在我的情况下,这非常有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM