PPP y píxeles independientes del dispositivo
Para programar eficazmente con gráficos de Windows, debe comprender dos conceptos relacionados:
- Puntos por pulgada (PPP)
- Píxeles independientes del dispositivo (DIP).
Comencemos con PPP. Esto requerirá un corto desvío en la tipografía. En la tipografía, el tamaño del tipo se mide en unidades denominadas puntos. Un punto es igual a 1/72 de pulgada.
- 1 pt = 1/72 pulgadas
Nota:
Esta es la definición de publicación de escritorio de punto. Históricamente, la medida exacta de un punto ha variado.
Por ejemplo, una fuente de 12 puntos está diseñada para ajustarse a una línea de texto de 1/6" (12/72). Obviamente, esto no significa que todos los caracteres de la fuente sean exactamente de 1/6" de alto. De hecho, algunos caracteres pueden ser más altos que 1/6". Por ejemplo, en muchas fuentes el carácter Å es más alto que el alto nominal de la fuente. Para mostrarse correctamente, la fuente necesita espacio adicional entre el texto. Este espacio se denomina líder.
En la ilustración siguiente se muestra una fuente de 72 puntos. Las líneas sólidas muestran un cuadro de límite de 1" alto alrededor del texto. La línea discontinua se denomina línea base. La mayoría de los caracteres de una fuente descansan en la línea base. El alto de la fuente incluye la parte situada encima de la línea base ( el ascenso) y la parte inferior a la línea base (el descenso). En la fuente que se muestra aquí, el ascenso es de 56 puntos y el descenso es de 16 puntos.
Sin embargo, cuando se trata de una pantalla del equipo, medir el tamaño del texto es problemático, porque los píxeles no tienen el mismo tamaño. El tamaño de un píxel depende de dos factores: la resolución de pantalla y el tamaño físico del monitor. Por lo tanto, las pulgadas físicas no son una medida útil, ya que no hay ninguna relación fija entre pulgadas físicas y píxeles. En su lugar, las fuentes se miden en unidades lógicas . Una fuente de 72 puntos se define como una pulgada lógica de alto. Después, las pulgadas lógicas se convierten en píxeles. Durante muchos años, Windows usó la siguiente conversión: una pulgada lógica es igual a 96 píxeles. Con este factor de escalado, una fuente de 72 puntos se representa como de 96 píxeles de alto. Una fuente de 12 puntos es de 16 píxeles de altura.
- 12 puntos = 12/72 pulgada lógica = 1/6 pulgada lógica = 96/6 píxeles = 16 píxeles
Este factor de escala se describe como 96 puntos por pulgada (PPP). El término puntos deriva de la impresión, donde los puntos físicos de la tinta se colocan en papel. En el caso de las pantallas de equipo, sería más preciso decir 96 píxeles por pulgada lógica, pero el término PPP se ha bloqueado.
Dado que los tamaños de píxel reales varían, el texto legible en un monitor puede ser demasiado pequeño en otro monitor. Además, las personas tienen diferentes preferencias: algunas prefieren texto más grande. Por este motivo, Windows permite al usuario cambiar la configuración de PPP. Por ejemplo, si el usuario establece la pantalla en 144 PPP, una fuente de 72 puntos tiene un alto de 144 píxeles. La configuración estándar de PPP es del 100 % (96 PPP), 125 % (120 PPP) y 150 % (144 PPP). El usuario también puede aplicar una configuración personalizada. A partir de Windows 7, PPP es una configuración por usuario.
Escalado de DWM
Si un programa no tiene en cuenta ppp, los siguientes defectos pueden ser evidentes en la configuración de valores altos de PPP:
- Elementos de interfaz de usuario recortados.
- Diseño incorrecto.
- Mapas de bits y iconos pixelados.
- Coordenadas incorrectas del mouse, que pueden afectar a las pruebas de posicionamiento, arrastrar y soltar, etc.
Para asegurarse de que los programas más antiguos funcionan en la configuración de valores altos de PPP, DWM implementa una reserva útil. Si un programa no está marcado como compatible con PPP, DWM escalará toda la interfaz de usuario para que coincida con la configuración de PPP. Por ejemplo, en 144 PPP, la interfaz de usuario se escala un 150 %, incluidos texto, gráficos, controles y tamaños de ventana. Si el programa crea una ventana de 500 × 500, la ventana aparece realmente como 750 × 750 píxeles y el contenido de la ventana se escala según corresponda.
Este comportamiento significa que los programas más antiguos "simplemente funcionan" en la configuración de valores altos de PPP. Sin embargo, el escalado también da como resultado una apariencia algo borrosa, ya que el escalado se aplica después de dibujar la ventana.
Aplicaciones compatibles con PPP
Para evitar el escalado de DWM, un programa puede marcarse como compatible con PPP. Esto indica al DWM que no realice ningún escalado automático de PPP. Todas las aplicaciones nuevas deben diseñarse para que sean compatibles con PPP, ya que el reconocimiento de PPP mejora la apariencia de la interfaz de usuario en una configuración de PPP superior.
Un programa se declara compatible con PPP a través de su manifiesto de aplicación. Un manifiesto es simplemente un archivo XML que describe un archivo DLL o una aplicación. El manifiesto se inserta normalmente en el archivo ejecutable, aunque se puede proporcionar como un archivo independiente. Un manifiesto contiene información como las dependencias dll, el nivel de privilegio solicitado y la versión de Windows para la que se diseñó el programa.
Para declarar que el programa es compatible con PPP, incluya la siguiente información en el manifiesto.
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>
La lista que se muestra aquí es solo un manifiesto parcial, pero el enlazador de Visual Studio genera automáticamente el resto del manifiesto. Para incluir un manifiesto parcial en el proyecto, realice los pasos siguientes en Visual Studio.
- En el menú Proyecto , haga clic en Propiedad.
- En el panel izquierdo, expanda Propiedades de configuración, expanda Herramienta manifiestoy, a continuación, haga clic en Entrada y salida.
- En el cuadro de texto Archivos de manifiesto adicionales , escriba el nombre del archivo de manifiesto y, a continuación, haga clic en Aceptar.
Al marcar el programa como compatible con PPP, le indica al DWM que no escale la ventana de la aplicación. Ahora, si crea una ventana de 500 × 500, la ventana ocupará 500 × 500 píxeles, independientemente de la configuración de PPP del usuario.
GDI y PPP
El dibujo GDI se mide en píxeles. Esto significa que si el programa está marcado como compatible con PPP y pide a GDI que dibuje un rectángulo de 200 × 100, el rectángulo resultante tendrá 200 píxeles de ancho y 100 píxeles de alto en la pantalla. Sin embargo, los tamaños de fuente GDI se escalan a la configuración de PPP actual. En otras palabras, si crea una fuente de 72 puntos, el tamaño de la fuente será de 96 píxeles a 96 PPP, pero 144 píxeles a 144 PPP. Esta es una fuente de 72 puntos representada en 144 PPP mediante GDI.
Si la aplicación es compatible con PPP y usa GDI para dibujar, escale todas las coordenadas de dibujo para que coincidan con el PPP.
Direct2D y PPP
Direct2D realiza automáticamente el escalado para que coincida con la configuración de PPP. En Direct2D, las coordenadas se miden en unidades denominadas píxeles independientes del dispositivo (DIP). Un DIP se define como 1/96 de una pulgada lógica . En Direct2D, todas las operaciones de dibujo se especifican en DIP y, a continuación, se escalan a la configuración de PPP actual.
Configuración de PPP | Tamaño de DIP |
---|---|
96 | 1 píxel |
120 | 1,25 píxeles |
144 | 1,5 píxeles |
Por ejemplo, si la configuración de PPP del usuario es de 144 PPP y pide a Direct2D que dibuje un rectángulo de 200 × 100, el rectángulo será de 300 × 150 píxeles físicos. Además, DirectWrite mide los tamaños de fuente en DIP, en lugar de puntos. Para crear una fuente de 12 puntos, especifique 16 DIP (12 puntos = 1/6 pulgadas lógicas = 96/6 DIP). Cuando el texto se dibuja en la pantalla, Direct2D convierte los DIP en píxeles físicos. La ventaja de este sistema es que las unidades de medida son coherentes tanto para el texto como para el dibujo, independientemente de la configuración actual de PPP.
Una palabra de precaución: las coordenadas del mouse y de la ventana todavía se proporcionan en píxeles físicos, no en DIP. Por ejemplo, si procesa el mensaje de WM_LBUTTONDOWN , la posición del mouse hacia abajo se asigna en píxeles físicos. Para dibujar un punto en esa posición, debe convertir las coordenadas de píxel en DIP.
Conversión de píxeles físicos a DIP
El valor base de PPP se define como que se establece en USER_DEFAULT_SCREEN_DPI
96. Para determinar el factor de escalado, tome el valor de PPP y divida por USER_DEFAULT_SCREEN_DPI
.
La conversión de píxeles físicos a DIP usa la fórmula siguiente.
DIPs = pixels / (DPI / USER_DEFAULT_SCREEN_DPI)
Para obtener la configuración de PPP, llame a la función GetDpiForWindow . El valor de PPP se devuelve como un valor de punto flotante. Calcule el factor de escala para ambos ejes.
float g_DPIScale = 1.0f;
void InitializeDPIScale(HWND hwnd)
{
float dpi = GetDpiForWindow(hwnd);
g_DPIScale = dpi / USER_DEFAULT_SCREEN_DPI;
}
template <typename T>
float PixelsToDipsX(T x)
{
return static_cast<float>(x) / g_DPIScale;
}
template <typename T>
float PixelsToDipsY(T y)
{
return static_cast<float>(y) / g_DPIScale;
}
Esta es una manera alternativa de obtener la configuración de PPP si no usa Direct2D:
void InitializeDPIScale(HWND hwnd)
{
HDC hdc = GetDC(hwnd);
g_DPIScaleX = (float)GetDeviceCaps(hdc, LOGPIXELSX) / USER_DEFAULT_SCREEN_DPI;
g_DPIScaleY = (float)GetDeviceCaps(hdc, LOGPIXELSY) / USER_DEFAULT_SCREEN_DPI;
ReleaseDC(hwnd, hdc);
}
Nota:
Recomendamos que para una aplicación de escritorio, use GetDpiForWindow; y para una aplicación de Plataforma universal de Windows (UWP), usa DisplayInformation::LogicalDpi. Aunque no se recomienda, es posible establecer el reconocimiento de PPP predeterminado mediante programación mediante SetProcessDpiAwarenessContext. Una vez creada una ventana (un HWND) en el proceso, ya no se admite el cambio del modo de reconocimiento de PPP. Si va a establecer el modo de reconocimiento de PPP predeterminado del proceso mediante programación, debe llamar a la API correspondiente antes de que se hayan creado los HWND. Para obtener más información, consulta Establecer el reconocimiento de PPP predeterminado para un proceso.
Cambio del tamaño del destino de representación
Si cambia el tamaño de la ventana, debe cambiar el tamaño del destino de representación para que coincida. En la mayoría de los casos, también tendrá que actualizar el diseño y volver a dibujar la ventana. En el código siguiente se muestran estos pasos.
void MainWindow::Resize()
{
if (pRenderTarget != NULL)
{
RECT rc;
GetClientRect(m_hwnd, &rc);
D2D1_SIZE_U size = D2D1::SizeU(rc.right, rc.bottom);
pRenderTarget->Resize(size);
CalculateLayout();
InvalidateRect(m_hwnd, NULL, FALSE);
}
}
La función GetClientRect obtiene el nuevo tamaño del área cliente, en píxeles físicos (no DIP). El método ID2D1HwndRenderTarget::Resize actualiza el tamaño del destino de representación, también especificado en píxeles. La función InvalidateRect fuerza un reintentos agregando todo el área de cliente a la región de actualización de la ventana. (Vea Pintar la ventana, en el módulo 1).
A medida que la ventana crece o se reduce, normalmente tendrá que volver a calcular la posición de los objetos que dibuja. Por ejemplo, en el programa de círculo, el radio y el punto central deben actualizarse:
void MainWindow::CalculateLayout()
{
if (pRenderTarget != NULL)
{
D2D1_SIZE_F size = pRenderTarget->GetSize();
const float x = size.width / 2;
const float y = size.height / 2;
const float radius = min(x, y);
ellipse = D2D1::Ellipse(D2D1::Point2F(x, y), radius, radius);
}
}
El método ID2D1RenderTarget::GetSize devuelve el tamaño del destino de representación en DIP (no píxeles), que es la unidad adecuada para calcular el diseño. Hay un método estrechamente relacionado, ID2D1RenderTarget::GetPixelSize, que devuelve el tamaño en píxeles físicos. Para un destino de representación de HWND , este valor coincide con el tamaño devuelto por GetClientRect. Pero recuerde que el dibujo se realiza en DIP, no en píxeles.