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 interfaceIDWriteTextLayout.
O produto final deste tutorial é um aplicativo que exibe texto com uma imagem embutida nele, como 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 IDWriteTextLayout objeto. 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 IDWriteTextLayout interface 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, deves alterar a chamada para o método ID2D1RenderTarget::DrawText para ID2D1RenderTarget::DrawTextLayout, 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 lida com o desenho do objeto embutido ao mesmo tempo que fornece métricas e outras informações ao 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 inline.
Crie um novo arquivo C++, chamado InlineImage.cpp, para a implementação da classe. Além do método LoadBitmapFromFile e os métodos herdados da interface IUnknown, a classe InlineImage é composta dos seguintes métodos.
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 do nome do ficheiro são passados para o método LoadBitmapFromFile que carrega o bitmap e armazena as dimensões do bitmap (largura e altura) na variável membro rect_.
O método de Desenho.
O método Draw é um callback 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, o desenho do bitmap é feito usando o ID2D1RenderTarget::D rawBitmap método; no entanto, qualquer método de 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, altura e linha de base numa estrutura DWRITE_INLINE_OBJECT_METRICS. IDWriteTextLayout chama esta função de callback para obter a medida do objeto inline.
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.
Finalmente, 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 é dependente 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 IDWriteTextLayout::SetInlineObject método 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_);