代码之家  ›  专栏  ›  技术社区  ›  Ian Boyd

如何在玻璃上画出纯色(不透明)的颜色?

  •  32
  • Ian Boyd  · 技术社区  · 14 年前

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

    你会注意到玻璃上没有合适的不透明颜色: alt text


    您还将注意到,完全不透明的颜色根据其颜色的不同而有不同的处理方式:

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

    alt text

    有谁能给我指一下桌面合成器上的文档,解释如何处理不同的颜色?


    更新3

    FillRectangle 行为不同于 FillEllipse :

    • 填充椭圆
    • 圆角矩形 使用不透明颜色绘制部分(或完全)透明

    alt text

    更新4

    Alwayslearning 建议我更改合成模式。从 MSDN :

    合成模式枚举

    这个 枚举指定如何将渲染颜色与背景颜色组合。此枚举由 Graphics::GetCompositingMode 'Graphics::SetCompositingMode' 绘图 上课。

    CompositingModeSourceOver
    

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

    CompositingModeSourceCopy
    

    从描述 CompositingModeSourceCopy ,听起来这不是我想要的选择。从它的局限性来看,这听起来像是我想要的选择。在合成或透明性被禁用的情况下 不是 我想要的选项,因为它执行 源拷贝 ,而不是 :

    alt text

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

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

    结果对输出没有影响:

    alt text

    笔记

    • 不是.NET( 本地的)
    • 本地的)
    • GDI+( 本地的)

    6 回复  |  直到 7 年前
        1
  •  1
  •   AlwaysLearning    14 年前

    对我来说似乎没问题。由于缺乏完整的代码示例,我假设您的合成模式是错误的。

    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);
    }
    
        2
  •  1
  •   Community CDub    7 年前

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

    正如你所注意到的,似乎有一些 FillRectangle FillEllipse 的。

    下面是我想出的两个解决办法 我的

    • 呼叫 圆角矩形 两次

      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 相反

      就像 填充椭圆 , 圆角多边形 似乎没有颜色/不透明度问题, 除非你这样称呼 :

      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%透明的形状。我猜这可能是由于某种形式的优化,它将调用传递给 圆角矩形 相反。或者-很可能-有一些问题 ,由调用 圆角矩形 . 不管怎样,如果你再加一个 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);
      

        3
  •  1
  •   Ray Brian Agnew    10 年前

    又一天,又一个解决办法。

    • 将要在玻璃上显示的所有内容绘制为位图。
    • 然后,用黑色清除窗体背景。
    • 紧接着,在窗体上绘制位图。

    文本呈现将无法正常工作,因为它总是将窗体的背景色作为antialas/cleartype提示。改用DrawThemeTextEx,它还支持后面带有辉光效果的文本。

        4
  •  0
  •   Abyx    13 年前

    我和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);
    }
    
        5
  •  0
  •   Azarien    10 年前

    我找到了另一个办法。使用 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慢,但工作正常。

        6
  •  0
  •   Ray Brian Agnew    10 年前

    你想要一个愚蠢的解决方案吗?给你一个愚蠢的解决方案。至少只有一行代码。并引起一个小的但不可忽视的副作用。

    假设

    这个聪明的优化是在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坐标。

    你可以决定这是否真的是你的解决方案,或者忽略它。

    推荐文章