Partager via


Rendre en utilisant le convertisseur de texte personnalisé

Une disposition DirectWritetext peut être dessinée par un convertisseur de texte personnalisé dérivé d’IDWriteTextRenderer. Un convertisseur personnalisé est nécessaire pour tirer parti de certaines fonctionnalités avancées de DirectWrite, telles que le rendu sur une surface bitmap ou GDI, des objets inline et des effets de dessin client. Ce tutoriel décrit les méthodes d’IDWriteTextRenderer et fournit un exemple d’implémentation qui utilise Direct2D pour afficher du texte avec un remplissage bitmap.

Ce didacticiel contient les éléments suivants :

Votre convertisseur de texte personnalisé doit implémenter les méthodes héritées de IUnknown en plus des méthodes répertoriées dans la page de référence IDWriteTextRenderer et ci-dessous.

Pour obtenir le code source complet du convertisseur de texte personnalisé, consultez les fichiers CustomTextRenderer.cpp et CustomTextRenderer.h de l’exemple de DirectWrite Hello World.

Constructeur

Votre convertisseur de texte personnalisé aura besoin d’un constructeur. Cet exemple utilise des pinceaux Direct2D solides et bitmap pour afficher le texte.

Pour cette raison, le constructeur prend les paramètres figurant dans le tableau ci-dessous avec des descriptions.

Paramètre Description
pD2DFactory Pointeur vers un objet ID2D1Factory qui sera utilisé pour créer les ressources Direct2D nécessaires.
Prt Pointeur vers l’objet ID2D1HwndRenderTarget dans lequel le texte sera rendu.
pOutlineBrush Pointeur vers le fichier ID2D1SolidColorBrush qui sera utilisé pour dessiner le plan du texte
pFillBrush Pointeur vers le fichier ID2D1BitmapBrush qui sera utilisé pour remplir le texte.

 

Celles-ci seront stockées par le constructeur, comme indiqué dans le code suivant.

CustomTextRenderer::CustomTextRenderer(
    ID2D1Factory* pD2DFactory, 
    ID2D1HwndRenderTarget* pRT, 
    ID2D1SolidColorBrush* pOutlineBrush, 
    ID2D1BitmapBrush* pFillBrush
    )
:
cRefCount_(0), 
pD2DFactory_(pD2DFactory), 
pRT_(pRT), 
pOutlineBrush_(pOutlineBrush), 
pFillBrush_(pFillBrush)
{
    pD2DFactory_->AddRef();
    pRT_->AddRef();
    pOutlineBrush_->AddRef();
    pFillBrush_->AddRef();
}

DrawGlyphRun()

La méthode DrawGlyphRun est la méthode de rappel main du convertisseur de texte. Il est passé une série de glyphes à rendre en plus d’informations telles que l’origine de la ligne de base et le mode de mesure. Il transmet également un objet d’effet de dessin client à appliquer à l’exécution du glyphe. Pour plus d’informations, consultez la rubrique Comment ajouter des effets de dessin client à une disposition de texte .

Cette implémentation de convertisseur de texte affiche les exécutions de glyphes en les convertissant en géométries Direct2D , puis en dessinant et en remplissant les géométries. Il s’agit des étapes suivantes.

  1. Créez un objet ID2D1PathGeometry , puis récupérez l’objet ID2D1GeometrySink à l’aide de la méthode ID2D1PathGeometry::Open .

    // Create the path geometry.
    ID2D1PathGeometry* pPathGeometry = NULL;
    hr = pD2DFactory_->CreatePathGeometry(
            &pPathGeometry
            );
    
    // Write to the path geometry using the geometry sink.
    ID2D1GeometrySink* pSink = NULL;
    if (SUCCEEDED(hr))
    {
        hr = pPathGeometry->Open(
            &pSink
            );
    }
    
  2. La DWRITE_GLYPH_RUN passée à DrawGlyphRun contient un objet IDWriteFontFace , nommé fontFace, qui représente la face de police pour l’ensemble de l’exécution du glyphe. Placez le contour de l’exécution du glyphe dans le récepteur geometry à l’aide de la méthode IDWriteFontFace:: GetGlyphRunOutline , comme indiqué dans le code suivant.

    // Get the glyph run outline geometries back from DirectWrite and place them within the
    // geometry sink.
    if (SUCCEEDED(hr))
    {
        hr = glyphRun->fontFace->GetGlyphRunOutline(
            glyphRun->fontEmSize,
            glyphRun->glyphIndices,
            glyphRun->glyphAdvances,
            glyphRun->glyphOffsets,
            glyphRun->glyphCount,
            glyphRun->isSideways,
            glyphRun->bidiLevel%2,
            pSink
            );
    }
    
  3. Après avoir rempli le récepteur geometry, fermez-le.

    // Close the geometry sink
    if (SUCCEEDED(hr))
    {
        hr = pSink->Close();
    }
    
  4. L’origine de l’exécution du glyphe doit être traduite afin qu’elle soit rendue à partir de l’origine de base correcte, comme indiqué dans le code suivant.

    // Initialize a matrix to translate the origin of the glyph run.
    D2D1::Matrix3x2F const matrix = D2D1::Matrix3x2F(
        1.0f, 0.0f,
        0.0f, 1.0f,
        baselineOriginX, baselineOriginY
        );
    

    Les valeurs baselineOriginX et baselineOriginY sont passées en tant que paramètres à la méthode de rappel DrawGlyphRun .

  5. Créez la géométrie transformée à l’aide de la méthode ID2D1Factory::CreateTransformedGeometry et en transmettant la géométrie de chemin d’accès et la matrice de traduction.

    // Create the transformed geometry
    ID2D1TransformedGeometry* pTransformedGeometry = NULL;
    if (SUCCEEDED(hr))
    {
        hr = pD2DFactory_->CreateTransformedGeometry(
            pPathGeometry,
            &matrix,
            &pTransformedGeometry
            );
    }
    
  6. Enfin, dessinez le contour de la géométrie transformée et remplissez-le à l’aide des méthodes ID2D1RenderTarget::D rawGeometry et ID2D1RenderTarget::FillGeometry et des pinceaux Direct2D stockés en tant que variables membres.

        // Draw the outline of the glyph run
        pRT_->DrawGeometry(
            pTransformedGeometry,
            pOutlineBrush_
            );
    
        // Fill in the glyph run
        pRT_->FillGeometry(
            pTransformedGeometry,
            pFillBrush_
            );
    
  7. Maintenant que vous avez terminé de dessiner, n’oubliez pas de propre les objets créés dans cette méthode.

    SafeRelease(&pPathGeometry);
    SafeRelease(&pSink);
    SafeRelease(&pTransformedGeometry);
    

DrawUnderline() et DrawStrikethrough()

IDWriteTextRenderer a également des rappels pour dessiner le soulignement et le barré. Cet exemple dessine un rectangle simple pour un trait de soulignement ou un barré, mais d’autres formes peuvent être dessinées.

Le dessin d’un soulignement à l’aide de Direct2D se compose des étapes suivantes.

  1. Tout d’abord, créez une structure D2D1_RECT_F de la taille et de la forme du soulignement. La structure DWRITE_UNDERLINE passée à la méthode de rappel DrawUnderline fournit le décalage, la largeur et l’épaisseur du soulignement.

    D2D1_RECT_F rect = D2D1::RectF(
        0,
        underline->offset,
        underline->width,
        underline->offset + underline->thickness
        );
    
  2. Ensuite, créez un objet ID2D1RectangleGeometry à l’aide de la méthode ID2D1Factory::CreateRectangleGeometry et de la structure D2D1_RECT_F initialisée.

    ID2D1RectangleGeometry* pRectangleGeometry = NULL;
    hr = pD2DFactory_->CreateRectangleGeometry(
            &rect, 
            &pRectangleGeometry
            );
    
  3. Comme pour l’exécution du glyphe, l’origine de la géométrie de soulignement doit être traduite, en fonction des valeurs d’origine de base, à l’aide de la méthode CreateTransformedGeometry .

    // Initialize a matrix to translate the origin of the underline
    D2D1::Matrix3x2F const matrix = D2D1::Matrix3x2F(
        1.0f, 0.0f,
        0.0f, 1.0f,
        baselineOriginX, baselineOriginY
        );
    
    ID2D1TransformedGeometry* pTransformedGeometry = NULL;
    if (SUCCEEDED(hr))
    {
        hr = pD2DFactory_->CreateTransformedGeometry(
            pRectangleGeometry,
            &matrix,
            &pTransformedGeometry
            );
    }
    
  4. Enfin, dessinez le contour de la géométrie transformée et remplissez-le à l’aide des méthodes ID2D1RenderTarget::D rawGeometry et ID2D1RenderTarget::FillGeometry et des pinceaux Direct2D stockés en tant que variables membres.

        // Draw the outline of the glyph run
        pRT_->DrawGeometry(
            pTransformedGeometry,
            pOutlineBrush_
            );
    
        // Fill in the glyph run
        pRT_->FillGeometry(
            pTransformedGeometry,
            pFillBrush_
            );
    
  5. Maintenant que vous avez terminé de dessiner, n’oubliez pas de propre les objets créés dans cette méthode.

    SafeRelease(&pRectangleGeometry);
    SafeRelease(&pTransformedGeometry);
    

Le processus de dessin d’un barré est le même. Toutefois, le barré aura un décalage différent, et probablement une largeur et une épaisseur différentes.

Alignement de pixels, pixels par DIP et transformation

IsPixelSnappingDisabled()

Cette méthode est appelée pour déterminer si l’alignement de pixels est désactivé. La valeur par défaut recommandée est FALSE, et il s’agit de la sortie de cet exemple.

*isDisabled = FALSE;

GetCurrentTransform()

Cet exemple affiche une cible de rendu Direct2D. Par conséquent, transférez la transformation à partir de la cible de rendu à l’aide de ID2D1RenderTarget::GetTransform.

//forward the render target's transform
pRT_->GetTransform(reinterpret_cast<D2D1_MATRIX_3X2_F*>(transform));

GetPixelsPerDip()

Cette méthode est appelée pour obtenir le nombre de pixels par pixel indépendant de l’appareil (DIP).

float x, yUnused;

pRT_->GetDpi(&x, &yUnused);
*pixelsPerDip = x / 96;

DrawInlineObject()

Un convertisseur de texte personnalisé a également un rappel pour dessiner des objets inline. Dans cet exemple, DrawInlineObject retourne E_NOTIMPL. Une explication de la façon de dessiner des objets inline dépasse le cadre de ce didacticiel. Pour plus d’informations, consultez la rubrique Comment ajouter des objets inline à une disposition de texte .

Le destructeur

Il est important de libérer tous les pointeurs utilisés par la classe de convertisseur de texte personnalisé.

CustomTextRenderer::~CustomTextRenderer()
{
    SafeRelease(&pD2DFactory_);
    SafeRelease(&pRT_);
    SafeRelease(&pOutlineBrush_);
    SafeRelease(&pFillBrush_);
}

Utilisation du convertisseur de texte personnalisé

Vous effectuez le rendu avec le renderer personnalisé à l’aide de la méthode IDWriteTextLayout::D raw , qui prend une interface de rappel dérivée de IDWriteTextRenderer en tant qu’argument, comme indiqué dans le code suivant.

// Draw the text layout using DirectWrite and the CustomTextRenderer class.
hr = pTextLayout_->Draw(
        NULL,
        pTextRenderer_,  // Custom text renderer.
        origin.x,
        origin.y
        );

La méthode IDWriteTextLayout::D raw appelle les méthodes du rappel de convertisseur personnalisé que vous fournissez. Les méthodes DrawGlyphRun, DrawUnderline, DrawInlineObject et DrawStrikethrough décrites ci-dessus exécutent les fonctions de dessin.