Подтвердить что ты не робот

Aero: Как нарисовать твердые (непрозрачные) цвета на стекле?

Использование 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::GetCompositingMode и 'Graphics:: SetCompositingMode ' класса Графика.

CompositingModeSourceOver

Указывает, что при отображении цвета он смешивается с цветом фона. Смесь определяется альфа-компонентом отображаемого цвета.

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 + (т.е. native)

См. также

4b9b3361

Ответ 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);
}

Ответ 2

У меня была аналогичная проблема, но она включала рисование на многоуровневое окно, а не на Aero glass. У меня нет никакого кода, с которым я могу проверить, разрешает ли это вашу проблему, но я решил, что это стоит того, так как симптомы вашей проблемы такие же, как у меня.

Как вы заметили, кажется, что есть несколько qwerks с FillRectangle, что видно из различий между его поведением и 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% -ную непрозрачную форму. Надеюсь, это также решит вашу проблему.

Ответ 3

Еще один день, другое решение от меня.

  • Нарисуйте все, что вы хотите отобразить на стекле, в растровое изображение.
  • Затем очистите фон формы черным цветом.
  • Сразу после этого нарисуйте растровое изображение в вашей форме.

Однако (как и при любом другом решении, не использующем DrawThemeTextEx): Отрисовка текста будет работать некорректно, потому что она всегда берет задний цвет вашей формы как подсказку antialias/cleartype. Вместо этого используйте DrawThemeTextEx, который также поддерживает текст с эффектом свечения.

Ответ 4

Я встретил ту же проблему с GDI.
GDI использует нулевое значение альфа-канала, поэтому самым простым решением является исправить альфа-канал, как это делает код:

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

Я нашел другой способ. Используйте 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

Вы хотите глупое решение? Здесь вы получаете глупое решение. По крайней мере, это всего лишь одна строка кода. И вызывает небольшой, но неочевидный побочный эффект.

Предположение

При рисовании сплошных прямоугольных прямоугольников GDI + стремится ускорить процесс, используя их быстрее, чем рисовать другие вещи. Этот метод называется битбитингом. Это на самом деле довольно умно, поскольку это самый быстрый способ рисовать прямоугольники на поверхности. Однако прямоугольники, которые нужно нарисовать, должны соответствовать правилу, что они находятся под прямым углом.

Эта умная оптимизация была сделана до того, как появились DWM, Aero, Glass и все новые причудливые вещи.

Внутренне биллинг просто копирует данные цвета RGBA пикселей из одной области памяти в другую (так сказать, из вашего рисунка на вашем окне). К сожалению, формат RGB, который он пишет, несовместим со стеклянными областями, в результате чего наблюдаются странные эффекты прозрачности.

Решение

Итак, вот идет поворот. GDI + может уважать матрицу преобразования, с помощью которой каждый рисунок можно масштабировать, перекосить, поворачивать или что угодно. Если мы применим такую ​​матрицу, то правило, что прямоугольники имеют прямоугольную форму, больше не гарантируется. Таким образом, GDI + остановит битбинг и нарисует их так же, как эллипсы.

Но мы также не хотим искажать, масштабировать или поворачивать наш рисунок. Мы просто применяем наименьшее возможное преобразование: мы создаем матрицу преобразования, которая перемещает каждый чертеж на один пиксель:

// If you don't get that matrix instance, ignore it, it just boring math
e.Graphics.Transform = new Matrix(1f, 0.001f, 0f, 1f, 0f, 0f);

Теперь битбитинг выключен, прямоугольники сплошные, фиалки - синие. Если бы был более простой способ контролировать это, особенно, если вы не перемещали чертежи!

Таким образом, если вы хотите нарисовать первую строку пикселей, используйте -1 в качестве координаты Y.

Вы можете решить, действительно ли это решение для вас или просто игнорировать его.