Como adicionar objetos embutidos a um layout de texto
Fornece um breve tutorial sobre como adicionar objetos embutidos a um aplicativo DirectWrite que exibe texto usando a interface IDWriteTextLayout.
O produto final deste tutorial é um aplicativo que exibe texto com uma imagem embutida inserida nele, conforme mostrado na captura de tela a seguir.
Este tutorial contém as seguintes partes:
- Etapa 1: criar um layout de texto.
- Etapa 2: defina uma classe derivada da interface IDWriteInlineObject.
- Etapa 3: implementar a classe de objeto embutido.
- Etapa 4: criar uma instância da classe InlineImage e adicioná-la ao Layout de Texto.
Etapa 1: criar um layout de texto.
Para começar, você precisará de um aplicativo com um objeto IDWriteTextLayout . Se você já tiver um aplicativo que exibe texto com um layout de texto, pule para a Etapa 2.
Para adicionar um layout de texto, você deve fazer o seguinte:
Declare um ponteiro para uma interface IDWriteTextLayout como um membro da classe .
IDWriteTextLayout* pTextLayout_;
No final do método CreateDeviceIndependentResources, crie um objeto de interface IDWriteTextLayout chamando o método CreateTextLayout .
// 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. ); }
Em seguida, você deve alterar a chamada para o método ID2D1RenderTarget::D rawText para ID2D1RenderTarget::D rawTextLayout, conforme mostrado no código a seguir.
pRT_->DrawTextLayout( origin, pTextLayout_, pBlackBrush_ );
Etapa 2: defina uma classe derivada da interface IDWriteInlineObject.
O suporte para objetos embutidos no DirectWrite é fornecido pela interface IDWriteInlineObject. Para usar objetos embutidos, você deve implementar essa interface. Ele manipula o desenho do objeto embutido, bem como fornece métricas e outras informações para o renderizador.
Crie um novo arquivo de cabeçalho e declare uma interface chamada InlineImage, derivada de IDWriteInlineObject.
Além de QueryInterface, AddRef e Release herdados de IUnknown, essa classe deve ter os seguintes métodos:
Etapa 3: implementar a classe de objeto embutido.
Crie um novo arquivo C++, chamado InlineImage.cpp, para a implementação da classe. Além do método LoadBitmapFromFile e dos métodos herdados da interface IUnknown, a classe InlineImage é composta pelos métodos a seguir.
O Construtor.
InlineImage::InlineImage(
ID2D1RenderTarget *pRenderTarget,
IWICImagingFactory *pIWICFactory,
PCWSTR uri
)
{
// Save the render target for later.
pRT_ = pRenderTarget;
pRT_->AddRef();
// Load the bitmap from a file.
LoadBitmapFromFile(
pRenderTarget,
pIWICFactory,
uri,
&pBitmap_
);
}
O primeiro argumento do construtor é o ID2D1RenderTarget para o qual a imagem embutida será renderizada. Isso é armazenado para uso posterior ao desenhar.
O destino de renderização, IWICImagingFactory e o uri filename são todos passados para o método LoadBitmapFromFile que carrega o bitmap e armazena o tamanho do bitmap (largura e altura) na variável de membro rect_.
O método Draw.
O método Draw é um retorno de chamada chamado pelo objeto IDWriteTextRenderer quando o objeto embutido precisa ser desenhado. O layout de texto não chama esse método diretamente.
HRESULT STDMETHODCALLTYPE InlineImage::Draw(
__maybenull void* clientDrawingContext,
IDWriteTextRenderer* renderer,
FLOAT originX,
FLOAT originY,
BOOL isSideways,
BOOL isRightToLeft,
IUnknown* clientDrawingEffect
)
{
float height = rect_.bottom - rect_.top;
float width = rect_.right - rect_.left;
D2D1_RECT_F destRect = {originX, originY, originX + width, originY + height};
pRT_->DrawBitmap(pBitmap_, destRect);
return S_OK;
}
Nesse caso, desenhar o bitmap é feito usando o método ID2D1RenderTarget::D rawBitmap ; no entanto, qualquer método para desenho pode ser usado.
O método GetMetrics.
HRESULT STDMETHODCALLTYPE InlineImage::GetMetrics(
__out DWRITE_INLINE_OBJECT_METRICS* metrics
)
{
DWRITE_INLINE_OBJECT_METRICS inlineMetrics = {};
inlineMetrics.width = rect_.right - rect_.left;
inlineMetrics.height = rect_.bottom - rect_.top;
inlineMetrics.baseline = baseline_;
*metrics = inlineMetrics;
return S_OK;
}
Para o método GetMetrics , armazene a largura, a altura e a linha de base em uma estrutura DWRITE_INLINE_OBJECT_METRICS . IDWriteTextLayout chama essa função de retorno de chamada para obter a medida do objeto embutido.
O método GetOverhangMetrics.
HRESULT STDMETHODCALLTYPE InlineImage::GetOverhangMetrics(
__out DWRITE_OVERHANG_METRICS* overhangs
)
{
overhangs->left = 0;
overhangs->top = 0;
overhangs->right = 0;
overhangs->bottom = 0;
return S_OK;
}
Nesse caso, nenhuma saliência é necessária, portanto, o método GetOverhangMetrics retorna todos os zeros.
O método GetBreakConditions.
HRESULT STDMETHODCALLTYPE InlineImage::GetBreakConditions(
__out DWRITE_BREAK_CONDITION* breakConditionBefore,
__out DWRITE_BREAK_CONDITION* breakConditionAfter
)
{
*breakConditionBefore = DWRITE_BREAK_CONDITION_NEUTRAL;
*breakConditionAfter = DWRITE_BREAK_CONDITION_NEUTRAL;
return S_OK;
}
Etapa 4: criar uma instância da classe InlineImage e adicioná-la ao Layout de Texto.
Por fim, no método CreateDeviceDependentResources, crie uma instância da classe InlineImage e adicione-a ao layout de texto. Como ele contém uma referência ao ID2D1RenderTarget, que é um recurso dependente do dispositivo, e o ID2D1Bitmap é criado usando o destino de renderização, o InlineImage também depende do dispositivo e deve ser recriado se o destino de renderização for recriado.
// Create an InlineObject.
pInlineImage_ = new InlineImage(pRT_, pWICFactory_, L"img1.jpg");
DWRITE_TEXT_RANGE textRange = {14, 1};
pTextLayout_->SetInlineObject(pInlineImage_, textRange);
O método IDWriteTextLayout::SetInlineObject usa uma estrutura de intervalo de texto. O objeto se aplica ao intervalo especificado aqui e qualquer texto no intervalo será suprimido. Se o comprimento do intervalo de texto for 0, o objeto não será desenhado.
Neste exemplo, há um asterisco (*) como um espaço reservado na posição em que a imagem será exibida.
// The string to display. The '*' character will be suppressed by our image.
wszText_ = L"Inline Object * Example";
cTextLength_ = wcslen(wszText_);
Como a classe InlineImage depende do ID2D1RenderTarget, você deve liberá-la quando liberar o destino de renderização.
SafeRelease(&pInlineImage_);