我在C#中使用WinForms。我有表单,其不透明度设置为 X 。我想绘制一个填充矩形,其不透明度为 Y 。
如果我这样画:
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.FillRectangle(
new SolidBrush(Color.FromArgb(Y, 255, 0, 0)),//Red brush (Y opacity)
new Rectangle(100,100,500,500)
);
}
然后,我对矩形绘图的Y
的{{1}}值乘以我的表单的opacity of the brush
不透明度,所以我得到X
。
我认为这与来自表单的X*Y
有关。我怎么能这样做呢?
答案 0 :(得分:1)
您没有在屏幕上绘图,而是在绘制基础曲面。控件和曲面是分层的,因此如果你在50%的东西上绘制100%不透明度的东西,结果将是50%的不透明度。这就是桌面组合的工作原理。与Photoshop不同的是,你可以利用30种不同的构图模式,它只是普通的图层"。
表单的不透明度未隐藏在Graphics
元素中。相反,您要在透明表面上绘制漂亮的100%不透明矩形;结果是透明表面上的100%不透明矩形。但是这个表面绘制在具有50%不透明度的表面上 - 并且不透明度相乘。要更改此设置,您必须更改整个表单的呈现方式,而不仅仅是您的控件。
解决这个问题的唯一方法是使用更黑巧的方式使表单透明 - 例如,强制除之外的所有以不同的不透明度绘制,包括表单边框等。这显然有点难 - 没有.NET方法可以帮助你这样做,你必须直接使用WinAPI,覆盖一些窗口消息处理等等。这太过分了工作
当然有解决方法。例如,您可以在不同的无边框形式上显示矩形,其移动方式与底层窗体相同。
答案 1 :(得分:1)
You can use UpdateLayeredWindow()
API to draw with per pixel alpha.
First make your window without borders and add the WS_EX_LAYERED
style:
protected override CreateParams CreateParams
{
get
{
// Add the layered extended style (WS_EX_LAYERED) to this window
CreateParams createParam = base.CreateParams;
createParam.ExStyle = (createParam.ExStyle | WS_EX_LAYERED);
return createParam;
}
}
You do all your drawings on a 32bpp bitmap and finally call the UpdateLayeredBitmap
function:
private void UpdateLayeredBitmap(Bitmap bitmap)
{
// Does this bitmap contain an alpha channel?
if (bitmap.PixelFormat != System.Drawing.Imaging.PixelFormat.Format32bppArgb)
{
//"The bitmap must be 32bpp with alpha-channel."
}
// Get device contexts
IntPtr screenDc = GetDC(IntPtr.Zero);
IntPtr memDc = CreateCompatibleDC(screenDc);
IntPtr hBitmap = IntPtr.Zero;
IntPtr hOldBitmap = IntPtr.Zero;
try
{
// Get handle to the new bitmap and select it into the current device context
hBitmap = bitmap.GetHbitmap(Color.FromArgb(0));
hOldBitmap = SelectObject(memDc, hBitmap);
newLocation = new POINT(this.Location.X, this.Location.Y);
// Update the window
UpdateLayeredWindow(Handle, IntPtr.Zero, ref newLocation, ref newSize, memDc, ref sourceLocation, 0, ref blend, ULW_ALPHA);
}
catch (Exception e)
{
Console.WriteLine("{0} Exception caught.", e);
}
if (hBitmap != IntPtr.Zero)
{
SelectObject(memDc, hOldBitmap);
// Remove bitmap resources
DeleteObject(hBitmap);
}
DeleteDC(memDc);
DeleteDC(screenDc);
}
For an example I am using a 500x500
form, and the the color green
with two opacity values X = 50
and Y = 180
Point pnt;
private POINT sourceLocation = new POINT (0, 0);
private SIZE newSize = new SIZE(500, 500); //Form size
private POINT newLocation;
BLENDFUNCTION blend;
public const int ULW_ALPHA = 2;
public const byte AC_SRC_OVER = 0;
public const byte AC_SRC_ALPHA = 1;
public const int WS_EX_LAYERED = 524288;
public const int WM_NCLBUTTONDOWN = 0xA1;
public const int HTCAPTION = 0x2;
public Form1()
{
InitializeComponent();
this.Location = new Point(200, 200);
bmp = new Bitmap(500, 500, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
//the form region
Region greenRegionOpacityX = new Region(new Rectangle (0, 0, 500, 500));
Graphics g;
g = Graphics.FromImage(bmp);
g.Clear(Color.Transparent);
g.FillRegion(new SolidBrush(Color.FromArgb(50, 0, 255, 0)), greenRegionOpacityX);
g.Dispose();
greenRegionOpacityX.Dispose();
blend = new BLENDFUNCTION();
// Only works with a 32bpp bitmap
blend.BlendOp = AC_SRC_OVER;
// Always 0
blend.BlendFlags = 0;
// Set to 255 for per-pixel alpha values
blend.SourceConstantAlpha = 255;
// Only works when the bitmap contains an alpha channel
blend.AlphaFormat = AC_SRC_ALPHA;
UpdateLayeredBitmap(bmp);
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
pnt = new Point(e.X, e.Y);
}
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
long start;
long stop;
long frequency;
double elapsedTime;
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
QueryPerformanceFrequency(out frequency);
QueryPerformanceCounter(out start);
//the form region
Region greenRegionOpacityX = new Region(new Rectangle(0, 0, 500, 500));
//we exclude the rectangle that we draw with the mouse
greenRegionOpacityX.Exclude(new Rectangle(Math.Min(pnt.X, e.X), Math.Min(pnt.Y, e.Y),
Math.Abs(pnt.X - e.X), Math.Abs(pnt.Y - e.Y)));
//the rectangle that we draw with the mouse
Region greenRegionOpacityY = new Region(new Rectangle(Math.Min(pnt.X, e.X), Math.Min(pnt.Y, e.Y),
Math.Abs(pnt.X - e.X), Math.Abs(pnt.Y - e.Y)));
Graphics g;
g = Graphics.FromImage(bmp);
g.Clear(Color.Transparent); //we always clear the bitmap
//draw the two regions
g.FillRegion(new SolidBrush(Color.FromArgb(50, 0, 255, 0)), greenRegionOpacityX);
g.FillRegion(new SolidBrush(Color.FromArgb(180, 0, 255, 0)), greenRegionOpacityY);
g.Dispose();
greenRegionOpacityX.Dispose();
greenRegionOpacityY.Dispose();
//upadate the layered window
UpdateLayeredBitmap(bmp);
QueryPerformanceCounter(out stop);
elapsedTime = ((double)(stop - start) * 1000000.0d) / (double)frequency;
Console.WriteLine("{0} μsec", elapsedTime);
//In my pc it is ~12msec
}
}
And these API:
[DllImport("user32.dll")]
static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("gdi32.dll", EntryPoint = "CreateCompatibleDC", SetLastError=true)]
static extern IntPtr CreateCompatibleDC([In] IntPtr hdc);
[DllImport("gdi32.dll", EntryPoint = "SelectObject")]
static extern IntPtr SelectObject([In] IntPtr hdc, [In] IntPtr hgdiobj);
[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst,
ref POINT pptDst, ref SIZE psize, IntPtr hdcSrc, ref POINT pptSrc, uint crKey,
[In] ref BLENDFUNCTION pblend, uint dwFlags);
[DllImport("gdi32.dll", EntryPoint = "DeleteDC")]
static extern bool DeleteDC([In] IntPtr hdc);
[DllImport("gdi32.dll", EntryPoint = "DeleteObject")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool DeleteObject([In] IntPtr hObject);
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
public POINT(int x, int y)
{
this.X = x;
this.Y = y;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct SIZE
{
public int cx;
public int cy;
public SIZE(int cx, int cy)
{
this.cx = cx;
this.cy = cy;
}
}
public struct BLENDFUNCTION
{
public byte BlendOp;
public byte BlendFlags;
public byte SourceConstantAlpha;
public byte AlphaFormat;
}
EDIT (Fast with GDI)
I used QueryPerformanceCounter
for the measurements:
[DllImport("KERNEL32")]
private static extern bool QueryPerformanceCounter(out long lpPerformanceCount);
[DllImport("Kernel32.dll")]
private static extern bool QueryPerformanceFrequency(out long lpFrequency);
long start;
long stop;
long frequency;
double elapsedTime;
QueryPerformanceFrequency(out frequency);
QueryPerformanceCounter(out start);
//code for time measurement
QueryPerformanceCounter(out stop);
elapsedTime = ((double)(stop - start) * 1000000.0d) / (double)frequency;
Console.WriteLine("{0} μsec", elapsedTime); //in micro seconds!
The GDI+
approach ~12 msec
(see the updated Form1_MouseMove
).
GDI:
[System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")]
private static extern int BitBlt(
IntPtr hdcDest, // handle to destination DC (device context)
int nXDest, // x-coord of destination upper-left corner
int nYDest, // y-coord of destination upper-left corner
int nWidth, // width of destination rectangle
int nHeight, // height of destination rectangle
IntPtr hdcSrc, // handle to source DC
int nXSrc, // x-coordinate of source upper-left corner
int nYSrc, // y-coordinate of source upper-left corner
int dwRop // raster operation code
);
public const int SRCCOPY = 0x00CC0020;
IntPtr hdcMemTransparent = IntPtr.Zero, hdcMemGreenOpacityX = IntPtr.Zero,
hdcMemGreenOpacityY = IntPtr.Zero, hdcMemForm = IntPtr.Zero;
IntPtr hBitmapTransparent = IntPtr.Zero, hBitmapGreenOpacityX = IntPtr.Zero,
hBitmapGreenOpacityY = IntPtr.Zero, hBitmapForm = IntPtr.Zero;
IntPtr hBitmapTransparentOld = IntPtr.Zero, hBitmapGreenOpacityXOld = IntPtr.Zero,
hBitmapGreenOpacityYOld = IntPtr.Zero, hBitmapFormOld = IntPtr.Zero;
Bitmap bitmapTransparent, bitmapGreenOpacityX, bitmapGreenOpacityY, bitmapForm;
public Form1()
{
InitializeComponent();
this.Location = new Point(200, 200);
bitmapForm = new Bitmap(500, 500, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
bitmapTransparent = new Bitmap(500, 500, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
bitmapGreenOpacityX = new Bitmap(500, 500, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
bitmapGreenOpacityY = new Bitmap(500, 500, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Graphics g;
g = Graphics.FromImage(bitmapTransparent);
g.Clear(Color.Transparent);
g.Dispose();
g = Graphics.FromImage(bitmapGreenOpacityX);
g.Clear(Color.FromArgb(50, 0, 255, 0));
g.Dispose();
g = Graphics.FromImage(bitmapGreenOpacityY);
g.Clear(Color.FromArgb(180, 0, 255, 0));
g.Dispose();
//Create hdc's
IntPtr screenDc = GetDC(IntPtr.Zero);
hdcMemForm = CreateCompatibleDC(screenDc);
hdcMemTransparent = CreateCompatibleDC(screenDc);
hdcMemGreenOpacityX = CreateCompatibleDC(screenDc);
hdcMemGreenOpacityY = CreateCompatibleDC(screenDc);
hBitmapForm = bitmapForm.GetHbitmap(Color.FromArgb(0));
hBitmapFormOld = SelectObject(hdcMemForm, hBitmapForm);
hBitmapTransparent = bitmapTransparent.GetHbitmap(Color.FromArgb(0));
hBitmapTransparentOld = SelectObject(hdcMemTransparent, hBitmapTransparent);
hBitmapGreenOpacityX = bitmapGreenOpacityX.GetHbitmap(Color.FromArgb(0));
hBitmapGreenOpacityXOld = SelectObject(hdcMemGreenOpacityX, hBitmapGreenOpacityX);
hBitmapGreenOpacityY = bitmapGreenOpacityY.GetHbitmap(Color.FromArgb(0));
hBitmapGreenOpacityYOld = SelectObject(hdcMemGreenOpacityY, hBitmapGreenOpacityY);
DeleteDC(screenDc);
//copy hdcMemGreenOpacityX to hdcMemForm
BitBlt(hdcMemForm, 0, 0, 500, 500, hdcMemGreenOpacityX, 0, 0, SRCCOPY);
blend = new BLENDFUNCTION();
// Only works with a 32bpp bitmap
blend.BlendOp = AC_SRC_OVER;
// Always 0
blend.BlendFlags = 0;
// Set to 255 for per-pixel alpha values
blend.SourceConstantAlpha = 255;
// Only works when the bitmap contains an alpha channel
blend.AlphaFormat = AC_SRC_ALPHA;
newLocation = new POINT(this.Location.X, this.Location.Y);
//Update the window
UpdateLayeredWindow(Handle, IntPtr.Zero, ref newLocation, ref newSize, hdcMemForm, ref sourceLocation,
0, ref blend, ULW_ALPHA);
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
long start;
long stop;
long frequency;
double elapsedTime;
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
QueryPerformanceFrequency(out frequency);
QueryPerformanceCounter(out start);
//copy hdcMemGreenOpacityX to hdcMemForm
BitBlt(hdcMemForm, 0, 0, 500, 500, hdcMemGreenOpacityX, 0, 0, SRCCOPY);
//clear the rectangle that we draw with mouse with transparent color
BitBlt(hdcMemForm, Math.Min(pnt.X, e.X), Math.Min(pnt.Y, e.Y),
Math.Abs(pnt.X - e.X), Math.Abs(pnt.Y - e.Y), hdcMemTransparent, 0, 0, SRCCOPY);
//copy hdcMemGreenOpacityY to hdcMemForm
BitBlt(hdcMemForm, Math.Min(pnt.X, e.X), Math.Min(pnt.Y, e.Y),
Math.Abs(pnt.X - e.X), Math.Abs(pnt.Y - e.Y), hdcMemGreenOpacityY, 0, 0, SRCCOPY);
newLocation = new POINT(this.Location.X, this.Location.Y);
//Update the window
UpdateLayeredWindow(Handle, IntPtr.Zero, ref newLocation, ref newSize, hdcMemForm, ref sourceLocation,
0, ref blend, ULW_ALPHA);
QueryPerformanceCounter(out stop);
elapsedTime = ((double)(stop - start) * 1000000.0d) / (double)frequency;
Console.WriteLine("{0} μsec", elapsedTime);
//GDI ~ 1.2 msec!!
}
}
Release resources:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (hBitmapForm != IntPtr.Zero)
{
SelectObject(hdcMemForm, hBitmapFormOld);
DeleteObject(hBitmapForm);
DeleteDC(hdcMemForm);
hdcMemForm = IntPtr.Zero;
hBitmapForm = IntPtr.Zero;
}
if (hBitmapTransparent != IntPtr.Zero)
{
SelectObject(hdcMemTransparent, hBitmapTransparentOld);
DeleteObject(hBitmapTransparent);
DeleteDC(hdcMemTransparent);
hdcMemTransparent = IntPtr.Zero;
hBitmapTransparent = IntPtr.Zero;
}
if (hBitmapGreenOpacityX != IntPtr.Zero)
{
SelectObject(hdcMemGreenOpacityX, hBitmapGreenOpacityXOld);
DeleteObject(hBitmapGreenOpacityX);
DeleteDC(hdcMemGreenOpacityX);
hdcMemGreenOpacityX = IntPtr.Zero;
hBitmapGreenOpacityX = IntPtr.Zero;
}
if (hBitmapGreenOpacityY != IntPtr.Zero)
{
SelectObject(hdcMemGreenOpacityY, hBitmapGreenOpacityYOld);
DeleteObject(hBitmapGreenOpacityY);
DeleteDC(hdcMemGreenOpacityY);
hdcMemGreenOpacityY = IntPtr.Zero;
hBitmapGreenOpacityY = IntPtr.Zero;
}
if (bitmapForm != null)
{
bitmapForm.Dispose();
bitmapForm = null;
}
if (bitmapTransparent != null)
{
bitmapTransparent.Dispose();
bitmapTransparent = null;
}
if (bitmapGreenOpacityX != null)
{
bitmapGreenOpacityX.Dispose();
bitmapGreenOpacityX = null;
}
if (bitmapGreenOpacityY != null)
{
bitmapGreenOpacityY.Dispose();
bitmapGreenOpacityY = null;
}
}
Final results:
GDI+ ~ 12 msec
GDI ~ 1.2 msec ten times faster!