vb.net - radius - Rectángulo redondeado no exacto
c# button border radius (4)
Cada código de muestra que he encontrado para dibujar rectángulos redondeados usando GDI + es algo como esto (levantado y ligeramente modificado de BobPowell.net):
Private Sub Panel1_Paint(ByVal sender As Object, ByVal e As PaintEventArgs) Handles Panel1.Paint
e.Graphics.Clear(SystemColors.Window)
e.Graphics.SmoothingMode = SmoothingMode.None
Call DrawRoundRect(e.Graphics, Pens.Red, 10, 10, 48, 24, 6)
End Sub
Public Sub DrawRoundRect(ByVal g As Graphics, ByVal p As Pen, ByVal x As Single, ByVal y As Single, ByVal width As Single, ByVal height As Single, ByVal radius As Single)
Using gp As New GraphicsPath()
gp.StartFigure()
gp.AddArc(x + width - radius, y, radius * 2, radius * 2, 270, 90)
gp.AddArc(x + width - radius, y + height - radius, radius * 2, radius * 2, 0, 90)
gp.AddArc(x, y + height - radius, radius * 2, radius * 2, 90, 90)
gp.AddArc(x, y, radius * 2, radius * 2, 180, 90)
gp.CloseFigure()
g.DrawPath(p, gp)
End Using
End Sub
Esto produce un rectángulo redondeado donde solo la esquina superior izquierda es precisa.
AntiAliasing tiene que estar desactivado porque está pasando por una conexión de escritorio remoto, y no puedo depender de que esté disponible. Además, estoy buscando un rectángulo redondeado y nítido.
Intenté cambiar el tamaño de las otras esquinas y cambiar las alineaciones de las plumas, pero nada parece producir un rectángulo redondeado simple y preciso.
¿Hay alguna manera de dibujar un rectángulo redondeado mejor que este en las buenas formas antiguas?
Debido a que nadie te ha respondido todavía, aquí hay un truco que he usado en el pasado. Funciona razonablemente bien, y definitivamente se ve mejor que la implementación clásica con AddArc ().
Utiliza círculos y recorte para lograr el resultado que desea. Puede mostrar artefactos leves cuando se utilizan plumas con un ancho superior a 1px, pero aparte de eso, funciona bien.
Espero que sea lo suficientemente bueno para su proyecto.
private void DrawRoundedRectangle(Graphics g, Pen pen, Rectangle rect, int radius)
{
g.DrawLine(pen, rect.Left + radius, rect.Top, rect.Right - radius, rect.Top);
g.DrawLine(pen, rect.Right, rect.Top+radius, rect.Right, rect.Bottom - radius);
g.DrawLine(pen, rect.Left + radius, rect.Bottom, rect.Right - radius, rect.Bottom);
g.DrawLine(pen, rect.Left, rect.Top + radius, rect.Left, rect.Bottom - radius);
g.SetClip(new Rectangle(rect.Left, rect.Top, radius, radius));
g.DrawEllipse(pen, rect.Left, rect.Top, radius * 2, radius * 2);
g.ResetClip();
g.SetClip(new Rectangle(rect.Right-radius, rect.Top, radius+1, radius+1));
g.DrawEllipse(pen, rect.Right - radius * 2, rect.Top, radius * 2, radius * 2);
g.ResetClip();
g.SetClip(new Rectangle(rect.Right - radius, rect.Bottom-radius, radius+1, radius+1));
g.DrawEllipse(pen, rect.Right - radius * 2, rect.Bottom - (radius * 2), radius * 2, radius * 2);
g.ResetClip();
g.SetClip(new Rectangle(rect.Left, rect.Bottom - radius, radius+1, radius+1));
g.DrawEllipse(pen, rect.Left, rect.Bottom - (radius * 2), radius * 2, radius * 2);
g.ResetClip();
}
La interfaz del método es sencilla, pero publique un comentario si necesita ayuda.
Editar: Otra cosa que debería funcionar es dibujar el mismo arco cuatro veces, pero voltear usando TranslateTransform y TranslateScale. Eso debería significar que el arco parece idéntico en cada esquina.
private void DrawRoundedRectangle(Graphics g, Pen pen, Rectangle rect, int radius)
{
g.DrawLine(pen, rect.Left + radius, rect.Top, rect.Right - radius, rect.Top);
g.DrawLine(pen, rect.Right-1, rect.Top+radius, rect.Right-1, rect.Bottom - radius);
g.DrawLine(pen, rect.Left + radius, rect.Bottom-1, rect.Right - radius, rect.Bottom-1);
g.DrawLine(pen, rect.Left, rect.Top + radius, rect.Left, rect.Bottom - radius);
g.TranslateTransform(rect.Left, rect.Top);
g.DrawArc(pen, 0, 0, radius * 2, radius * 2, 180, 90);
g.ResetTransform();
g.TranslateTransform(rect.Right, rect.Top);
g.ScaleTransform(-1, 1);
g.DrawArc(pen, 1, 0, radius * 2, radius * 2, 180, 90);
g.ResetTransform();
g.TranslateTransform(rect.Right, rect.Bottom);
g.ScaleTransform(-1, -1);
g.DrawArc(pen, 1, 1, radius * 2, radius * 2, 180, 90);
g.ResetTransform();
g.TranslateTransform(rect.Left, rect.Bottom);
g.ScaleTransform(1, -1);
g.DrawArc(pen, 0, 1, radius * 2, radius * 2, 180, 90);
g.ResetTransform();
}
Esto es similar al antiguo método de gráficos por computadora de dibujar un círculo, donde se dibuja un cuarto de círculo cuatro veces para evitar errores de redondeo como el de GDI.
Otra alternativa es dibujar el primer arco en una imagen y luego dibujar la imagen cuatro veces, volteando según sea necesario. A continuación se muestra una variación del segundo método, usando una imagen para dibujar los arcos.
private void DrawRoundedRectangle(Graphics g, Pen pen, Rectangle rect, int radius)
{
g.DrawLine(pen, rect.Left + radius, rect.Top, rect.Right - radius, rect.Top);
g.DrawLine(pen, rect.Right - 1, rect.Top + radius, rect.Right - 1, rect.Bottom - radius);
g.DrawLine(pen, rect.Left + radius, rect.Bottom - 1, rect.Right - radius, rect.Bottom - 1);
g.DrawLine(pen, rect.Left, rect.Top + radius, rect.Left, rect.Bottom - radius);
Bitmap arc = new Bitmap(radius, radius, g);
Graphics.FromImage(arc).DrawArc(pen, 0, 0, radius * 2, radius * 2, 180, 90);
g.TranslateTransform(rect.Left, rect.Top);
g.DrawImage(arc, 0, 0);
g.ResetTransform();
g.TranslateTransform(rect.Right, rect.Top);
g.ScaleTransform(-1, 1);
g.DrawImage(arc, 0, 0);
g.ResetTransform();
g.TranslateTransform(rect.Right, rect.Bottom);
g.ScaleTransform(-1, -1);
g.DrawImage(arc, 0, 0);
g.ResetTransform();
g.TranslateTransform(rect.Left, rect.Bottom);
g.ScaleTransform(1, -1);
g.DrawImage(arc, 0, 0);
g.ResetTransform();
arc.Dispose();
}
En ocasiones, he usado un enfoque de "baja tecnología" para lidiar con los errores de redondeo en GDI +
1) Cambie el tamaño de su imagen de origen a un múltiplo binario de su tamaño original. Normalmente, volveré a muestrear a un ancho y alto 4 veces mayor (o 8, o 16) que el original.
2) Realice todas mis operaciones de dibujo de GDI + (teniendo en cuenta, por supuesto, que mis coordenadas deberán multiplicarse por un factor de 4). No hay necesidad de usar ningún anti-aliasing elegante.
3) Vuelva a muestrear la imagen de nuevo a las dimensiones originales. La reducción de la imagen produce un agradable efecto de suavizado y minimiza los errores de redondeo en líneas, curvas, etc.
He encontrado la mejor solución para ser simplemente API de Windows de la vieja escuela:
Private Sub DrawRoundRect(ByVal g As Graphics, ByVal r As Rectangle)
Dim hDC As IntPtr = g.GetHdc
Dim hPen As IntPtr = CreatePen(PS_SOLID, 0, ColorTranslator.ToWin32(Color.Red))
Dim hOldPen As IntPtr = SelectObject(hDC, hPen)
SelectObject(hDC, GetStockObject(NULL_BRUSH))
RoundRect(hDC, r.Left, r.Top, r.Right - 1, r.Bottom - 1, 12, 12)
SelectObject(hDC, hOldPen)
DeleteObject(hPen)
g.ReleaseHdc(hDC)
End Sub
Esto produce el rectángulo redondeado simétrico que he estado buscando:
1) Cambie el tamaño de su imagen de origen a un múltiplo binario de su tamaño original. Normalmente, volveré a muestrear a un ancho y alto 4 veces mayor (o 8, o 16) que el original.
2) Realice todas mis operaciones de dibujo de GDI + (teniendo en cuenta, por supuesto, que mis coordenadas deberán multiplicarse por un factor de 4). No hay necesidad de usar ningún anti-aliasing elegante.
3) Vuelva a muestrear la imagen de nuevo a las dimensiones originales. La reducción de la imagen produce un agradable efecto de suavizado y minimiza los errores de redondeo en líneas, curvas, etc.
private Bitmap GenerateButton(int overSampling) {
int overSampling = 8;
int width=(48 + 10 + 10 + 6) * overSampling;
int height=(24 + 10 + 10 + 6) * overSampling;
// Draw the button with the rounded corners, but do
// so at 8 times the normal size.
Bitmap bitmap=new Bitmap(width,height);
using (Graphics g = Graphics.FromImage(bitmap)) {
g.Clear(Color.White);
g.SmoothingMode = SmoothingMode.None;
DrawRoundRect(overSampling, g, new Pen(Color.Red, overSampling), 10, 10, 48, 24, 6);
}
// Shrink the image down to its intended size
Bitmap shrunkVersion=new Bitmap(bitmap.Width / overSampling, bitmap.Height / overSampling);
using (Graphics g = Graphics.FromImage(shrunkVersion)) {
// Use hi-quality resampling for a nice, smooth image.
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(bitmap, 0, 0, shrunkVersion.Width, shrunkVersion.Height);
}
return shrunkVersion;
}
private void DrawRoundRect(int overSampling, Graphics g, Pen p, float x, float y, float width, float height, float radius)
{
using (GraphicsPath gp = new GraphicsPath())
{
gp.StartFigure();
gp.AddArc((x + width - radius) * overSampling, y * overSampling, (radius * 2) * overSampling, (radius * 2) * overSampling, 270, 90);
gp.AddArc((x + width - radius) * overSampling, (y + height - radius) * overSampling, (radius * 2) * overSampling, (radius * 2) * overSampling, 0, 90);
gp.AddArc(x * overSampling, (y + height - radius) * overSampling, radius * 2 * overSampling, radius * 2 * overSampling, 90, 90);
gp.AddArc(x * overSampling, y * overSampling, radius * 2 * overSampling, radius * 2 * overSampling, 180, 90);
gp.CloseFigure();
g.DrawPath(p, gp);
}
}
Sin sobremuestreo:
Con sobremuestreo 8 veces: