Tutorial: Introducción con DirectWrite

En este documento se muestra cómo usar DirectWrite y Direct2D para crear texto simple que contenga un solo formato y, a continuación, texto que contenga varios formatos.

Este tutorial contiene las siguientes partes:

Código fuente

El código fuente que se muestra en esta información general se toma del ejemplo de DirectWrite Hola mundo. Cada parte se implementa en una clase independiente (SimpleText y MultiformattedText) y se muestra en una ventana secundaria independiente. Cada clase representa una ventana de Microsoft Win32. Además del método WndProc , cada clase contiene los métodos siguientes:

Función Descripción
CreateDeviceIndependentResources Crea recursos independientes del dispositivo, por lo que se pueden reutilizar en cualquier lugar.
DiscardDeviceIndependentResources Libera los recursos independientes del dispositivo después de que ya no se necesiten.
CreateDeviceResources Crea recursos, como pinceles y destinos de representación, que están vinculados a un dispositivo determinado.
DiscardDeviceResources Libera los recursos dependientes del dispositivo después de que ya no se necesiten.
DrawD2DContent Usa Direct2D para representar en la pantalla.
Drawtext Dibuja la cadena de texto mediante Direct2D.
Onresize Cambia el tamaño del destino de representación de Direct2D cuando se cambia el tamaño de la ventana.

 

Puedes usar el ejemplo proporcionado o usar las instrucciones siguientes para agregar DirectWrite y Direct2D a tu propia aplicación Win32. Para obtener más información sobre el ejemplo y los archivos de proyecto asociados, vea el DirectWrite HelloWorld.

Dibujar texto simple

En esta sección se muestra cómo usar DirectWrite y Direct2D para representar texto simple con un solo formato, como se muestra en la siguiente captura de pantalla.

captura de pantalla de

Dibujar texto simple en la pantalla requiere cuatro componentes:

  • Cadena de caracteres que se va a representar.
  • Instancia de IDWriteTextFormat.
  • Dimensiones del área que se van a contener el texto.
  • Objeto que puede representar el texto. En este tutorial, usa un destino de representación de Direct2D .

La interfaz IDWriteTextFormat describe el nombre de familia de fuentes, el tamaño, el peso, el estilo y la extensión usados para dar formato al texto y describe la información de configuración regional. IDWriteTextFormat también define métodos para establecer y obtener las siguientes propiedades:

  • Espaciado de línea.
  • Alineación del texto con respecto a los bordes izquierdo y derecho del cuadro de diseño.
  • Alineación de párrafo relativa a la parte superior e inferior del cuadro de diseño.
  • Dirección de lectura.
  • Granularidad de recorte de texto para texto que desborda el cuadro de diseño.
  • Tabulación incremental.
  • Dirección del flujo del párrafo.

La interfaz IDWriteTextFormat es necesaria para dibujar texto que use ambos procesos descritos en este documento.

Para poder crear un objeto IDWriteTextFormat o cualquier otro objeto de DirectWrite, necesita una instancia IDWriteFactory. Se usa un IDWriteFactory para crear instancias IDWriteTextFormat y otros objetos DirectWrite. Para obtener una instancia de generador, use la función DWriteCreateFactory .

Parte 1: Declarar DirectWrite y recursos de Direct2D.

En esta parte, declara los objetos que usará más adelante para crear y mostrar texto como miembros de datos privados de la clase. Todas las interfaces, funciones y tipos de datos de DirectWrite se declaran en el archivo de encabezado dwrite.h y las de Direct2D se declaran en d2d1.h; si aún no lo ha hecho, incluya estos encabezados en el proyecto.

  1. En el archivo de encabezado de clase (SimpleText.h), declare punteros a las interfaces IDWriteFactory e IDWriteTextFormat como miembros privados.

    IDWriteFactory* pDWriteFactory_;
    IDWriteTextFormat* pTextFormat_;
    
    
  2. Declare los miembros para que contengan la cadena de texto que se va a representar y la longitud de la cadena.

    const wchar_t* wszText_;
    UINT32 cTextLength_;
    
    
  3. Declare punteros a las interfaces ID2D1Factory, ID2D1HwndRenderTarget e ID2D1SolidColorBrush para representar el texto con Direct2D.

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

Parte 2: Crear recursos independientes del dispositivo.

Direct2D proporciona dos tipos de recursos: recursos dependientes del dispositivo y recursos independientes del dispositivo. Los recursos dependientes del dispositivo están asociados a un dispositivo de representación y ya no funcionan si se quita ese dispositivo. Por otro lado, los recursos independientes del dispositivo pueden durar para el ámbito de la aplicación.

DirectWrite recursos son independientes del dispositivo.

En esta sección, creará los recursos independientes del dispositivo que usa la aplicación. Estos recursos se deben liberar con una llamada al método Release de la interfaz .

Algunos de los recursos que se usan solo se deben crear una vez y no están vinculados a un dispositivo. La inicialización de estos recursos se coloca en el método SimpleText::CreateDeviceIndependentResources , al que se llama al inicializar la clase .

  1. Dentro del método SimpleText::CreateDeviceIndependentResources en el archivo de implementación de clase (SimpleText.cpp), llame a la función D2D1CreateFactory para crear una interfaz ID2D1Factory , que es la interfaz de fábrica raíz para todos los objetos Direct2D . Usa la misma fábrica para crear instancias de otros recursos de Direct2D.

    hr = D2D1CreateFactory(
        D2D1_FACTORY_TYPE_SINGLE_THREADED,
        &pD2DFactory_
        );
    
    
  2. Llame a la función DWriteCreateFactory para crear una interfaz IDWriteFactory, que es la interfaz de generador raíz para todos los objetos DirectWrite. Use la misma fábrica para crear instancias de otros recursos DirectWrite.

    if (SUCCEEDED(hr))
    {
        hr = DWriteCreateFactory(
            DWRITE_FACTORY_TYPE_SHARED,
            __uuidof(IDWriteFactory),
            reinterpret_cast<IUnknown**>(&pDWriteFactory_)
            );
    }
    
    
  3. Inicialice la cadena de texto y almacene su longitud.

    wszText_ = L"Hello World using  DirectWrite!";
    cTextLength_ = (UINT32) wcslen(wszText_);
    
    
  4. Cree un objeto de interfaz IDWriteTextFormat mediante el método IDWriteFactory::CreateTextFormat . IdWriteTextFormat especifica la fuente, el peso, el ajuste, el estilo y la configuración regional que se usarán para representar la cadena de texto.

    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. Centre el texto horizontal y verticalmente llamando a los métodos 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);
    }
    
    

En esta parte, inicializó los recursos independientes del dispositivo que usa la aplicación. En la siguiente parte, inicializa los recursos dependientes del dispositivo.

Parte 3: Crear recursos de Device-Dependent.

En esta parte, creará un ID2D1HwndRenderTarget y un ID2D1SolidColorBrush para representar el texto.

Un destino de representación es un objeto Direct2D que crea recursos de dibujo y representa comandos de dibujo en un dispositivo de representación. Un ID2D1HwndRenderTarget es un destino de representación que se representa en un HWND.

Uno de los recursos de dibujo que un destino de representación puede crear es un pincel para pintar contornos, rellenos y texto. Un ID2D1SolidColorBrush pinta con un color sólido.

Las interfaces ID2D1HwndRenderTarget y ID2D1SolidColorBrush se enlazan a un dispositivo de representación cuando se crean y se deben liberar y volver a crear si el dispositivo deja de ser válido.

  1. Dentro del método SimpleText::CreateDeviceResources, compruebe si el puntero de destino de representación es NULL. Si es así, recupere el tamaño del área de representación y cree un ID2D1HwndRenderTarget de ese tamaño. Use ID2D1HwndRenderTarget para crear 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. En el método SimpleText::D iscardDeviceResources, libere el destino de pincel y representación.

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

Ahora que ha creado un destino de representación y un pincel, puede usarlos para representar el texto.

Parte 4: Dibujar texto mediante el método DrawText de Direct2D.

  1. En el método SimpleText::D rawText de la clase, defina el área para el diseño de texto recuperando las dimensiones del área de representación y cree un rectángulo Direct2D que tenga las mismas dimensiones.

    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. Use el método ID2D1RenderTarget::D rawText y el objeto IDWriteTextFormat para representar texto en la pantalla. El método ID2D1RenderTarget::D rawText toma los parámetros siguientes:

    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: Representar el contenido de la ventana mediante Direct2D

Para representar el contenido de la ventana mediante Direct2D cuando se recibe un mensaje de pintura, haga lo siguiente:

  1. Cree los recursos dependientes del dispositivo llamando al método SimpleText::CreateDeviceResources implementado en la parte 3.
  2. Llame al método ID2D1HwndRenderTarget::BeginDraw del destino de representación.
  3. Desactive el destino de representación llamando al método ID2D1HwndRenderTarget::Clear .
  4. Llame al método SimpleText::D rawText, implementado en la parte 4.
  5. Llame al método ID2D1HwndRenderTarget::EndDraw del destino de representación.
  6. Si es necesario, descarte los recursos dependientes del dispositivo para que se puedan volver a crear cuando se vuelva a dibujar la ventana.
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 clase SimpleText se implementa en SimpleText.h y SimpleText.cpp.

Dibujar texto con varios formatos.

En esta sección se muestra cómo usar DirectWrite y Direct2D para representar texto con varios formatos, como se muestra en la captura de pantalla siguiente.

captura de pantalla de

El código de esta sección se implementa como la clase MultiformattedText en el DirectWrite HelloWorld. Se basa en los pasos de la sección anterior.

Para crear texto con varios formatos, use la interfaz IDWriteTextLayout además de la interfaz IDWriteTextFormat introducida en la sección anterior. La interfaz IDWriteTextLayout describe el formato y el diseño de un bloque de texto. Además del formato predeterminado especificado por un objeto IDWriteTextFormat , el formato de intervalos de texto específicos se puede cambiar mediante IDWriteTextLayout. Esto incluye el nombre de familia de fuentes, el tamaño, el peso, el estilo, el ajuste, el tachado y la inserción.

IDWriteTextLayout también proporciona métodos de prueba de posicionamiento. Las métricas de prueba de posicionamiento devueltas por estos métodos son relativas al cuadro de diseño especificado cuando se crea el objeto de interfaz IDWriteTextLayout mediante el método CreateTextLayout de la interfaz IDWriteFactory .

La interfaz IDWriteTypography se usa para agregar características tipográficas OpenType opcionales a un diseño de texto, como swashes y conjuntos de texto de estilo alternativos. Las características tipográficas se pueden agregar a un intervalo específico de texto dentro de un diseño de texto llamando al método AddFontFeature de la interfaz IDWriteTypography . Este método recibe una estructura de DWRITE_FONT_FEATURE como parámetro que contiene una constante de enumeración DWRITE_FONT_FEATURE_TAG y un parámetro de ejecución UINT32 . Puede encontrar una lista de las características de OpenType registradas en el Registro de etiquetas de diseño de OpenType en microsoft.com. Para ver las constantes de enumeración de DirectWrite equivalentes, consulte DWRITE_FONT_FEATURE_TAG.

Parte 1: Crear una interfaz IDWriteTextLayout.

  1. Declare un puntero a una interfaz IDWriteTextLayout como miembro de la clase MultiformattedText.

    IDWriteTextLayout* pTextLayout_;
    
    
  2. Al final del método MultiformattedText::CreateDeviceIndependentResources, cree un objeto de interfaz IDWriteTextLayout llamando al método CreateTextLayout . La interfaz IDWriteTextLayout proporciona características de formato adicionales, como la capacidad de aplicar diferentes formatos a partes seleccionadas de texto.

    // 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: Aplicar formato con IDWriteTextLayout.

El formato, como el tamaño de fuente, el peso y la inserción, se puede aplicar a las subcadenas del texto que se van a mostrar mediante la interfaz IDWriteTextLayout .

  1. Establezca el tamaño de fuente de la subcadena "Di" de "DirectWrite" en 100 declarando un DWRITE_TEXT_RANGE y llamando al método 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. Subraya la subcadena "DirectWrite" llamando al método 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. Establezca el peso de fuente en negrita para la subcadena "DirectWrite" llamando al método 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: Agregar características tipográficas con IDWriteTypography.

  1. Declare y cree un objeto de interfaz IDWriteTypography llamando al método IDWriteFactory::CreateTypography .

    // Declare a typography pointer.
    IDWriteTypography* pTypography = NULL;
    
    // Create a typography interface object.
    if (SUCCEEDED(hr))
    {
        hr = pDWriteFactory_->CreateTypography(&pTypography);
    }
    
    
  2. Agregue una característica de fuente declarando un objeto DWRITE_FONT_FEATURE que tenga el conjunto estilístico 7 especificado y llamando al método 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. Establezca el diseño de texto para usar la tipografía en toda la cadena declarando una variable DWRITE_TEXT_RANGE y llamando al método IDWriteTextLayout::SetTypography y pasando el intervalo de texto.

    if (SUCCEEDED(hr))
    {
        // Set the typography for the entire string.
        DWRITE_TEXT_RANGE textRange = {0,
                                       cTextLength_};
        hr = pTextLayout_->SetTypography(pTypography, textRange);
    }
    
    
  4. Establezca el nuevo ancho y alto para el objeto de diseño de texto en el método MultiformattedText::OnResize.

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

Parte 4: Dibujar texto con el método DrawTextLayout direct2D.

Para dibujar el texto con la configuración de diseño de texto especificada por el objeto IDWriteTextLayout , cambie el código en el método MultiformattedText::D rawText para usar IDWriteTextLayout::D rawTextLayout.

  1. Delcare un D2D1_POINT_2F variable y establézcalo en el punto superior izquierdo de la ventana.

    D2D1_POINT_2F origin = D2D1::Point2F(
        static_cast<FLOAT>(rc.left / dpiScaleX_),
        static_cast<FLOAT>(rc.top / dpiScaleY_)
        );
    
    
  2. Dibuja el texto en la pantalla llamando al método ID2D1RenderTarget::D rawTextLayout del destino de representación de Direct2D y pasando el puntero IDWriteTextLayout .

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

La clase MultiformattedText se implementa en MultiformattedText.h y MultiformattedText.cpp.