Otimizar PNGs com Oxipng pela CLI e em Rust
Para comprimir um PNG com o Oxipng mantendo o arquivo original, use --out com um nome de arquivo diferente.
O passo a passo abaixo mostra uma prévia do resultado, grava uma cópia otimizada e depois executa a mesma
operação a partir do Rust. Ele usa PNGs comuns, não animados, e mantém desativadas as transformações
opcionais com perdas.
A otimização de PNG sem perdas pode alterar a compressão, a filtragem, o layout da paleta e a representação de cores sem alterar os valores de pixel decodificados. Portanto, um hash de arquivo diferente não significa que a imagem perdeu qualidade. Isso também não prova que todos os metadados foram preservados. Mantenha o arquivo de origem quando precisar de um original para arquivamento.
Instalar o Oxipng
Estes exemplos em Bash foram testados no Linux com Rust e Cargo 1.98.1 e Oxipng 10.2.1. Você precisa do Cargo
no seu PATH, de um compilador C e um linker para a dependência nativa de compressão e do diretório de binários
do Cargo no seu PATH. O Oxipng 10.2.1 declara
o Rust 1.88.0 como versão mínima.
cargo install oxipng --version 10.2.1 --locked &&
oxipng --version
O comando de versão deve exibir oxipng 10.2.1. Se ele exibir outra versão, verifique qual binário
o seu shell encontra antes de continuar. cargo install
compila em modo release por padrão; --locked usa o lockfile de dependências empacotado com o crate.
Otimizar um único PNG
Coloque o seu PNG no diretório atual como input.png. Primeiro, veja uma prévia da otimização sem gravar
nada:
oxipng -o 4 --dry-run -v -- input.png
--dry-run ainda executa o trabalho de otimização; apenas deixa de gravar o resultado. A versão 10
substituiu a antiga flag --pretend.
Para salvar o resultado separadamente e comparar os tamanhos dos arquivos:
oxipng -o 4 -v --out optimized.png -- input.png &&
wc -c input.png optimized.png
Isso deixa input.png intacto. Esse comando substitui um optimized.png existente sem perguntar, inclusive
em uma nova execução não interativa. Escolha um novo nome de destino se precisar manter uma saída anterior.
O diretório pai do destino já precisa existir.
A predefinição quatro é um ponto de partida, não uma economia garantida. Os níveis disponíveis vão de zero a seis; o padrão é dois. Predefinições mais altas dedicam mais trabalho à busca, mas não garantem um resultado menor. Um PNG já otimizado pode não ter nenhuma redução. Com essas configurações, uma saída separada ainda é gravada, usando os bytes originais quando o Oxipng não consegue melhorá-los. Sem um destino separado, esse caso deixa o arquivo de entrada intocado.
Por padrão, o Oxipng usa as CPUs lógicas disponíveis. Adicione --threads 4 para limitar as threads de trabalho em uma
máquina compartilhada. Consulte o manual versionado da CLI
para ver a lista completa de opções.
Escolher o que preservar
Três tipos diferentes de preservação importam aqui:
| Configuração | O que significa |
|---|---|
Sem --alpha | Mantém os valores RGB mesmo em pixels totalmente transparentes. Adicionar --alpha permite alterar essas cores ocultas para melhorar a compressão; o resultado é visualmente sem perdas, mas altera os dados de pixel. |
Sem --strip | Mantém os metadados PNG que continuam válidos após a otimização, sujeito às exceções abaixo. |
--preserve | Tenta manter as permissões do sistema de arquivos e a data de modificação. Isso não controla os metadados PNG nem mantém a data de acesso. |
As opções da biblioteca também mantêm desativadas por padrão a otimização
de alfa e a redução com perdas de 16 para oito bits. Não adicione --scale16 se precisar de
valores de pixel sem perdas.
A retenção padrão de metadados não é uma preservação exata para arquivamento. O Oxipng descarta bKGD, sBIT e
hIST se uma mudança de tipo de cor ou de profundidade de bits os invalidar. Ele também remove, por padrão, os chunks C2PA caBX e
os chunks iDOT da Apple. Um perfil ICC incorporado pode ser recomprimido. Esses comportamentos são descritos
na implementação do tratamento de chunks
e no manual da CLI.
Use --strip safe somente quando pretender descartar informações auxiliares, como texto e EXIF.
Essa opção mantém chunks selecionados relacionados à exibição, incluindo sRGB e pHYs, mas não é uma promessa de
manter todos os metadados. --strip all vai além e pode remover informações de gerenciamento de cores, alterando
a forma como uma imagem é exibida. Nenhuma das opções é necessária para os exemplos aqui.
Se você quiser deliberadamente substituir a sua entrada mantendo as permissões do arquivo e a data de modificação, use:
oxipng -o 4 --preserve -- input.png
Esta operação altera o próprio arquivo de entrada: mantenha um backup antes de executá-la. A preservação de atributos é feita na base do melhor esforço; o Oxipng emite um aviso se não conseguir restaurar um atributo.
Processar uma pasta em lote
Para um diretório de arquivos PNG que você aceita substituir, execute:
find ./images -type f -name '*.png' -exec oxipng -o 4 --preserve -- {} +
Isso percorre diretórios aninhados e passa com segurança nomes de arquivo com espaços. O comando encontra extensões
.png em minúsculas e modifica os arquivos no próprio local; uma nova execução opera sobre esses mesmos arquivos. Adicione --dry-run
aos argumentos do Oxipng para ver uma prévia. Use uma cópia do diretório de arquivos quando precisar manter os originais.
Incorporar o Oxipng ao seu código Rust
Crie um novo projeto e fixe a mesma versão do crate:
cargo new --bin --vcs none png-optimize &&
cd png-optimize &&
cargo add oxipng@=10.2.1
Continue somente depois que isso funcionar. Um diretório png-optimize existente faz o cargo new falhar;
escolha um novo nome de projeto em vez de excluir um projeto existente. Mantenha o Cargo.lock gerado
para preservar as versões de dependências resolvidas.
Coloque uma cópia do seu PNG neste diretório do projeto como input.png. Substitua o src/main.rs gerado
por este programa completo:
use oxipng::{optimize, InFile, Options, OutFile};
use std::path::PathBuf;
fn main() -> Result<(), oxipng::PngError> {
let input = InFile::Path(PathBuf::from("input.png"));
let output = OutFile::from_path(PathBuf::from("output.png"));
let options = Options::from_preset(4);
let (before, after) = optimize(&input, &output, &options)?;
println!("{before} -> {after} bytes: output.png");
Ok(())
}
A partir de png-optimize, execute:
cargo run --release
O programa lê input.png em relação ao diretório atual, grava output.png e exibe as
contagens de bytes de entrada e de saída retornadas por
optimize. Um output.png existente é substituído
sem confirmação. Contagens iguais são um resultado válido, não uma falha.
OutFile::from_path escolhe um arquivo
separado e não solicita a preservação de atributos. Use OutFile::None para uma simulação (dry run) na biblioteca, ou
OutFile::Path { path: Some(PathBuf::from("output.png")), preserve_attrs: true } quando precisar que a saída tenha as
permissões e a data de modificação da entrada.
Tratar entradas com falha em um build
Uma entrada ausente ou malformada faz a CLI encerrar com status de falha. O exemplo em Rust propaga o
PngError por meio de ?, então ele também encerra com falha e não exibe as contagens de bytes de sucesso.
Execute-o uma vez sem input.png para ver esse caminho de erro. Um diretório de saída ausente ou um erro de
permissão de gravação também pode fazer a operação falhar.
Verifique o status de encerramento do processo antes de usar um arquivo de saída: uma saída antiga ainda pode existir depois de uma execução com falha. Uma otimização bem-sucedida também é diferente de conseguir uma redução de tamanho. Em um build de assets, trate falhas como erros e registre as contagens reais de bytes em vez de exigir uma economia fixa.
