Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Este artigo explica a diferença entre pixels físicos e DIPs (pixels independentes do dispositivo) e como o DPI (pontos por polegada) é tratado no Win2D.
O Win2D foi projetado de forma que muitos aplicativos possam ignorar essa distinção, pois fornece comportamentos padrão sensatos que farão a coisa certa quando executados em dispositivos de DPI baixo e alto. Se o seu aplicativo tiver necessidades mais especializadas ou se você tiver uma opinião diferente sobre o que significa "padrão sensato", continue lendo para obter os detalhes sangrentos...
O que é DPI?
DPI significa "pontos por polegada". Esta é uma medida aproximada da densidade de pixels de uma exibição de saída, como um monitor de computador ou tela de telefone. Quanto maior o DPI, mais pontos menores compõem a tela.
O DPI é apenas uma medida aproximada porque nem todo hardware de exibição pode ser confiável para relatar informações precisas. Alguns monitores de computador não relatam DPI para o sistema operacional ou o usuário pode ter configurado seu sistema para renderizar usando um DPI diferente do hardware real (por exemplo, para alterar o tamanho dos elementos de texto da interface do usuário). Os aplicativos podem usar o DPI para escolher o tamanho das coisas que devem ser desenhadas, mas não devem confiar nele como uma medida física exata do tamanho da exibição.
Um valor de DPI de 96 é considerado um padrão neutro.
O que é um pixel?
Um pixel é um único ponto colorido. As imagens em computação gráfica são compostas de muitos pixels dispostos em uma grade bidimensional. Você pode pensar em pixels como os átomos a partir dos quais todas as imagens são construídas.
O tamanho físico de um pixel pode variar muito de uma tela para outra. Quando um computador está conectado a um monitor grande, mas de baixa resolução ou tela externa, os pixels podem ser bastante grandes, mas em um telefone com uma tela de 1080p com apenas alguns centímetros de diâmetro, os pixels são minúsculos.
No Win2D, sempre que você vir uma API que especifica uma posição ou tamanho usando tipos de dados inteiros (ou um struct como BitmapSize que contém inteiros), isso significa que a API está operando em unidades de pixel.
A maioria das APIs Win2D funciona com DIPs em vez de pixels.
O que é um DIP?
DIP significa "pixel independente do dispositivo". Esta é uma unidade virtualizada que pode ser igual ou menor que um pixel físico.
A proporção entre pixels e DIPs é determinada pelo DPI:
pixels = dips * dpi / 96
Quando o DPI é 96, os pixels e os DIPs são os mesmos. Ao usar DPI mais alto, um único DIP pode corresponder a mais de um pixel (ou partes de pixels no caso comum em que o DPI não é um múltiplo exato de 96).
A maioria das APIs do Tempo de Execução do Windows, incluindo Win2D, usa DIPs em vez de pixels. Isso tem a vantagem de manter os gráficos aproximadamente do mesmo tamanho físico, independentemente da tela em que um aplicativo é executado. Por exemplo, se um aplicativo especificar que um botão tem 100 DIPs de largura, quando executado em um dispositivo de alto DPI, como um telefone ou monitor 4k, esse botão será dimensionado automaticamente para ter mais de 100 pixels de largura, portanto, permanece um tamanho sensato que é possível para o usuário clicar. Se o tamanho do botão fosse especificado em pixels, por outro lado, ele pareceria ridiculamente pequeno nesse tipo de tela de alto DPI, então o aplicativo teria que fazer mais trabalho para ajustar os layouts de maneira diferente para cada tipo de tela.
No Win2D, sempre que você vir uma API que especifica uma posição ou tamanho usando tipos de dados de ponto flutuante (ou structs como Vector2 ou Size que contêm valores de ponto flutuante), isso significa que a API está operando em DIPs.
Para converter entre DIPs e pixels, use os métodos ConvertDipsToPixels(Single, CanvasDpiRounding)
e ConvertPixelsToDips(Int32)
.
Recursos Win2D que têm DPI
Todos os recursos Win2D que contêm uma imagem de bitmap também têm uma propriedade DPI associada:
CanvasBitmap
CanvasRenderTarget
CanvasSwapChain
CanvasControl
CanvasVirtualControl
CanvasAnimatedControl
CanvasImageSource
Todos os outros tipos de recursos são independentes do DPI. Por exemplo, uma única CanvasDevice
instância pode ser usada para desenhar para controles ou rendertargets de muitos DPIs diferentes, portanto, o dispositivo não tem DPI próprio.
Da mesma forma, CanvasCommandList
não tem um DPI, pois contém instruções de desenho vetorial em vez de uma imagem de bitmap. O DPI só entra em jogo durante o processo de rasterização, quando a lista de comandos é desenhada para um rendertarget ou controle (que tem DPI).
DPI de controle
Os controles Win2D (CanvasControl
e CanvasVirtualControl
CanvasAnimatedControl
) usam automaticamente o mesmo DPI que a exibição em que o aplicativo está sendo executado. Isso corresponde ao sistema de coordenadas usado por XAML, CoreWindow e outras APIs do Tempo de Execução do Windows.
Se o DPI for alterado (por exemplo, se o aplicativo for movido para uma exibição diferente), o controle gerará o CreateResources
evento e passará um CanvasCreateResourcesReason
de DpiChanged
. Os aplicativos devem responder a esse evento recriando todos os recursos (como rendertargets) que dependem da DPI do controle.
DPI do destino de renderização
As coisas que podem ser usadas (o que inclui não apenas CanvasRenderTarget
, mas também os tipos CanvasSwapChain
semelhantes a rendertarget e CanvasImageSource
) têm um DPI próprio, mas, ao contrário dos controles, esses tipos não estão diretamente conectados a uma exibição, portanto, o Win2D não pode determinar automaticamente qual deve ser o DPI. Se você estiver desenhando para um rendertarget que mais tarde será copiado para a tela, provavelmente deseja que esse rendertarget use o mesmo DPI da tela, mas se estiver desenhando para algum outro propósito (por exemplo, gerar imagens para upload em um site), um padrão de 96 DPI seria mais apropriado.
Para facilitar esses dois padrões de uso, o Win2D fornece dois tipos de sobrecarga de construtor:
CanvasRenderTarget(ICanvasResourceCreator, width, height, dpi)
CanvasRenderTarget(ICanvasResourceCreatorWithDpi, width, height)
A ICanvasResourceCreator
interface é implementada e CanvasDevice
os controles Win2D. Como um dispositivo não tem nenhuma DPI específica própria, você deve especificar explicitamente a DPI ao criar um rendertarget de um.
Por exemplo, para criar um destino de renderização DPI padrão em que DIPs e pixels sempre serão a mesma coisa:
const float defaultDpi = 96;
var rtWithFixedDpi = new CanvasRenderTarget(canvasDevice, width, height, defaultDpi);
ICanvasResourceCreatorWithDpi
estende-se ICanvasResourceCreator
adicionando uma propriedade DPI. Essa interface é implementada pelos controles Win2D e facilita a criação de um rendertarget que herdará automaticamente o mesmo DPI que o controle do qual foi criado:
var rtWithSameDpiAsDisplay = new CanvasRenderTarget(canvasControl, width, height);
Bitmap DPI
CanvasBitmap
, ao contrário de um rendertarget, não herda automaticamente o DPI de um controle. Os métodos para criar e carregar bitmaps incluem sobrecargas para especificar explicitamente o DPI, mas se você deixar isso de fora, o DPI do bitmap será padronizado para 96, independentemente da configuração de exibição atual.
A razão pela qual os bitmaps são diferentes de outros tipos é que eles são uma fonte de dados de entrada, em vez de uma saída que será desenhada. Portanto, o importante para bitmaps não é o DPI de onde essa saída terminará, mas o DPI da imagem de origem, que não está totalmente relacionado às configurações de exibição atuais.
Se você carregar, digamos, um bitmap de DPI padrão de 100x100 e, em seguida, desenhá-lo em um destino de renderização, o bitmap será dimensionado de 100 DIPs a 96 DPI (que é 100 pixels) a 100 DIPs no DPI do destino de renderização (que pode ser um número maior de pixels se for um destino de renderização de alto DPI). A imagem resultante sempre terá 100 DIPs de tamanho (portanto, não haverá surpresas desagradáveis no layout), mas poderá sofrer algum desfoque se um bitmap de origem de baixo DPI for dimensionado para um destino de DPI mais alto.
Para obter clareza máxima em alto DPI, alguns aplicativos podem querer fornecer vários conjuntos de imagens de bitmap em diferentes resoluções e, no momento do carregamento, selecionar a versão que mais se aproxima do DPI do controle de destino. Outros aplicativos podem preferir enviar apenas bitmaps de alto DPI e permitir que o Win2D os reduza verticalmente ao executar em exibições de DPI mais baixas (reduzir verticalmente geralmente pode parecer melhor do que dimensionar). Em ambos os casos, o DPI de bitmap pode ser especificado como um parâmetro para LoadAsync(ICanvasResourceCreator, String, Single)
.
Observe que alguns formatos de arquivo de bitmap contêm metadados DPI próprios, mas o Win2D ignora isso, pois geralmente é definido incorretamente. Em vez disso, o DPI deve ser especificado explicitamente ao carregar o bitmap.
CanvasDrawingSession DPI
CanvasDrawingSession
herda seu DPI de qualquer controle, rendertarget, swapchain, etc., em que esteja desenhando.
Por padrão, todas as operações de desenho operam em DIPs. Se você preferir trabalhar em pixels, isso pode ser alterado por meio da Units
propriedade.
DPI de efeito
O pipeline de efeitos de imagem herda seu DPI de qualquer efeito que CanvasDrawingSession
esteja sendo desenhado. Internamente, o processamento de efeitos sempre opera em pixels. Valores de parâmetros, como tamanhos ou posições, são especificados em DIPs, mas essas unidades são convertidas em pixels antes que qualquer manipulação real da imagem ocorra.
Quando um bitmap de DPI diferente da sessão de desenho de destino é usado como uma imagem de origem do efeito, um interno DpiCompensationEffect
é inserido automaticamente entre o bitmap e o efeito. Isso dimensiona o bitmap para corresponder ao DPI de destino, que geralmente é o que você deseja. Se não for o que você deseja, você pode inserir sua própria instância de DpiCompensationEffect
para personalizar o comportamento.
Observação
Se estiver implementando um efeito personalizado, considere aplicar um esquema de tratamento de DPI equivalente para garantir um comportamento consistente quando usado em conjunto com efeitos Win2D internos.
API de composição
As Microsoft.Graphics.Canvas.Composition
APIs operam em um nível inferior aos controles XAML do Win2D, portanto, elas não tentam lidar automaticamente com o DPI em seu nome. Cabe a você decidir em quais unidades prefere operar e definir as transformações necessárias para conseguir isso como parte de sua árvore visual de composição.
Windows.UI.Composition
APIs como CreateDrawingSurface
sempre especificam tamanhos em unidades de pixel. Ao usar o Win2D para desenhar em uma superfície de composição, você pode especificar qualquer DPI que deseja usar ao chamar CreateDrawingSession(CompositionDrawingSurface, Rect, Single)
. Todos os desenhos realizados através do retornado CanvasDrawingSession
serão ampliados ou reduzidos de acordo.
Como testar o tratamento de DPI
A maneira mais fácil de testar se seu aplicativo fará a coisa certa em resposta à alteração do DPI de exibição é executar no Windows 10 ou Windows 11 e alterar as configurações de exibição enquanto o aplicativo está em execução:
- Clique com o botão direito do mouse no plano de fundo da área de trabalho e escolha 'Configurações de exibição'
- Mova o controle deslizante rotulado "Alterar o tamanho do texto, aplicativos e outros itens"
- Clique no botão 'Aplicar'
- Escolha 'Sair mais tarde'
Se você não tiver o Windows 10 ou o Windows 11, também poderá testar com o Simulador do Windows. Na barra de ferramentas do Visual Studio, altere a configuração "Computador Local" para "Simulador" e use o ícone Alterar Resolução para alternar a exibição simulada entre:
- 100% (DPI = 96)
- 140% (DPI = 134,4)
- 180% (DPI = 172,8)
Windows developer