问题
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:
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!
回答1:
Painting with transparency is unfortunately only supported in one way: By applying the RGB
channels in the strenght of the alpha
value.
With alpha = 0
nothing happens.
Other modes are desirable but not supported in GDI+
drawing.
One simple workaround is to turn off antialiasing, draw in a weird color you don't need and then call 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
.
The result is a green bitmap with a transparent hole in it. Here it is in 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.
Edit by 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:
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. Also, the invalidating is being handled by OdGsDevice _pDevice
which is a Teigha object. In my situation this worked fabulously.
来源:https://stackoverflow.com/questions/37646892/how-do-i-draw-a-transparent-rectangle