Ppp et pixels indépendants de l’appareil

Pour programmer efficacement avec des graphiques Windows, vous devez comprendre deux concepts connexes :

  • Points par pouce (PPP)
  • Pixel indépendant de l’appareil (DIPs).

Commençons par PPP. Cela nécessite un court détour par la typographie. En typographie, la taille du type est mesurée en unités appelées points. Un point est égal à 1/72 de pouce.

1 pt = 1/72 pouce

Notes

Il s’agit de la définition de point de publication de bureau. Historiquement, la mesure exacte d’un point a varié.

Par exemple, une police à 12 points est conçue pour s’adapter à une ligne de texte de 1/6 pouce (12/72). Évidemment, cela ne signifie pas que chaque caractère de la police a exactement 1/6 » de haut. En fait, certains caractères peuvent être plus grands que 1/6 ». Par exemple, dans de nombreuses polices, le caractère Å est plus grand que la hauteur nominale de la police. Pour s’afficher correctement, la police a besoin d’un espace supplémentaire entre le texte. Cet espace est appelé le début.

L’illustration suivante montre une police à 72 points. Les lignes pleines affichent un cadre englobant de 1 pouce de haut autour du texte. La ligne en pointillés est appelée ligne de base. La plupart des caractères d’une police reposent sur la ligne de base. La hauteur de la police inclut la partie au-dessus de la ligne de base ( l’ascension) et la partie en dessous de la ligne de base (la descente). Dans la police présentée ici, la montée est de 56 points et la descente est de 16 points.

illustration montrant une police de 72 points.

Toutefois, lorsqu’il s’agit d’un écran d’ordinateur, la mesure de la taille du texte est problématique, car les pixels ne sont pas tous de la même taille. La taille d’un pixel dépend de deux facteurs : la résolution d’affichage et la taille physique du moniteur. Par conséquent, les pouces physiques ne sont pas une mesure utile, car il n’existe aucune relation fixe entre les pouces physiques et les pixels. Au lieu de cela, les polices sont mesurées en unités logiques . Une police de 72 points est définie pour avoir une hauteur d’un pouce logique. Les pouces logiques sont ensuite convertis en pixels. Pendant de nombreuses années, Windows a utilisé la conversion suivante : un pouce logique est égal à 96 pixels. À l’aide de ce facteur de mise à l’échelle, une police de 72 points est affichée avec une hauteur de 96 pixels. Une police à 12 points mesure 16 pixels.

12 points = 12/72 pouce logique = 1/6 pouce logique = 96/6 pixels = 16 pixels

Ce facteur de mise à l’échelle est décrit comme étant de 96 points par pouce (PPP). Le terme points dérive de l’impression, où des points physiques d’encre sont mis sur papier. Pour les écrans d’ordinateur, il serait plus précis de dire 96 pixels par pouce logique, mais le terme PPP est bloqué.

Étant donné que la taille réelle des pixels varie, le texte lisible sur un moniteur peut être trop petit sur un autre. En outre, les utilisateurs ont des préférences différentes : certaines personnes préfèrent un texte plus volumineux. Pour cette raison, Windows permet à l’utilisateur de modifier le paramètre PPP. Par exemple, si l’utilisateur définit l’affichage sur 144 PPP, une police de 72 points mesure 144 pixels. Les paramètres PPP standard sont 100 % (96 PPP), 125 % (120 PPP) et 150 % (144 PPP). L’utilisateur peut également appliquer un paramètre personnalisé. À partir de Windows 7, ppp est un paramètre par utilisateur.

Mise à l’échelle DWM

Si un programme ne prend pas en compte la ppp, les défauts suivants peuvent être apparents dans les paramètres de haute résolution :

  • Éléments d’interface utilisateur clippés.
  • Disposition incorrecte.
  • Bitmaps et icônes pixelisées.
  • Coordonnées de souris incorrectes, ce qui peut affecter le test d’accès, le glisser-déplacer, etc.

Pour s’assurer que les programmes plus anciens fonctionnent avec des paramètres de ppp élevés, le DWM implémente une solution de secours utile. Si un programme n’est pas marqué comme prenant en charge ppp, le DWM met à l’échelle l’ensemble de l’interface utilisateur pour qu’elle corresponde au paramètre PPP. Par exemple, à 144 PPP, l’interface utilisateur est mise à l’échelle de 150 %, y compris le texte, les graphiques, les contrôles et les tailles de fenêtre. Si le programme crée une fenêtre de 500 × 500, la fenêtre apparaît en fait comme 750 × 750 pixels, et le contenu de la fenêtre est mis à l’échelle en conséquence.

Ce comportement signifie que les programmes plus anciens « fonctionnent simplement » avec des paramètres de haute résolution. Toutefois, la mise à l’échelle entraîne également une apparence quelque peu floue, car la mise à l’échelle est appliquée une fois la fenêtre dessinée.

Applications prenant en charge l’ppp

Pour éviter la mise à l’échelle DWM, un programme peut se marquer comme prenant en charge les PPP. Cela indique au DWM de ne pas effectuer de mise à l’échelle automatique ppp. Toutes les nouvelles applications doivent être conçues pour prendre en charge les PPP, car la reconnaissance ppp améliore l’apparence de l’interface utilisateur à des paramètres PPP plus élevés.

Un programme se déclare prenant en charge par le biais de son manifeste d’application. Un manifeste est simplement un fichier XML qui décrit une DLL ou une application. Le manifeste est généralement incorporé dans le fichier exécutable, bien qu’il puisse être fourni sous la forme d’un fichier distinct. Un manifeste contient des informations telles que les dépendances DLL, le niveau de privilège demandé et la version de Windows pour laquelle le programme a été conçu.

Pour déclarer que votre programme prend en charge les ppp, incluez les informations suivantes dans le manifeste.

<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 liste présentée ici n’est qu’un manifeste partiel, mais l’éditeur de liens Visual Studio génère automatiquement le reste du manifeste pour vous. Pour inclure un manifeste partiel dans votre projet, procédez comme suit dans Visual Studio.

  1. Dans le menu Projet , cliquez sur Propriété.
  2. Dans le volet gauche, développez Propriétés de configuration, outil manifeste, puis cliquez sur Entrée et sortie.
  3. Dans la zone de texte Fichiers manifeste supplémentaires , tapez le nom du fichier manifeste, puis cliquez sur OK.

En marquant votre programme comme prenant en charge les ppp, vous indiquez au DWM de ne pas mettre à l’échelle votre fenêtre d’application. Maintenant, si vous créez une fenêtre de 500 × 500, la fenêtre occupe 500 × 500 pixels, quel que soit le paramètre PPP de l’utilisateur.

GDI et DPI

Le dessin GDI est mesuré en pixels. Cela signifie que si votre programme est marqué comme prenant en charge les ppp et que vous demandez à GDI de dessiner un rectangle de 200 × 100, le rectangle résultant aura une largeur de 200 pixels et une hauteur de 100 pixels à l’écran. Toutefois, les tailles de police GDI sont mises à l’échelle selon le paramètre PPP actuel. En d’autres termes, si vous créez une police à 72 points, la taille de la police sera de 96 pixels à 96 PPP, mais de 144 pixels à 144 PPP. Voici une police de 72 points rendue à 144 PPP à l’aide de GDI.

diagramme montrant la mise à l’échelle de police dpi dans gdi.

Si votre application prend en charge la résolution des problèmes et que vous utilisez GDI pour le dessin, mettez à l’échelle toutes vos coordonnées de dessin pour qu’elles correspondent à la ppp.

Direct2D et PPP

Direct2D effectue automatiquement la mise à l’échelle pour correspondre au paramètre PPP. Dans Direct2D, les coordonnées sont mesurées en unités appelées pixels indépendants des appareils (DIPs). Un DIP est défini comme 1/96e d’un pouce logique . Dans Direct2D, toutes les opérations de dessin sont spécifiées dans les DIPs, puis mises à l’échelle pour atteindre le paramètre PPP actuel.

Paramètre PPP Taille dip
96 1 pixel
120 1,25 pixels
144 1,5 pixels

Par exemple, si le paramètre PPP de l’utilisateur est de 144 PPP et que vous demandez à Direct2D de dessiner un rectangle de 200 × 100, le rectangle sera de 300 × 150 pixels physiques. En outre, DirectWrite mesure les tailles de police dans les dips, plutôt que les points. Pour créer une police à 12 points, spécifiez 16 DIPs (12 points = 1/6 pouce logique = 96/6 DIPs). Lorsque le texte est dessiné à l’écran, Direct2D convertit les adresses DIPs en pixels physiques. L’avantage de ce système est que les unités de mesure sont cohérentes pour le texte et le dessin, quel que soit le paramètre PPP actuel.

Mise en garde : les coordonnées de la souris et de la fenêtre sont toujours indiquées en pixels physiques, et non en DIPs. Par exemple, si vous traitez le message WM_LBUTTONDOWN , la position vers le bas de la souris est indiquée en pixels physiques. Pour dessiner un point à cette position, vous devez convertir les coordonnées de pixels en DIPs.

Conversion de pixels physiques en DIPs

La valeur de base de DPI est définie sur USER_DEFAULT_SCREEN_DPI 96. Pour déterminer le facteur de mise à l’échelle, prenez la valeur DPI et divisez par USER_DEFAULT_SCREEN_DPI.

La conversion de pixels physiques en DIPs utilise la formule suivante.

DIPs = pixels / (DPI / USER_DEFAULT_SCREEN_DPI)

Pour obtenir le paramètre DPI, appelez la fonction GetDpiForWindow . L’ppp est retourné sous forme de valeur à virgule flottante. Calculez le facteur de mise à l’échelle pour les deux axes.

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 PixelsToDips(T y)
{
    return static_cast<float>(y) / g_DPIScale;
}

Voici une autre façon d’obtenir le paramètre DPI si vous n’utilisez pas 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);
}

Notes

Nous vous recommandons d’utiliser GetDpiForWindow pour une application de bureau ; et pour une application plateforme Windows universelle (UWP), utilisez DisplayInformation::LogicalDpi. Bien que nous ne le recommandons pas, il est possible de définir la reconnaissance DPI par défaut par programme à l’aide de SetProcessDpiAwarenessContext. Une fois qu’une fenêtre (HWND) a été créée dans votre processus, la modification du mode de sensibilisation DPI n’est plus prise en charge. Si vous définissez le mode de sensibilisation DPI par défaut du processus par programme, vous devez appeler l’API correspondante avant de créer des HWND. Pour plus d’informations, consultez Définition de la reconnaissance DPI par défaut pour un processus.

Redimensionnement de la cible de rendu

Si la taille de la fenêtre change, vous devez redimensionner la cible de rendu pour qu’elle corresponde. Dans la plupart des cas, vous devez également mettre à jour la disposition et repeindre la fenêtre. Le code suivant illustre ces étapes.

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 fonction GetClientRect obtient la nouvelle taille de la zone cliente, en pixels physiques (et non en DIPs). La méthode ID2D1HwndRenderTarget::Resize met à jour la taille de la cible de rendu, également spécifiée en pixels. La fonction InvalidateRect force un repaint en ajoutant l’ensemble de la zone cliente à la région de mise à jour de la fenêtre. (Voir Peinture de la fenêtre, dans le module 1.)

À mesure que la fenêtre augmente ou diminue, vous devez généralement recalculer la position des objets que vous dessinez. Par exemple, dans le programme circulaire, le rayon et le point central doivent être mis à jour :

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);
    }
}

La méthode ID2D1RenderTarget::GetSize retourne la taille de la cible de rendu en DIPs (et non en pixels), qui est l’unité appropriée pour calculer la disposition. Il existe une méthode étroitement liée, ID2D1RenderTarget::GetPixelSize, qui retourne la taille en pixels physiques. Pour une cible de rendu HWND , cette valeur correspond à la taille retournée par GetClientRect. N’oubliez pas que le dessin s’effectue dans des DIPs, et non dans des pixels.

Suivant

Utilisation de la couleur dans Direct2D