31

使用 GDI+ 绘制各种颜色:

brush = new SolidBrush(color);
graphics.FillRectangle(brush, x, y, width, height);

您会注意到玻璃上没有正确显示不透明的颜色: 替代文字

如何在玻璃上绘制纯色?


您还会注意到,完全不透明的颜色会根据它的颜色进行不同的处理:

  • 不透明黑色:完全透明
  • 不透明颜色:部分透明
  • 不透明白色:完全不透明

替代文字

谁能指出我在桌面合成器上解释如何处理不同颜色的文档?


更新 3

您还会注意到它的FillRectangle行为不同于FillEllipse

  • FillEllipsewith an opaque color 绘制不透明颜色
  • FillRectangle使用不透明颜色绘制部分(或完全)透明

替代文字

请解释无意义的行为。

更新 4

Alwayslearning建议我更改合成模式。来自MSDN

合成模式枚举

CompositingMode枚举指定渲染颜色如何与背景颜色组合。此枚举由GraphicsGraphics::GetCompositingMode类的'Graphics::SetCompositingMode'方法使用。

CompositingModeSourceOver

指定在渲染颜色时,将其与背景颜色混合。混合由正在渲染的颜色的 alpha 分量决定。

CompositingModeSourceCopy

指定在呈现颜色时,它会覆盖背景颜色。此模式不能与 TextRenderingHintClearTypeGridFit 一起使用。

从 的描述来看CompositingModeSourceCopy,这听起来不是我想要的选项。从它施加的限制来看,这听起来像是我想要的选择。并且在禁用合成或透明度的情况下,它不是我想要的选项,因为它执行SourceCopy,而不是SourceBlend

替代文字

幸运的是,这不是我必须考虑的邪恶,因为它不能解决我的实际问题。构建我的graphics对象后,我尝试更改合成模式:

graphics = new Graphics(hDC);
graphics.SetCompositingMode(CompositingModeSourceCopy); //CompositingModeSourceCopy = 1

结果对输出没有影响:

替代文字

笔记

  • Win32 原生
  • 不是 .NET(本机)
  • 不是 Winforms(原生)
  • GDI+(原生)

也可以看看

4

6 回答 6

1

我有一个类似的问题,但它涉及到在分层窗口上绘图,而不是在 Aero 的玻璃上。我没有任何代码可以测试这是否可以解决您的问题,但我认为值得一试,因为您的问题症状与我的相同。

正如您所注意到的,似乎有一些 qwerks ,其行为与'sFillRectangle之间的差异显而易见。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% 不透明的形状。我希望这也能解决您的问题。

于 2013-10-07T03:46:14.013 回答
1

另一天,我的另一个解决方案。

  • 将您想要出现在玻璃上的所有内容绘制成位图。
  • 然后,用黑色清除表单背景。
  • 紧接着,在表单上绘制位图。

但是(与不使用 DrawThemeTextEx 的任何其他解决方案一样):文本渲染将无法正常工作,因为它总是将表单的背景颜色作为抗锯齿/cleartype 提示。请改用 DrawThemeTextEx,它还支持后面带有发光效果的文本。

于 2015-01-28T13:54:48.603 回答
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);
}
于 2010-11-26T02:37:33.970 回答
0

你想要一个愚蠢的解决方案吗?在这里你得到一个愚蠢的解决方案。至少它只是一行代码。并造成很小但可忽略的副作用。

假设

在绘制实心直角矩形时,GDI+ 倾向于通过以比绘制其他东西更快的方法绘制它们来加快速度。这种技术称为位块化。这实际上非常聪明,因为它是在表面上绘制矩形的最快方法。但是,要绘制的矩形必须满足它们是直角的规则。

这种巧妙的优化是在 DWM、Aero、Glass 和所有新奇的东西出现之前完成的。

在内部,bitblitting 只是将像素的 RGBA 颜色数据从一个内存区域复制到另一个内存区域(也就是说,从您在窗口上的绘图中说)。可悲的是,它写入的 RGB 格式与玻璃区域不兼容,导致您观察到奇怪的透明度效果。

解决方案

所以这里出现了一个转折。GDI+ 可以尊重一个转换矩阵,每个绘图都可以使用该矩阵进行缩放、倾斜、旋转或其他任何操作。如果我们应用这样的矩阵,则不再保证矩形不再是直角的规则。因此,GDI+ 将停止对这些进行位传输,并以类似于椭圆的方式绘制它们。

但我们也不想倾斜、缩放或旋转我们的绘图。我们只是应用可能的最小变换:我们创建一个变换矩阵,将每个绘图向下移动一个像素:

// 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 坐标。

您可以决定这是否真的适合您,或者直接忽略它。

于 2015-01-27T15:42:11.270 回答
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);
}
于 2011-05-17T10:42:56.110 回答
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 慢,但工作正常。

于 2014-10-22T14:11:50.393 回答