ventana - Aero: ¿Cómo dibujar colores sólidos(opacos) sobre el vidrio?
dibujos de vidrio para colorear (6)
Usando GDI + para dibujar varios colores:
brush = new SolidBrush(color);
graphics.FillRectangle(brush, x, y, width, height);
Notará que no se muestra ningún color opaco correctamente en el cristal:
¿Cómo dibujo colores sólidos en vidrio?
También notará que un color completamente opaco se maneja de manera diferente según el color:
- negro opaco: completamente transparente
- color opaco: parcialmente transparente
- blanco opaco: completamente opaco
¿Alguien puede indicarme la documentación del compositor de escritorio que explica cómo se manejan los diferentes colores?
Actualización 3
También notará que FillRectangle
comporta de forma diferente que FillEllipse
:
-
FillEllipse
con un color opaco dibuja un color opaco -
FillRectangle
con un color opaco dibuja parcialmente (o completamente) transparente
Explicación del comportamiento no sensorial, por favor.
Actualización 4
Alwayslearning sugirió que cambie el modo de composición. Desde MSDN :
CompositingMode Enumeration
La enumeración CompositingMode especifica cómo se combinan los colores renderizados con los colores de fondo. Esta enumeración es utilizada por los
Graphics::GetCompositingMode
y ''Graphics::SetCompositingMode'' de la clase Graphics .
CompositingModeSourceOver
Especifica que cuando se representa un color, se mezcla con el color de fondo. La combinación está determinada por el componente alfa del color que se está renderizando.
CompositingModeSourceCopy
Especifica que cuando se representa un color, sobrescribe el color de fondo. Este modo no se puede usar junto con TextRenderingHintClearTypeGridFit.
De la descripción de CompositingModeSourceCopy
, parece que no es la opción que quiero. De las limitaciones que impone, suena como la opción que quiero. Y con la composición, o la transparencia deshabilitada, no es la opción que quiero, ya que realiza una SourceCopy , en lugar de SourceBlend :
Afortunadamente no es un mal que tengo que contemplar porque no resuelve mi problema real. Después de construir mi objeto graphics
, intenté cambiar el modo de composición:
graphics = new Graphics(hDC);
graphics.SetCompositingMode(CompositingModeSourceCopy); //CompositingModeSourceCopy = 1
El resultado no tiene efecto en el resultado:
Notas
- Win32 nativo
- no .NET ( es decir, nativo)
- no Winforms ( es decir, nativo)
- GDI + ( es decir, nativo)
Ver también
He encontrado otra forma de evitarlo. Use LinearGradientBrush
con ambos colores de la misma manera:
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);
Esto es quizás más lento que SolidBrush, pero funciona bien.
Me encontré con el mismo problema con GDI.
GDI usa un valor cero de canal alfa, por lo que la solución más simple es arreglar el canal alfa como lo hace este código:
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);
}
Otro día, otra solución de mi parte.
- Dibuja todo lo que quieras que aparezca sobre el vidrio en un mapa de bits.
- Luego, borre el fondo del formulario con color negro.
- Inmediatamente después de esto, dibuje el mapa de bits en su formulario.
Sin embargo (como con cualquier otra solución que no use DrawThemeTextEx): la representación de texto no funcionará correctamente, porque siempre toma el color de fondo de su formulario como una sugerencia de antialias / cleartype. Use DrawThemeTextEx en su lugar, que también admite texto con un efecto de brillo detrás.
Parece que funciona bien para mí. Con la falta de un ejemplo de código completo, supongo que tienes mal el modo de composición.
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);
}
Tuve un issue similar, pero se trataba de dibujar en una ventana en capas , en lugar de en el cristal de Aero. No tengo ningún código con el que pueda probar si esto resuelve tu problema, pero pensé que vale la pena intentarlo, ya que los síntomas de tu problema son los mismos que los míos.
Como habrás notado, parece que hay algunos qwerks con FillRectangle
, aparentes por las diferencias entre su comportamiento y los de FillEllipse
.
Aquí hay dos soluciones que se me ocurrieron, cada una resuelve mi problema:
Llamar a
FillRectangle
dos vecesSolidBrush b(Color(254, 255, 0, 0)); gfx.FillRectangle(&b, Rect(0, 0, width, height)); gfx.FillRectangle(&b, Rect(0, 0, width, height));
Dado que la misma área se está llenando dos veces, deben mezclar y crear RGB (255, 0, 0) independientemente del color del vidrio, lo que conduce a un resultado de una forma 100% opaca. No prefiero este método, ya que requiere que cada rectángulo se dibuje dos veces.
Use
FillPolygon
enFillPolygon
lugarAl igual que con
FillEllipse
,FillPolygon
no parece tener el problema de color / opacidad, a menos que lo llame así :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
Para mí, el código anterior resultó en una forma 100% transparente. Supongo que esto se debe a alguna forma de optimización que pasa la llamada a
FillRectangle
. O, lo más probable, hay algún problema conFillPolygon
, queFillRectangle
llama. De todos modos, si agrega unPoint
extra a la matriz, puede evitarlo: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);
El código anterior dibuja una forma 100% opaca para mí. Espero que esto también resuelva tu problema.
¿Quieres una solución estúpida? Aquí obtienes una solución estúpida. Al menos es solo una línea de código. Y causando un efecto secundario pequeño pero ignorable.
Suposición
Al dibujar rectángulos sólidos en ángulo recto, GDI + tiende a acelerar las cosas dibujándolas en un método más rápido que dibujar otras cosas. Esta técnica se llama bitbliting. Eso es bastante inteligente, ya que es la forma más rápida de dibujar rectángulos en una superficie. Sin embargo, los rectángulos a dibujar deben cumplir la regla de que tienen un ángulo recto.
Esta inteligente optimización se realizó antes de que existieran DWM, Aero, Glass y todas las novedades.
Internamente, bitblitting simplemente copia los datos de color RGBA de píxeles de un área de memoria a otra (por ejemplo, desde su dibujo en su ventana). Lamentablemente, el formato RGB que escribe es incompatible con las áreas de vidrio, lo que produce los extraños efectos de transparencia que observaste.
Solución
Así que aquí viene un giro. GDI + puede respetar una matriz de transformación, con la que cada dibujo se puede escalar, sesgar, rotar o lo que sea. Si aplicamos dicha matriz, ya no se garantiza la regla de que los rectángulos tienen un ángulo recto. Por lo tanto, GDI + dejará de bitblitting estos y dibujarlos de una manera similar a las elipsis.
Pero tampoco queremos sesgar, escalar o rotar nuestro dibujo. Simplemente aplicamos la transformación más pequeña posible: creamos una matriz de transformación que mueve cada dibujo hacia abajo un píxel:
// 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);
Ahora, bitblitting está desactivado, los rectángulos son sólidos, los violetas son azules. ¡Si hubiera una forma más sencilla de controlar eso, especialmente uno que no moviera los dibujos!
Dicho esto, si desea dibujar en la primera fila de píxeles, use -1 como una coordenada Y.
Puedes decidir si esto realmente es una solución para ti o simplemente ignorarlo.