Aero: Как нарисовать сплошные (непрозрачные) цвета на стекле? - PullRequest
32 голосов
/ 23 ноября 2010

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

CompositingModeSourceOver

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

CompositingModeSourceCopy

Указывает, что при отображении цвета он перезаписывает цвет фона. Этот режим нельзя использовать вместе с TextRenderingHintClearTypeGridFit.

Из описания CompositingModeSourceCopy звучит так, будто это не тот вариант, который мне нужен. Из ограничений, которые он накладывает, это звучит как вариант, который я хочу. А с отключенной композицией или прозрачностью не является желаемым вариантом, поскольку он выполняет SourceCopy , а не SourceBlend :

alt text

К счастью, это не зло, которое я должен созерцать, потому что это не решает мою настоящую проблему. После создания объекта graphics я попытался изменить режим компоновки:

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

Результат не влияет на вывод:

alt text

Примечания

  • Win32 native
  • не .NET ( т.е. нативная)
  • не Winforms ( т.е. native)
  • GDI + ( т.е. native)

Смотри также

Ответы [ 6 ]

1 голос
/ 28 января 2015

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

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

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

1 голос
/ 07 октября 2013

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

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

1 голос
/ 26 ноября 2010

Кажется, работает хорошо для меня. Из-за отсутствия полного примера кода, я предполагаю, что вы неправильно выбрали режим компоновки.

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);
}
0 голосов
/ 27 января 2015

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

Успение

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

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

Внутренне, битовое копирование просто копирует данные цвета 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);

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

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

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

0 голосов
/ 22 октября 2014

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

0 голосов
/ 17 мая 2011

Я встречал ту же проблему с 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);
}
...