Voltar aos projetos
2026 · Ferramenta

Fix My Halo

Ferramenta web e CLI para Windows que remove artefatos de texturas com transparência.

  • Página inicial do Fix My Halo com área para soltar texturas, os formatos suportados, o controle de preenchimento de dilatação e o botão Save ZIP.
  • O aplicativo Fix My Halo ao lado do painel Como funciona, comparando um sprite com halo branco ao resultado dilatado, com explicações do problema e da solução.
  • Página da CLI com um construtor de comandos que exibe a chamada em PowerShell, campo de entrada com glob, controle de dilatação, alternadores recursivo e sobrescrever, e link de download para Windows.

O Fix My Halo automatiza a dilatação de textura, também conhecida como alpha bleeding, em sprites transparentes usados em jogos. Em alguns métodos de filtragem, a GPU combina os pixels visíveis da borda com valores RGB armazenados em pixels transparentes. Quando esses valores ocultos são brancos ou pretos, a interpolação cria um contorno ao redor do sprite.

A aplicação web processa os arquivos localmente no navegador, sem enviá-los para um backend. A CLI nativa oferece a mesma correção para arquivos individuais, diretórios e padrões glob.


🧩 Decisões técnicas

1. Um core em Rust para dois formatos de distribuição

Desafio: O navegador oferece o fluxo mais acessível, enquanto uma CLI nativa atende melhor projetos de mods com muitas texturas. Duas implementações permitiriam que o comportamento do processamento divergisse com o tempo.

Implementação: O engine.rs concentra a função process_image e as etapas de dilatação. O lib.rs expõe esse core por meio do wasm-bindgen: recebe os bytes da imagem e devolve bytes em PNG ao navegador. Já o main.rs usa o mesmo engine por trás de uma interface nativa construída com Clap.

Resultado:

  • Os dois formatos de distribuição usam o mesmo algoritmo e a mesma interpretação do padding.
  • O código específico de cada plataforma fica restrito à descoberta de arquivos, transferência de bytes, progresso e saída.

2. Alpha bleeding iterativo sem alterar a transparência

Desafio: A ferramenta precisa substituir os dados RGB problemáticos dentro de pixels totalmente transparentes sem mudar o formato visível do sprite nem suas bordas semitransparentes.

Implementação: Cada etapa da dilatação percorre os pixels totalmente transparentes e copia a cor do primeiro pixel não transparente encontrado entre os oito vizinhos. A repetição expande as cores da borda em um pixel por unidade de padding configurada. Ao final, o canal alpha original é restaurado em toda a imagem.

Resultado:

  • Apenas os dados de cor ocultos são alterados; a transparência original permanece intacta.
  • O processamento termina antes do limite quando outra etapa não produziria nenhuma mudança.

3. Processamento isolado no navegador e jobs paralelos na CLI

Desafio: O processamento de imagens não deve bloquear a interface em React, enquanto lotes grandes na CLI devem aproveitar as threads disponíveis da CPU.

Implementação:

  • Navegador: Um Web Worker inicializa o módulo Wasm, recebe cada job e transfere os buffers de entrada e saída sem copiá-los pela thread da interface.
  • CLI: Entradas por arquivo, diretório ou padrão glob são convertidas em jobs. O par_iter() do Rayon distribui esses jobs entre suas threads, enquanto a CLI exibe o progresso e um resumo ao final.

Resultado:

  • A interface continua atualizando o estado dos arquivos enquanto o Wasm processa uma imagem.
  • Lotes de diretórios, inclusive recursivos, processam vários arquivos ao mesmo tempo na CLI.

🏗️ Arquitetura e fluxos de uso

Rust foi escolhido principalmente para permitir que o navegador e a CLI reutilizassem a mesma implementação, não porque o algoritmo fosse inviável em JavaScript. A crate compartilhada é compilada com wasm-pack para o navegador e como executável nativo para processamento em lote.

Interface React -> buffer transferível -> Web Worker -> binding Wasm -> core Rust
Entrada CLI -> descoberta de arquivos -> jobs do Rayon ------------> core Rust

A interface em React oferece lotes por drag and drop, ajuste do raio de dilatação, status por arquivo, download individual e exportação em ZIP. A fila do navegador processa uma imagem por vez fora da thread principal; o paralelismo entre arquivos fica a cargo da CLI.

📦 Distribuição e verificação

A versão 0.2.0 oferece um executável para Windows x64. Depois de baixar o fixmyhalo.exe, ele pode processar um arquivo ou percorrer um diretório recursivamente:

.\fixmyhalo.exe "Textures\Player.png"
.\fixmyhalo.exe "C:\MeuMod\Textures" --recursive
  • Testes unitários em Rust cobrem a seleção do pixel vizinho, uma etapa de dilatação, padding com várias etapas, cores transparentes já corrigidas e imagens totalmente opacas.
  • O GitHub Actions gera o executável para Windows x64 sempre que uma tag de versão é enviada e anexa o arquivo à release correspondente.
  • A aplicação web oferece o fluxo sem instalação.

Depois que compartilhei o projeto com a comunidade de RimWorld, um modder relatou que ele removeu uma borda esbranquiçada de uma textura de bancada que não havia conseguido corrigir manualmente. O post de lançamento e a discussão trazem validação direta do público para o qual a ferramenta foi criada.

Escopo e limitações

O projeto não publica benchmarks entre plataformas. Por isso, as afirmações de desempenho se limitam a decisões observáveis na implementação: o processamento fica fora da thread da interface no navegador, e a CLI paraleliza os jobs por arquivo. O executável nativo disponível atualmente é voltado ao Windows x64.


🛠️ Tech stack

  • Core de processamento: Rust, image
  • Concorrência: Web Workers, buffers transferíveis, Rayon
  • Web: WebAssembly, React, TypeScript, Zustand
  • Interface: Tailwind CSS, Radix UI, Motion
  • Distribuição: Clap, GitHub Actions, Vercel
  • Rust
  • WebAssembly
  • React
  • TypeScript
  • Tailwind CSS