Esercitazione: Introduzione con DirectWrite

Questo documento illustra come usare DirectWrite e Direct2D per creare testo semplice contenente un singolo formato e quindi testo contenente più formati.

Questa esercitazione contiene le parti seguenti:

Codice sorgente

Il codice sorgente illustrato in questa panoramica viene tratto dall'esempio di DirectWrite Hello World. Ogni parte viene implementata in una classe separata (SimpleText e MultiformattedText) e viene visualizzata in una finestra figlio separata. Ogni classe rappresenta una finestra di Microsoft Win32. Oltre al metodo WndProc , ogni classe contiene i metodi seguenti:

Funzione Descrizione
CreateDeviceIndependentResources Crea risorse indipendenti dal dispositivo, in modo che possano essere riutilizzate ovunque.
DiscardDeviceIndependentResources Rilascia le risorse indipendenti dal dispositivo dopo che non sono più necessarie.
CreateDeviceResources Crea risorse, ad esempio pennelli e destinazioni di rendering, associate a un determinato dispositivo.
DiscardDeviceResources Rilascia le risorse dipendenti dal dispositivo dopo che non sono più necessarie.
DrawD2DContent Usa Direct2D per eseguire il rendering sullo schermo.
Drawtext Disegna la stringa di testo usando Direct2D.
Onresize Ridimensiona la destinazione di rendering Direct2D quando viene modificata la dimensione della finestra.

 

È possibile usare l'esempio fornito o usare le istruzioni che seguono per aggiungere DirectWrite e Direct2D alla propria applicazione Win32. Per altre informazioni sull'esempio e sui file di progetto associati, vedere la DirectWrite HelloWorld.

Disegno di testo semplice

Questa sezione illustra come usare DirectWrite e Direct2D per eseguire il rendering di testo semplice con un singolo formato, come illustrato nella schermata seguente.

screenshot di

Il disegno di testo semplice sullo schermo richiede quattro componenti:

  • Stringa di caratteri da eseguire per il rendering.
  • Istanza di IDWriteTextFormat.
  • Dimensioni dell'area da contenere il testo.
  • Oggetto che può eseguire il rendering del testo. In questa esercitazione. si usa una destinazione di rendering Direct2D .

L'interfaccia IDWriteTextFormat descrive il nome della famiglia di caratteri, le dimensioni, lo stile e l'estensione usati per formattare il testo e descrive le informazioni sulle impostazioni locali. IDWriteTextFormat definisce anche i metodi per l'impostazione e ottenere le proprietà seguenti:

  • Spaziatura linea.
  • Allineamento del testo rispetto ai bordi sinistro e destro della casella di layout.
  • Allineamento del paragrafo rispetto alla parte superiore e inferiore della casella di layout.
  • Direzione di lettura.
  • Granularità di ritaglio del testo per il testo che esegue il overflow della casella di layout.
  • L'arresto della scheda incrementale.
  • Direzione del flusso di paragrafo.

L'interfaccia IDWriteTextFormat è necessaria per il testo di disegno che usa entrambi i processi descritti in questo documento .

Prima di poter creare un oggetto IDWriteTextFormat o qualsiasi altro oggetto DirectWrite, è necessaria un'istanza IDWriteFactory. Si usa un IDWriteFactory per creare istanze IDWriteTextFormat e altri oggetti DirectWrite. Per ottenere un'istanza di factory, usare la funzione DWriteCreateFactory .

Parte 1: dichiarare DirectWrite e risorse Direct2D.

In questa parte si dichiarano gli oggetti che verranno usati più avanti per la creazione e la visualizzazione del testo come membri dati privati della classe. Tutte le interfacce, le funzioni e i tipi di dati per DirectWrite vengono dichiarate nel file di intestazione dwrite.h e quelle per Direct2D vengono dichiarate nella d2d1.h. Se non è già stato fatto, includere queste intestazioni nel progetto.

  1. Nel file di intestazione della classe (SimpleText.h), dichiarare puntatori alle interfacce IDWriteFactory e IDWriteTextFormat come membri privati.

    IDWriteFactory* pDWriteFactory_;
    IDWriteTextFormat* pTextFormat_;
    
    
  2. Dichiarare i membri per contenere la stringa di testo per eseguire il rendering e la lunghezza della stringa.

    const wchar_t* wszText_;
    UINT32 cTextLength_;
    
    
  3. Dichiarare puntatori a ID2D1Factory, ID2D1HwndRenderTarget e INTERFACCE ID2D1SolidColorBrush per il rendering del testo con Direct2D.

    ID2D1Factory* pD2DFactory_;
    ID2D1HwndRenderTarget* pRT_;
    ID2D1SolidColorBrush* pBlackBrush_;
    
    

Parte 2: Creare risorse indipendenti dal dispositivo.

Direct2D offre due tipi di risorse: risorse dipendenti dal dispositivo e risorse indipendenti dal dispositivo. Le risorse dipendenti dal dispositivo sono associate a un dispositivo di rendering e non funzionano più se tale dispositivo viene rimosso. Le risorse indipendenti dal dispositivo, invece, possono durare per l'ambito dell'applicazione.

DirectWrite risorse sono indipendenti dal dispositivo.

In questa sezione vengono create le risorse indipendenti dal dispositivo usate dall'applicazione. Queste risorse devono essere liberate con una chiamata al metodo Release dell'interfaccia.

Alcune delle risorse usate devono essere create solo una volta e non sono associate a un dispositivo. L'inizializzazione per queste risorse viene inserita nel metodo SimpleText::CreateDeviceIndependentResources , che viene chiamato durante l'inizializzazione della classe.

  1. All'interno del metodo SimpleText::CreateDeviceIndependentResources nel file di implementazione della classe (SimpleText.cpp), chiamare la funzione D2D1CreateFactory per creare un'interfaccia ID2D1Factory , ovvero l'interfaccia factory radice per tutti gli oggetti Direct2D . Si usa la stessa factory per creare un'istanza di altre risorse Direct2D.

    hr = D2D1CreateFactory(
        D2D1_FACTORY_TYPE_SINGLE_THREADED,
        &pD2DFactory_
        );
    
    
  2. Chiamare la funzione DWriteCreateFactory per creare un'interfaccia IDWriteFactory, ovvero l'interfaccia factory radice per tutti gli oggetti DirectWrite. Si usa la stessa factory per creare un'istanza di altre risorse DirectWrite.

    if (SUCCEEDED(hr))
    {
        hr = DWriteCreateFactory(
            DWRITE_FACTORY_TYPE_SHARED,
            __uuidof(IDWriteFactory),
            reinterpret_cast<IUnknown**>(&pDWriteFactory_)
            );
    }
    
    
  3. Inizializzare la stringa di testo e archiviarne la lunghezza.

    wszText_ = L"Hello World using  DirectWrite!";
    cTextLength_ = (UINT32) wcslen(wszText_);
    
    
  4. Creare un oggetto interfaccia IDWriteTextFormat usando il metodo IDWriteFactory::CreateTextFormat . IDWriteTextFormat specifica il tipo di carattere, il peso, lo stile e le impostazioni locali che verranno usati per eseguire il rendering della stringa di testo.

    if (SUCCEEDED(hr))
    {
        hr = pDWriteFactory_->CreateTextFormat(
            L"Gabriola",                // Font family name.
            NULL,                       // Font collection (NULL sets it to use the system font collection).
            DWRITE_FONT_WEIGHT_REGULAR,
            DWRITE_FONT_STYLE_NORMAL,
            DWRITE_FONT_STRETCH_NORMAL,
            72.0f,
            L"en-us",
            &pTextFormat_
            );
    }
    
    
  5. Centro il testo orizzontalmente e verticalmente chiamando i metodi IDWriteTextFormat::SetTextAlignment e IDWriteTextFormat::SetParagraphAlignment.

    // Center align (horizontally) the text.
    if (SUCCEEDED(hr))
    {
        hr = pTextFormat_->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
    }
    
    if (SUCCEEDED(hr))
    {
        hr = pTextFormat_->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
    }
    
    

In questa parte sono state inizializzate le risorse indipendenti dal dispositivo usate dall'applicazione. Nella parte successiva inizializzare le risorse dipendenti dal dispositivo.

Parte 3: Creare risorse Device-Dependent.

In questa parte viene creato un ID2D1HwndRenderTarget e un ID2D1SolidColorBrush per il rendering del testo.

Una destinazione di rendering è un oggetto Direct2D che crea risorse di disegno e esegue il rendering dei comandi di disegno in un dispositivo di rendering. Un ID2D1HwndRenderTarget è una destinazione di rendering che esegue il rendering in un HWND.

Una delle risorse di disegno che una destinazione di rendering può creare è un pennello per contorni, riempimenti e testo. Un ID2D1SolidColorBrush dipinge con un colore a tinta unita.

Entrambe le interfacce ID2D1HwndRenderTarget e ID2D1SolidColorBrush sono associate a un dispositivo di rendering quando vengono create e devono essere rilasciate e ricreate se il dispositivo diventa non valido.

  1. All'interno del metodo SimpleText::CreateDeviceResources verificare se il puntatore di destinazione di rendering è NULL. In caso affermativo, recuperare le dimensioni dell'area di rendering e creare un ID2D1HwndRenderTarget di tale dimensione. Usare ID2D1HwndRenderTarget per creare un ID2D1SolidColorBrush.

    RECT rc;
    GetClientRect(hwnd_, &rc);
    
    D2D1_SIZE_U size = D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top);
    
    if (!pRT_)
    {
        // Create a Direct2D render target.
        hr = pD2DFactory_->CreateHwndRenderTarget(
                D2D1::RenderTargetProperties(),
                D2D1::HwndRenderTargetProperties(
                    hwnd_,
                    size
                    ),
                &pRT_
                );
    
        // Create a black brush.
        if (SUCCEEDED(hr))
        {
            hr = pRT_->CreateSolidColorBrush(
                D2D1::ColorF(D2D1::ColorF::Black),
                &pBlackBrush_
                );
        }
    }
    
    
  2. Nel metodo SimpleText::D iscardDeviceResources rilasciare sia il pennello che la destinazione di rendering.

    SafeRelease(&pRT_);
    SafeRelease(&pBlackBrush_);
    
    

Dopo aver creato una destinazione di rendering e un pennello, è possibile usarli per eseguire il rendering del testo.

Parte 4: Disegnare testo usando il metodo Direct2D DrawText.

  1. Nel metodo SimpleText::D rawText della classe definire l'area per il layout di testo recuperando le dimensioni dell'area di rendering e creare un rettangolo Direct2D con le stesse dimensioni.

    D2D1_RECT_F layoutRect = D2D1::RectF(
        static_cast<FLOAT>(rc.left) / dpiScaleX_,
        static_cast<FLOAT>(rc.top) / dpiScaleY_,
        static_cast<FLOAT>(rc.right - rc.left) / dpiScaleX_,
        static_cast<FLOAT>(rc.bottom - rc.top) / dpiScaleY_
        );
    
    
  2. Utilizzare il metodo ID2D1RenderTarget::D rawText e l'oggetto IDWriteTextFormat per eseguire il rendering del testo sullo schermo. Il metodo ID2D1RenderTarget::D rawText accetta i parametri seguenti:

    pRT_->DrawText(
        wszText_,        // The string to render.
        cTextLength_,    // The string's length.
        pTextFormat_,    // The text format.
        layoutRect,       // The region of the window where the text will be rendered.
        pBlackBrush_     // The brush used to draw the text.
        );
    
    

Parte 5: Eseguire il rendering del contenuto della finestra usando Direct2D

Per eseguire il rendering del contenuto della finestra usando Direct2D quando viene ricevuto un messaggio di disegno, eseguire le operazioni seguenti:

  1. Creare le risorse dipendenti dal dispositivo chiamando il metodo SimpleText::CreateDeviceResources implementato nella parte 3.
  2. Chiamare il metodo ID2D1HwndRenderTarget::BeginDraw della destinazione di rendering.
  3. Cancellare la destinazione di rendering chiamando il metodo ID2D1HwndRenderTarget::Clear .
  4. Chiamare il metodo SimpleText::D rawText, implementato nella parte 4.
  5. Chiamare il metodo ID2D1HwndRenderTarget::EndDraw della destinazione di rendering.
  6. Se necessario, eliminare le risorse dipendenti dal dispositivo in modo che possano essere ricreate quando la finestra viene ridisegnata.
hr = CreateDeviceResources();

if (SUCCEEDED(hr))
{
    pRT_->BeginDraw();

    pRT_->SetTransform(D2D1::IdentityMatrix());

    pRT_->Clear(D2D1::ColorF(D2D1::ColorF::White));

    // Call the DrawText method of this class.
    hr = DrawText();

    if (SUCCEEDED(hr))
    {
        hr = pRT_->EndDraw(
            );
    }
}

if (FAILED(hr))
{
    DiscardDeviceResources();
}

La classe SimpleText viene implementata in SimpleText.h e SimpleText.cpp.

Disegno di testo con più formati.

Questa sezione illustra come usare DirectWrite e Direct2D per eseguire il rendering del testo con più formati, come illustrato nella schermata seguente.

screenshot di

Il codice per questa sezione viene implementato come classe MultiformattedText nella DirectWrite HelloWorld. Si basa sui passaggi della sezione precedente.

Per creare testo in più formati, usare l'interfaccia IDWriteTextLayout oltre all'interfaccia IDWriteTextFormat introdotta nella sezione precedente. L'interfaccia IDWriteTextLayout descrive la formattazione e il layout di un blocco di testo. Oltre alla formattazione predefinita specificata da un oggetto IDWriteTextFormat , la formattazione per intervalli specifici di testo può essere modificata tramite IDWriteTextLayout. Ciò include il nome della famiglia di caratteri, le dimensioni, il peso, lo stile, l'estensione, il barrato e la sottolineatura.

IDWriteTextLayout offre anche metodi di hit testing. Le metriche di hit testing restituite da questi metodi sono relative alla casella di layout specificata quando l'oggetto interfaccia IDWriteTextLayout viene creato usando il metodo CreateTextLayout dell'interfaccia IDWriteFactory .

L'interfaccia IDWriteTypography viene usata per aggiungere funzionalità tipografiche OpenType facoltative a un layout di testo, ad esempio lavaggi e set di testo stilistici alternativi. Le funzionalità tipografiche possono essere aggiunte a un intervallo di testo specifico all'interno di un layout di testo chiamando il metodo AddFontFeature dell'interfaccia IDWriteTypography . Questo metodo riceve una struttura DWRITE_FONT_FEATURE come parametro che contiene una costante di enumerazione DWRITE_FONT_FEATURE_TAG e un parametro di esecuzione UINT32 . Un elenco delle funzionalità OpenType registrate è disponibile nel Registro di sistema dei tag di layout OpenType in microsoft.com. Per le costanti di enumerazione DirectWrite equivalenti, vedere DWRITE_FONT_FEATURE_TAG.

Parte 1: Creare un'interfaccia IDWriteTextLayout.

  1. Dichiarare un puntatore a un'interfaccia IDWriteTextLayout come membro della classe MultiformattedText.

    IDWriteTextLayout* pTextLayout_;
    
    
  2. Alla fine del metodo MultiformattedText::CreateDeviceIndependentResources creare un oggetto interfaccia IDWriteTextLayout chiamando il metodo CreateTextLayout . L'interfaccia IDWriteTextLayout offre funzionalità di formattazione aggiuntive, ad esempio la possibilità di applicare formati diversi a parti selezionate di testo.

    // Create a text layout using the text format.
    if (SUCCEEDED(hr))
    {
        RECT rect;
        GetClientRect(hwnd_, &rect); 
        float width  = rect.right  / dpiScaleX_;
        float height = rect.bottom / dpiScaleY_;
    
        hr = pDWriteFactory_->CreateTextLayout(
            wszText_,      // The string to be laid out and formatted.
            cTextLength_,  // The length of the string.
            pTextFormat_,  // The text format to apply to the string (contains font information, etc).
            width,         // The width of the layout box.
            height,        // The height of the layout box.
            &pTextLayout_  // The IDWriteTextLayout interface pointer.
            );
    }
    
    

Parte 2: Applicazione della formattazione con IDWriteTextLayout.

La formattazione, ad esempio le dimensioni del carattere, il peso e la sottolineatura, possono essere applicate alle sottostringa del testo da visualizzare usando l'interfaccia IDWriteTextLayout .

  1. Impostare le dimensioni del carattere per la sottostringa "Di" di "DirectWrite" su 100 dichiarando un DWRITE_TEXT_RANGE e chiamando il metodo IDWriteTextLayout::SetFontSize.

    // Format the "DirectWrite" substring to be of font size 100.
    if (SUCCEEDED(hr))
    {
        DWRITE_TEXT_RANGE textRange = {20,        // Start index where "DirectWrite" appears.
                                        6 };      // Length of the substring "Direct" in "DirectWrite".
        hr = pTextLayout_->SetFontSize(100.0f, textRange);
    }
    
  2. Sottolinea la sottostringa "DirectWrite" chiamando il metodo IDWriteTextLayout::SetUnderline.

    // Format the word "DWrite" to be underlined.
    if (SUCCEEDED(hr))
    {
    
        DWRITE_TEXT_RANGE textRange = {20,      // Start index where "DirectWrite" appears.
                                       11 };    // Length of the substring "DirectWrite".
        hr = pTextLayout_->SetUnderline(TRUE, textRange);
    }
    
  3. Impostare il peso del carattere su grassetto per la sottostringa "DirectWrite" chiamando il metodo IDWriteTextLayout::SetFontWeight.

    if (SUCCEEDED(hr))
    {
        // Format the word "DWrite" to be bold.
        DWRITE_TEXT_RANGE textRange = {20,
                                       11 };
        hr = pTextLayout_->SetFontWeight(DWRITE_FONT_WEIGHT_BOLD, textRange);
    }
    

Parte 3: Aggiunta di funzionalità tipografiche con IDWriteTypography.

  1. Dichiarare e creare un oggetto interfaccia IDWriteTypography chiamando il metodo IDWriteFactory::CreateTypography .

    // Declare a typography pointer.
    IDWriteTypography* pTypography = NULL;
    
    // Create a typography interface object.
    if (SUCCEEDED(hr))
    {
        hr = pDWriteFactory_->CreateTypography(&pTypography);
    }
    
    
  2. Aggiungere una funzionalità del tipo di carattere dichiarando un oggetto DWRITE_FONT_FEATURE con il set stilistico 7 specificato e chiamando il metodo IDWriteTypography::AddFontFeature .

    // Set the stylistic set.
    DWRITE_FONT_FEATURE fontFeature = {DWRITE_FONT_FEATURE_TAG_STYLISTIC_SET_7,
                                       1};
    if (SUCCEEDED(hr))
    {
        hr = pTypography->AddFontFeature(fontFeature);
    }
    
    
  3. Impostare il layout di testo per usare la tipografia sull'intera stringa dichiarando una variabile DWRITE_TEXT_RANGE e chiamando il metodo IDWriteTextLayout::SetTypography e passando l'intervallo di testo.

    if (SUCCEEDED(hr))
    {
        // Set the typography for the entire string.
        DWRITE_TEXT_RANGE textRange = {0,
                                       cTextLength_};
        hr = pTextLayout_->SetTypography(pTypography, textRange);
    }
    
    
  4. Impostare la nuova larghezza e l'altezza per l'oggetto layout di testo nel metodo MultiformattedText::OnResize.

    if (pTextLayout_)
    {
        pTextLayout_->SetMaxWidth(static_cast<FLOAT>(width / dpiScaleX_));
        pTextLayout_->SetMaxHeight(static_cast<FLOAT>(height / dpiScaleY_));
    }
    

Parte 4: Disegnare testo usando il metodo Direct2D DrawTextLayout.

Per disegnare il testo con le impostazioni del layout di testo specificate dall'oggetto IDWriteTextLayout , modificare il codice nel metodo MultiformattedText ::D rawText per usare IDWriteTextLayout::D rawTextLayout.

  1. Delcare una variabile D2D1_POINT_2F e impostarla sul punto superiore sinistro della finestra.

    D2D1_POINT_2F origin = D2D1::Point2F(
        static_cast<FLOAT>(rc.left / dpiScaleX_),
        static_cast<FLOAT>(rc.top / dpiScaleY_)
        );
    
    
  2. Disegnare il testo nella schermata chiamando il metodo ID2D1RenderTarget::D rawTextLayout della destinazione di rendering Direct2D e passando il puntatore IDWriteTextLayout .

    pRT_->DrawTextLayout(
        origin,
        pTextLayout_,
        pBlackBrush_
        );
    
    

La classe MultiformattedText viene implementata in MultiformattedText.h e MultiformattedText.cpp.