Aero:如何在玻璃上绘制实心(不透明)颜色?

时间:2010-11-23 16:26:53

标签: aero dwm aero-glass

使用GDI +绘制各种颜色:

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

您会注意到玻璃上没有显示不透明的颜色: alt text

我如何在玻璃上画出纯色?


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

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

alt text

有人能指出我在桌面合成器上的文档,它解释了如何处理不同的颜色吗?


更新3

您还会注意到FillRectangle的行为与FillEllipse不同:

  • FillEllipse使用不透明的颜色绘制不透明的颜色
  • 具有不透明颜色的
  • FillRectangle部分(或完全)透明

alt text

请解释非感性行为。

更新4

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

  

CompositingMode枚举

     

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

CompositingModeSourceOver
     

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

CompositingModeSourceCopy
     

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

CompositingModeSourceCopy的描述中,听起来好像不是我想要的选项。从它所施加的限制来看,它听起来像我想要的选项。并且通过禁用合成或透明度,不是我想要的选项,因为它执行 SourceCopy ,而不是 SourceBlend

alt text

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

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

结果对输出没有影响:

alt text

备注

  • Win32 native
  • 不是.NET( native)
  • 不是Winforms( native)
  • GDI +( ie。 native)

另见

6 个答案:

答案 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坐标。

您可以决定这是否真的是一个解决方案,或者只是忽略它。