Qual formato de imagem tem mais eficiência de memória: PNG, JPEG ou GIF?


Respostas:


153

"Memória" e "eficiência" são termos mal utilizados, por isso darei uma resposta para quatro elementos diferentes que podem afetar o desempenho do seu jogo.

Estarei simplificando muitas coisas demais para mantê-lo curto e conciso, mas há toneladas de imprecisões neste texto abaixo; portanto, leve-o com uma pitada de sal. No entanto, os principais conceitos devem ser compreensíveis.

Armazenamento

Esse é o tamanho que suas imagens consomem na sua distribuição de software. Quanto mais espaço seus recursos consumirem, mais longo será o download (como no seu site). Se você estiver distribuindo em mídia física, como CDs ou DVDs, provavelmente precisará fazer algumas otimizações sérias nessa frente.

Em geral, o JPEG compacta melhor para fotografias e imagens sem bordas nítidas. No entanto, suas imagens terão qualidade degradada porque o JPEG usa compactação com perdas (você pode ajustar o nível / degradação da compactação ao exportar imagens como JPEG. Consulte a documentação do software de imagem para obter mais informações sobre isso).

No entanto, por melhor que seja o JPEG, ele não suporta transparência . Isso é crucial se você deseja que as imagens sejam exibidas através de outras pessoas ou se deseja imagens com formas irregulares. O GIF é uma boa opção, mas foi amplamente substituído pelo PNG (há apenas algumas coisas que o GIF suporta que o PNG não suporta, mas elas são irrelevantes na programação de jogos).

O PNG suporta transparência (e semitransparência), compacta dados sem degradar a qualidade (ou seja, usa compactação sem perdas) e compacta razoavelmente bem, mas não tanto quanto JPG.

O problema surge quando você precisa de boa compactação e transparência. Se você não se importa com imagens levemente degradadas, pode usar programas de quantização PNG, como pngquant , que você pode testar on-line no TinyPNG . Lembre-se de que a degradação da imagem realizada por quantização é diferente da do JPEG (que inclui quantização e outras técnicas agressivas), portanto, tente as duas com uma grande variedade de configurações.

Se você quiser minimizar agressivamente o tamanho da sua distribuição, poderá processar manualmente todas as imagens como esta:

if the image has transparency then
    try pngquant on the image
    if the results are not satisfactory then
        revert to the non-quantized image
    end
    store as PNG
else
    try storing it as JPG with different quality settings
    if no single setting yields an image of an acceptable quality then
        try pngquant on the image
        if the results are not satisfactory then
            revert to the non-quantized image
        end
        store as PNG
    else
        store as JPG
    end
end

Dica: não há problema em armazenar algumas imagens em um formato e outras em outro formato.

Existem outros formatos especializados, como DXT, ETC e PVRTC. Eles suportam compactação e também podem ser carregados compactados na memória, mas são suportados apenas por GPUs específicas, e a maioria dessas GPUs suporta apenas uma delas, portanto, a menos que você saiba as especificações exatas de hardware do hardware de destino (um caso notável é iPhone / iPad, que suporta texturas PVRTC), evite esses formatos.

Memória de programa

Eu incluí aqui, porque é isso que é comumente conhecido por "memória". No entanto, se seu jogo usa aceleração de gráficos (e se você está criando um jogo depois de 1998, provavelmente é), a única coisa que consumirá memória são os descritores de textura (apenas alguns bytes por imagem), que são apenas efetuada pela quantidade de imagens, e não pelo tamanho ou formato (isso tem algumas ressalvas, mas é principalmente irrelevante).

Se sua plataforma não possui memória de vídeo dedicada, não é acelerada por hardware ou outros casos incomuns, a próxima seção sobre VRAM ocorrerá total ou parcialmente na RAM, mas os princípios principais serão os mesmos.

Memória de Vídeo

É aqui que suas imagens serão armazenadas quando o programa estiver em execução. Em geral, o formato em que você os armazenou não fará diferença aqui, pois todas as imagens são descompactadas antes de carregá-las na memória de vídeo.

Agora, a VRAM consumida por suas imagens será aproximadamente width * height * bitdepth para cada imagem carregada na VRAM. Há algumas coisas a serem observadas aqui:

  1. A largura e a altura em que suas imagens são armazenadas no VRAM não coincidem necessariamente com as da sua imagem original. Algumas GPUs podem lidar apenas com texturas com tamanhos em potências de 2; portanto, sua imagem de 320x240 pode realmente ser armazenada em um espaço de 512x256 na VRAM, com a memória não utilizada efetivamente desperdiçada. Algumas vezes, você nem tem permissão para carregar texturas com tamanhos que não são potências de 2 (como no GLES 1.1).

    Portanto, se você deseja minimizar o uso da VRAM, considere atlasar suas imagens e dimensioná-las com potências de 2, o que também terá a vantagem de menos alterações no estado de renderização ao renderizar. Mais sobre isso mais tarde.

  2. A profundidade de bits é muito importante. Normalmente, as texturas são carregadas na VRAM em ARGB de 32 bits ou XRGB de 32 bits, mas se o seu hardware suportar profundidades de 16 bits e você não se importar de ter uma profundidade de bits menor, poderá metade da quantidade de VRAM consumida por cada imagem , o que pode ser algo interessante a considerar.

  3. Mas não importa o que você faça, o fator mais importante ao considerar a quantidade de VRAM usada pelo seu jogo é a quantidade de imagens que você tem na VRAM em um determinado momento. Este é o número que você provavelmente deseja manter o mais baixo possível, se quiser um jogo com bom desempenho. Carregar e descarregar texturas no VRAM é caro, portanto você não pode carregar cada imagem sempre que a usar. Você deve encontrar um equilíbrio entre pré-carregar as imagens que provavelmente usará e descarregá-las quando tiver certeza de que não as usará mais. Fazer esse direito não é trivial, e você deve pensar em sua própria estratégia para o seu jogo em particular.

Velocidade de execução

Mesmo que não seja "memória", está muito relacionado ao desempenho nos jogos. Desenhar imagens é caro e você deseja garantir que sua renderização seja executada o mais rápido possível. Obviamente, aqui, o formato não importa, mas outras coisas fazem:

  1. Tamanho da imagem (na verdade, seria "tamanho de amostra"): quanto maior a região de uma imagem que você vai desenhar, mais tempo levará para desenhá-la. A renderização de uma imagem enorme em uma pequena seção da tela não é muito eficaz; portanto, existe uma técnica chamada mipmapping , que consiste em trocar a VRAM pela velocidade de renderização, armazenando suas imagens várias vezes em várias resoluções e usando a menor que pode oferecer você a qualidade necessária a qualquer momento. O mapeamento de mip pode ser executado quando as imagens são carregadas, o que afetará a velocidade de carregamento e o uso da VRAM, ou no pré-processamento (armazenando manualmente versões diferentes da mesma imagem ou usando um formato que ofereça suporte nativo ao mipmap, como DDS), o que afetará o armazenamento e uso de VRAM, mas terá pouco impacto na velocidade de carregamento.

  2. Renderizar alterações de estado. Você provavelmente desejará desenhar várias imagens diferentes na tela ao mesmo tempo. No entanto, a GPU pode usar apenas uma única imagem de origem a qualquer momento (isso não é verdade, mas tenha paciência comigo aqui). A imagem usada atualmente para renderização é um dos muitos estados de renderização e é cara. Portanto, se você usar a mesma imagem várias vezes (lembra quando mencionei atlas de textura ?), Notará um enorme ganho de desempenho se reutilizar uma imagem o máximo que puder antes de alterar o estado de renderização e começar a usar um imagem diferente (existem outros estados de renderização além disso, e o ajuste fino da ordem em que você desenha suas coisas para minimizar as alterações no estado de renderização é uma atividade muito comum ao aprimorar o desempenho de um jogo)

No entanto , a otimização do uso da imagem é um tópico muito complexo, e o que escrevi aqui é uma visão geral muito ampla e simplificada de alguns dos fatores que você precisará considerar ao escrever um jogo. Por isso, acho que é definitivamente melhor se você simplificar, e otimize apenas quando você realmente precisar. Na maioria das vezes, a otimização prematura é desnecessária (e às vezes até prejudicial), portanto, vá com calma.


25
+1. Esta é uma resposta extremamente abrangente que cobre uma enorme quantidade de terreno. Não tenho certeza de que o tamanho dos descritores de textura exigisse tanto espaço quanto ele recebeu, e a menção DXT / ETC / PVRTC deve incluir realmente que cada um desses funciona apenas em algumas plataformas - eles não são abertos em várias plataformas padrões que são utilizáveis ​​em qualquer lugar, como JPEG, PNG e GIF. Mas no geral, muito impressionado. Isso é muito mais do que apenas "uma pequena anotação" em cima da minha resposta! : D
Trevor Powell

9
@PandaPajama: Talvez não, mas é uma resposta surpreendentemente melhor do que essa pergunta merece.
Marcks Thomas

11
+1 e mais se eu pudesse. Essa é uma resposta impressionante e deve ser um ponto de referência padrão para qualquer pergunta sobre "eficiência da memória", pois desmascara tão efetivamente o mito comum de que usar menos memória é sempre a melhor abordagem, e não apenas as imagens. Um item que eu sugeriria acrescentar é que, no caso não acelerado, se alguma descompressão imediata for necessária ao desenhar, isso pode significar uma sobrecarga inaceitável de desempenho.
Maximus Minimus

2
Gostaria de sugerir PNGGauntlet . Ele pode fazer grandes otimizações sem perdas para tamanhos de arquivo PNG, que podem ser adicionadas, especialmente quando você tem muitos sprites. Infelizmente, é apenas o Windows, mas as alternativas estão listadas na página inicial de outros sistemas operacionais.
orlp

11
@DavidDimalanta Não tenho experiência com libgdx, mas, como posso ver na documentação setEnforcePotImages, desabilita a imposição de poder de texturas de tamanho 2 para o OpenGLES 1.0. Essa não é uma boa ideia, pois nem todo hardware suporta texturas que não sejam de potência 2. O OpenGLES 2.0 requer suporte para texturas que não sejam de potência 2, portanto, se você estiver direcionando para 2.0, poderá usar texturas de qualquer tamanho. Para mais informações, consulte a documentação da libgdx.
Panda Pyjama

26

Depois que uma imagem é carregada do disco e formatada para renderização, ela usa a mesma quantidade de memória, independentemente de a imagem ter sido salva no disco usando PNG, JPEG ou GIF.

Regra geral: JPEG é um formato com perda e diminui a qualidade da imagem para diminuir a imagem no disco. PNG, por outro lado, é um formato de imagem sem perdas e, portanto, normalmente resulta em tamanhos de arquivo maiores no disco. O GIF também é tecnicamente um formato sem perdas, mas suporta apenas um máximo de 256 cores por imagem; portanto, uma imagem de alta cor geralmente gera uma perda de alta qualidade se salva como GIF.

Isso é apenas para a representação em disco, no entanto. Na memória, os dois se expandem para o mesmo formato de textura, usando a mesma quantidade de memória, independentemente de você os ter salvado no disco como PNG, JPEG, JPEG ou GIF.


Portanto, não é a extensão do arquivo que varia o tamanho da memória, mas o tamanho do arquivo?
Tredecies Nocturne

Nem. O tamanho da memória é baseado nas dimensões e profundidade da imagem. Qualquer compactação no arquivo de imagem é removida quando a imagem é carregada na memória para ser desenhada na tela. (Porque GPUs não pode tornar PNGs ou JPEGs ou etc. As imagens são expandidos para fora em pixmaps genéricos para que GPUs pode lidar com eles.)
Trevor Powell

2
Isso não é 100% verdade. As GPUs mais modernas (incluindo as incorporadas) suportam o carregamento e o armazenamento de texturas compactadas na memória gráfica, sendo os formatos DXT, ETC e PVRTC os mais comuns no mundo incorporado. Obviamente, você teria que armazenar suas texturas nesses formatos em primeiro lugar, em vez de PNG / JPG / GIF.
Panda Pyjama

6
A pergunta foi uma escolha explícita entre PNG / JPG / GIF, nenhum dos quais é suportado nativamente em nenhuma GPU que eu conheça. O solicitante original estava tendo problemas suficientes com a distinção entre o tamanho do arquivo no disco e o espaço ocupado na RAM, e senti que adicionar mais formatos de arquivo apenas confundiria a questão fundamental. Sinta-se livre para fornecer sua própria resposta, no entanto.
Trevor Powell

2
Meu comentário é apenas uma anotação. Se eu desse uma resposta, provavelmente teria escrito o mesmo que você, além do meu comentário como uma declaração final. Como não tenho mais nada a acrescentar, evitarei a repetição fornecendo minha própria resposta.
Panda Pyjama

3

Depende.

JPEG é mais eficiente para fotografias. Não é sem perdas, mas os artefatos introduzidos pela compactação são menos visíveis nesse caso de uso.

PNG é sem perdas e mais eficiente para pixel-art com linhas nítidas e poucas cores. Ele também suporta transparência alfa.

O GIF não pode fazer nada O PNG não pode fazer melhor, exceto pela capacidade de armazenar animações. Mas isso é relevante apenas no contexto de aplicativos da web. No desenvolvimento de jogos, você geralmente cria animações usando uma planilha.

Observe que, quando você usa um mecanismo gráfico como o Libgdx, provavelmente descompacta as imagens logo após carregá-las e as mantém na memória como valores RGBA não compactados. Portanto, o formato da imagem importa apenas para a velocidade de carregamento e tinha espaço em disco (ou uso de largura de banda quando você os envia pela rede).


2

Eu não sei muito sobre libgdx, mas sobre formatos de imagem e gráficos:

O JPEG é muito bom em casos de fotos do mundo real. Eles estão com perdas, mas você não verá artefatos nas fotos, a menos que tire fotos de bordas nítidas com espaços coloridos simples, como, por exemplo, texto escrito ou quadrinhos. Use-os para grandes gráficos de fundo.

O GIF é obsoleto, só pode armazenar cores paletizadas (até 8 bits por pixel) com uma cor dedicada para total transparência. Permite pequenas animações baseadas em quadros. Uma vez houve uma patente em seu algoritmo de empacotamento para que ele não pudesse ser usado em qualquer lugar legalmente. Por causa dessa patente, o PNG foi desenvolvido.

PNG é mais ou menos um bitmap compactado que pode armazenar RGB + alfa (até 32 bits) e outros formatos de pixel. É especializado para descompactar rapidamente pequenas partes dessa imagem, o que é conveniente para dispositivos muito pequenos e lentos (como um celular de 10 anos), mas as bibliotecas atuais apenas as descompactam em bitmaps quando carregadas.

PNG é melhor que GIF em tamanho, velocidade e recursos, mas se você deseja armazenar bitmaps com eficiência, sugiro: .PNM.BZ2 ([editar] Devido ao método diferente de empacotamento, .PNM.BZ2 nem sempre é mais eficiente que .PNG. [/ editar])

PNM / PBM / PGM / PAM são formatos de bitmap simples com cabeçalhos KISS em texto simples. O uso do gzip nesses arquivos resultará em um tamanho de arquivo semelhante ao PNG; portanto, o bzip2 é a melhor solução para isso. Se você usará bitmaps internamente em seu programa, convém usar bitmaps compactados bzip2 em um contêiner .tar ou .zip. Se você não possui o bzip2, o uso de PNMs em um contêiner zip (zip com compactação máxima) pode ser semelhante ao uso de PNGs. - Portanto, armazenar PNGs em um arquivo ZIP pode ter apenas um benefício pequeno ou nenhum - provavelmente aumentaria o tempo para carregar a imagem.

Além disso, é uma boa opção armazenar vários sprites / fotos em um bitmap, especialmente quando você precisar deles juntos na mesma situação.


O arquivo PNM está disponível e legível para todos os SOs de smartphones e SOs de computadores de mesa?
David Dimalanta

11
@ David Dimalanta: Não sei onde é suportado e onde não é. Se você deseja programar com PNMs, raramente deseja usar qualquer biblioteca, porque esse formato é muito simples para escolher qualquer API. Por isso, é legível e gravável com todas as linguagens de programação existentes que suportam a leitura / gravação de arquivos binários. Claro que você pode usar o Netpbm em qualquer sistema operacional, não apenas no Unix. Por outro lado, não sei quais bibliotecas de imagens gerais não suportam isso. Como não é um formato compactado, raramente é usado para armazenar imagens em disco. veja en.wikipedia.org/wiki/Netpbm_format
comonad

1

Como formato de armazenamento, o JPEG é provavelmente a melhor escolha para algumas texturas, como grama ou paredes, onde a perda de informações é provavelmente indetectável. Seguido por PNG quando você precisar de transparência ou quando não puder pagar com perda de informações, por exemplo, sprites em um jogo 2D (jogador, inimigos, baú do tesouro), você provavelmente desejará usar PNG para essas imagens.

Quando se fala em custo de memória, o formato usado para armazenar gráficos de jogos no sistema de arquivos não é relevante. Se você armazena buffers de pixel em VRAM ou RAM (renderizador de software), provavelmente os armazena sem compressão, porque os jogos favorecem a leitura rápida de pixels versus memória usada por cada buffer de pixel.

Os dados compactados armazenados na memória não fazem sentido, exceto que você mantém algum tipo de cache para salvar as leituras do disco, mas provavelmente precisará ler esse cache para um estado descompactado para as imagens em uso em um determinado momento do seu jogo.

Os dados de imagem compactada têm um pouco mais de sentido se fosse possível uma rápida decodificação de hardware. Pelo menos no mapeamento normal, lembro-me deste http://en.wikipedia.org/wiki/3Dc . Com isso você pode economizar VRAM. Ainda não estou ciente de outros exemplos de decodificação de hardware.

Em resumo: quaisquer que sejam os formatos usados ​​para o seu jogo para armazenar gráficos em armazenamento persistente, você precisará decodificar e manter uma versão não compactada na memória dinâmica, na memória de vídeo ou em ambas, para poder renderizá-las rapidamente quando necessário.

Finalmente: eu sou um cara da área de trabalho. Quando digo "memória", sempre me refiro à memória dinâmica. Quando digo "disco", "sistema de arquivos" ou "armazenamento persistente", sempre me refiro ao que o seu dispositivo usa como armazenamento persistente, geralmente penso em discos rígidos. Quando você disse "eficiência da memória", aceitei "memória dinâmica" e não "armazenamento persistente". Ultimamente, vejo muitas pessoas usando a palavra "memória" para se referir a "armazenamento persistente" (talvez a terminologia de dispositivos móveis?).


Também é possível que o JPEG e o PNG não afetem a memória, mas o tamanho do arquivo?
Tredecies Nocturne

Sim. Você escolherá qualquer um dos formatos discutidos pensando em economizar espaço em disco ou largura de banda. Ao carregar imagens no jogo, qualquer biblioteca / mecanismo que eu conheço os decodificará em buffers de pixels não compactados (pense em buffers de pixels como uma matriz de valores RGB ou RGBA, onde cada canal normalmente ocupa 1 byte em RAM / VRAM se estiver usando 32 bits por pixels, é provavelmente o que todo mundo está fazendo).
Hatoru Hansou 31/01
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.