使用GDI +绘制各种颜色:
brush = new SolidBrush(color);
graphics.FillRectangle(brush, x, y, width, height);
您会注意到玻璃上没有显示不透明的颜色:
我如何在玻璃上画出纯色?
您还会注意到,完全不透明的颜色会根据颜色的不同而处理不同:
有人能指出我在桌面合成器上的文档,它解释了如何处理不同的颜色吗?
您还会注意到FillRectangle
的行为与FillEllipse
不同:
FillEllipse
使用不透明的颜色绘制不透明的颜色FillRectangle
部分(或完全)透明
请解释非感性行为。
Alwayslearning建议我更改合成模式。来自MSDN:
CompositingMode枚举
CompositingMode 枚举指定渲染颜色与背景颜色的组合方式。此枚举由 Graphics 类的
Graphics::GetCompositingMode
和'Graphics::SetCompositingMode'方法使用。CompositingModeSourceOver
指定在渲染颜色时,它与背景颜色混合。混合由要渲染的颜色的alpha分量决定。
CompositingModeSourceCopy
指定在渲染颜色时,它会覆盖背景颜色。此模式不能与TextRenderingHintClearTypeGridFit一起使用。
从CompositingModeSourceCopy
的描述中,听起来好像不是我想要的选项。从它所施加的限制来看,它听起来像我想要的选项。并且通过禁用合成或透明度,不是我想要的选项,因为它执行 SourceCopy ,而不是 SourceBlend :
幸运的是,这不是一个我必须考虑的邪恶,因为它无法解决我的实际问题。构建我的graphics
对象后,我尝试更改了合成模式:
graphics = new Graphics(hDC);
graphics.SetCompositingMode(CompositingModeSourceCopy); //CompositingModeSourceCopy = 1
结果对输出没有影响:
答案 0 :(得分:1)
似乎对我有用。由于缺少完整的代码示例,我假设您的合成模式错误。
public void RenderGdiPlus()
{
List<string> colors = new List<string>(new string[] { "000000", "ff0000", "00ff00", "0000ff", "ffffff" });
List<string> alphas = new List<string>(new string[] { "00", "01", "40", "80", "c0", "fe", "ff" });
Bitmap bmp = new Bitmap(200, 300, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Graphics graphics = Graphics.FromImage(bmp);
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.None;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
SolidBrush backBrush = new SolidBrush(Color.FromArgb(254, 131, 208, 129));
graphics.FillRectangle(backBrush, 0, 0, 300, 300);
graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
Pen pen = new Pen(Color.Gray);
for (int row = 0; row < alphas.Count; row++)
{
string alpha = alphas[row];
for (int column=0; column<colors.Count; column++)
{
string color = "#" + alpha + colors[column];
SolidBrush brush = new SolidBrush(ColorTranslator.FromHtml(color));
graphics.DrawRectangle(pen, 40*column, 40*row, 32, 32);
graphics.FillRectangle(brush, 1+40*column, 1+40*row, 31, 31);
}
}
Graphics gr2 = Graphics.FromHwnd(this.Handle);
gr2.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
gr2.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
gr2.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.None;
gr2.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
gr2.DrawImage(bmp, 0, 0);
}
答案 1 :(得分:1)
我有一个类似的issue,但它涉及到layered window,而不是在Aero的玻璃上。我没有任何代码可以测试这是否可以解决您的问题,但我认为它值得一试,因为您的问题的症状与我的相同。
正如您所注意到的,FillRectangle
似乎有一些qwerks,其行为与FillEllipse
之间的差异显而易见。
以下是我提出的两个解决方案,每个都解决我的问题:
致电 FillRectangle
两次
SolidBrush b(Color(254, 255, 0, 0));
gfx.FillRectangle(&b, Rect(0, 0, width, height));
gfx.FillRectangle(&b, Rect(0, 0, width, height));
由于相同的区域被填充两次,因此无论玻璃颜色如何,它们都应混合并创建RGB(255,0,0),这会导致100%不透明的形状。我不喜欢这种方法,因为它需要绘制两个矩形。
使用 FillPolygon
代替
与FillEllipse
一样,FillPolygon
似乎没有颜色/不透明度问题,除非您这样称呼:
SolidBrush b(Color(255, 255, 0, 0));
Point points[4];
points[0] = Point(0, 0);
points[1] = Point(width, 0);
points[2] = Point(width, height);
points[4] = Point(0, height);
gfx.FillPolygon(&b, points, 4); //don't copy and paste - this won't work
对我来说,上面的代码形成了100%透明的形状。我猜这是由于某种形式的优化将调用传递给FillRectangle
。或者 - 很可能 - FillPolygon
存在一些问题,由FillRectangle
调用。无论如何,如果你向数组添加额外的Point
,你可以绕过它:
SolidBrush b(Color(255, 255, 0, 0));
Point points[5];
points[0] = Point(0, 0);
points[1] = Point(0, 0); //<-
points[2] = Point(width, 0);
points[3] = Point(width, height);
points[4] = Point(0, height);
gfx.FillPolygon(&b, points, 5);
上面的代码确实为我绘制了100%不透明的形状。我希望这也能解决你的问题。
答案 2 :(得分:1)
另一天,我的另一个解决方案。
但是(与任何其他不使用DrawThemeTextEx的解决方案一样): 文本呈现无法正常工作,因为它总是将表单的背面颜色作为antialias / cleartype提示。请改用DrawThemeTextEx,它还支持后面带有发光效果的文本。
答案 3 :(得分:0)
我遇到了与GDI相同的问题 GDI使用零alpha通道值,因此最简单的解决方案是修复alpha通道,就像这段代码一样:
void fix_alpha_channel()
{
std::vector<COLORREF> pixels(cx * cy);
BITMAPINFOHEADER bmpInfo = {0};
bmpInfo.biSize = sizeof(bmpInfo);
bmpInfo.biWidth = cx;
bmpInfo.biHeight = -int(cy);
bmpInfo.biPlanes = 1;
bmpInfo.biBitCount = 32;
bmpInfo.biCompression = BI_RGB;
GetDIBits(memDc, hBmp, 0, cy, &pixels[0], (LPBITMAPINFO)&bmpInfo, DIB_RGB_COLORS);
std::for_each(pixels.begin(), pixels.end(), [](COLORREF& pixel){
if(pixel != 0) // black pixels stay transparent
pixel |= 0xFF000000; // set alpha channel to 100%
});
SetDIBits(memDc, hBmp, 0, cy, &pixels[0], (LPBITMAPINFO)&bmpInfo, DIB_RGB_COLORS);
}
答案 4 :(得分:0)
我找到了另一种解决方法。使用两种颜色相同的LinearGradientBrush
:
LinearGradientBrush brush(Point(0,0), Point(0,0), Color(255,231,45,56), Color(255,231,45,56));
g.FillRectangle(&brush, 25, 25, 30, 30);
这可能比SolidBrush慢,但效果很好。
答案 5 :(得分:0)
你想要一个愚蠢的解决方案吗?在这里你得到一个愚蠢的解决方案。至少它只是一行代码。并造成一个小但可忽略的副作用。
<强>假设强>
当绘制实心的直角矩形时,GDI +倾向于通过以比绘制其他东西更快的方法绘制它们来加快速度。这种技术称为bitbliting。这实际上非常聪明,因为它是在表面上绘制矩形的最快方法。但是,要绘制的矩形必须符合它们成直角的规则。
这个聪明的优化是在DWM,Aero,Glass和所有新奇事物之前完成的。
在内部,bitblitting只是将像素的RGBA颜色数据从一个存储区域复制到另一个存储区域(从窗口上的绘图中可以看出)。可悲的是,它所写的RGB格式与玻璃区域不兼容,导致您观察到的奇怪的透明效果。
<强>解决方案强>
所以这里有一个转折点。 GDI +可以尊重变换矩阵,每个绘图都可以缩放,倾斜,旋转等等。如果我们应用这样的矩阵,则不再保证矩形成直角的规则。因此,GDI +将停止对这些进行bitblitting并以类似于省略号的方式绘制它们。
但我们也不想歪曲,缩放或旋转绘图。我们只是应用尽可能小的变换:我们创建一个变换矩阵,将每个绘图向下移动一个像素:
// If you don't get that matrix instance, ignore it, it's just boring math
e.Graphics.Transform = new Matrix(1f, 0.001f, 0f, 1f, 0f, 0f);
现在,bitblitting关闭,矩形是实心的,紫罗兰是蓝色的。如果只有一种更简单的方法来控制它,特别是没有移动图纸的那种!
如上所述,如果要在第一个像素行上绘制,请使用-1作为Y坐标。
您可以决定这是否真的是一个解决方案,或者只是忽略它。