Lançamento de uma nova versão do ImageMagick
Na Transloadit, queremos oferecer a você a melhor experiência de codificação possível, e um dos principais fatores para isso é garantir que estamos usando as ferramentas de codificação mais recentes.
Não é segredo que dependemos fortemente de tecnologia de código aberto para isso e, por isso, fazemos um esforço consciente para retribuir aos gigantes sobre cujos ombros estamos, por exemplo, fornecendo hardware gratuito ao projeto ImageMagick.
Embora tenhamos uma relação próxima com o projeto, modernizar nosso stack tem se mostrado muito problemático para nós.
As partes ‘divertidas’ de manter software de codificação
Muitas vezes, nossos clientes dependem do comportamento da versão antiga (até mesmo dos bugs dela,
mas falaremos disso mais adiante), então “simplesmente atualizar”, algo que ingenuamente tentamos
fazer alguns anos atrás, quebra o fluxo de trabalho deles. Para garantir uma experiência consistente
mesmo entre atualizações do sistema operacional, precisamos compilar
o ImageMagick e todas as suas 31 dependências. Sim, 31, porque ao adicionar bibliotecas como
pixman, pango e ufraw, ganhamos suporte a 131
formatos a mais do que um apt-get install imagemagick padrão ofereceria no Ubuntu. E ao
incluir bibliotecas até o nível de zlib, também nos protegemos de qualquer quebra de
retrocompatibilidade que possa ocorrer em decorrência de atualizações do sistema operacional,
permitindo assim que nossos recursos de codificação sejam a constante confiável sobre a qual nossos
clientes podem construir seus negócios.
Além do amplo suporte a formatos e dessa continuidade garantida, a vantagem de compilar é que isso permite criar um binário estático. Trata-se de um único arquivo executável com todas as dependências embutidas. Dessa forma, fica fácil executar versões muito diferentes do mesmo software lado a lado, sem conflitos, oferecendo aos nossos clientes um caminho de atualização opcional (opt-in). Foi assim que oferecemos atualizações do nosso software de codificação de vídeo.
O problema que enfrentamos agora ao atualizar nosso stack de imagens, porém, é que o processo de compilar o ImageMagick e suas 31 dependências levaria duas horas, e isso em condições perfeitas, em um servidor robusto. Se a versão de uma biblioteca entrasse em conflito com outra, talvez você só descobrisse depois de 90 minutos, e então teria de pensar em uma possível correção e tentar tudo de novo. Isso dificilmente pode ser considerado viável ou sustentável e certamente não corresponde ao nível de agilidade que almejamos no nosso desenvolvimento.
Achávamos que a introdução dos contêineres Docker resolveria parte dessa
dificuldade, já que poderíamos ao menos executar versões diferentes lado a lado com mais facilidade
e acelerar nossos tempos de build com o uso de camadas. Além disso, em vez de recompilar enormes
binários únicos, poderíamos recompilar apenas uma única biblioteca compartilhada quando necessário.
Nossa alegria inicial diminuiu quando descobrimos que não havia como iniciar contêineres sem
privilégios de sistema perigosos. Isso significava que ou teríamos de manter os contêineres em
execução e deixar nosso usuário de API sem privilégios encaminhar comandos a eles via proxy, ou
aceitar conceder a esse usuário o privilégio de iniciar contêineres. Nenhuma das opções era ótima,
para dizer o mínimo. O proxy seria bastante frágil e, ao conceder esses privilégios, um invasor
poderia, em teoria, montar qualquer diretório da máquina host como um --volume dentro do contêiner
e então ler/gravar/excluir o que quisesse. Mais tarde, também soubemos do trabalho que estava sendo
feito no projeto runC para iniciar
contêineres sem acesso root (e provavelmente adotaremos essa
abordagem no futuro), mas ele ainda não tinha o nível de maturidade de que precisávamos.
Também consideramos executar um cluster separado para a nova versão, mas isso traria custos sérios em termos de hardware adicional, manutenção e sobrecarga operacional.
Exploramos a fundo todas essas direções, mas acabamos tendo de recuar todas as vezes, pois havia desvantagens sérias. Como deveria ser o futuro da manutenção do nosso stack? Que solução é ao mesmo tempo confiável e madura o suficiente para nos dar continuidade garantida sem perder nossa agilidade?
Nix
Há muito tempo, também vínhamos considerando o Nix como uma possível solução. Temos compilado nosso software com um framework de compilação simples chamado depmake, que o Felix escreveu em 2012 e que já implementava algumas das ideias e qualidades básicas do Nix. Ele tem funcionado muito bem para nós ao longo de todos esses anos, mas o Nix leva essas e outras ideias muito mais longe.
O Nix é uma linguagem de programação funcional que permite definir como compilar software de forma declarativa. Ao usar um cache global, você só precisa compilar o que é exclusivo do seu ambiente, mas, em muitos casos, pode simplesmente obter o software resultante direto desse cache. Isso pode melhorar muito os tempos de build pelo simples fato de que não há compilação *acena com a mão* 👋
Depois de bastante aprendizado e experimentação, descobrimos como o Nix e o depmake poderiam coexistir, para que possamos começar a usar o novo sem jogar fora o antigo.
Futuro
Usar o Nix realmente abre as portas para novos softwares em potencial e vai tornar extremamente fácil para nós lançar um novo stack, personalizá-lo, garantir que continue funcionando da mesma forma para sempre e permitir que várias versões do mesmo software coexistam lado a lado.
A maior parte do trabalho de oferecer um novo stack aos nossos clientes passou agora da compilação para os testes, o que é uma grande melhoria.
Ainda estamos considerando empacotar nosso software compilado com Nix em contêineres e executá-los sem root, mas isso serviria principalmente para oferecer segurança adicional além do isolamento que já oferecemos.
Algum dia vamos descontinuar nossas versões antigas? Sim. Mas faremos isso com períodos de carência bastante longos, durante os quais incentivaremos gentilmente as pessoas a migrar para o novo.
Sem mais delongas… versão dois!
Temos muito orgulho de apresentar nosso novo stack do ImageMagick, apelidado de v2.0.3 (não segue
SemVer; qualquer alteração pode potencialmente quebrar a retrocompatibilidade). Ele já está em beta
privado há duas semanas e agora entra em beta público. Ele é baseado na versão mais recente do
ImageMagick que o Nix fornece, mas com acréscimos personalizados, como libraw, para que nossos
clientes continuem aproveitando esses 131 formatos extras.
Além do RAW e de todos os outros formatos que já suportamos, os clientes ficarão felizes em saber que o novo stack traz suporte aos formatos WebP e DjVu.
Atualizamos nossa página de formatos suportados para que você possa descobrir com mais facilidade as diferenças entre as versões do nosso stack. A captura de tela abaixo mostra os controles de comparação no momento deste lançamento.

Para sua conveniência, aqui estão as listas completas:
Passou a ter suporte de leitura: 3FR, 3G2, 3GP, CANVAS, CLIP (antes somente gravação), FILE, FTP, HALD, HTTP, IIQ, JNX, MAC, MEF, NRW, PES, RAW, RMF, RW2, SCREENSHOT, WMF, WMZ.
Passou a ter suporte de gravação: BRF, DDS (antes somente leitura), INLINE (antes somente leitura), ISOBRL, ISOBRL6, JSON, SPARSE, UBRL, UBRL6
Passou a ter suporte de leitura/gravação: AAI, BGRA, BGRO, CAL, CALS, DXT1, DXT5, G4, GROUP4, HDR, JPE, JPS, MASK, MKV, PNG00, PNG48, PNG64, PSB, RGF, SIX, SIXEL, TIFF64, VIPS, WEBP
Além de mais formatos, nossa nova versão do ImageMagick também ativa o HDRI (High Dynamic Range Imaging) por padrão, então você pode esperar resultados de processamento de imagens mais precisos.
Além dessas melhorias voltadas aos clientes, estamos bastante satisfeitos com o fato de a nova versão também trazer alguns benefícios operacionais para nós, como a correção de várias vulnerabilidades de segurança, vazamentos de memória e assim por diante.
Mudanças incompatíveis notáveis
Nossa versão anterior do ImageMagick tinha um bug no tratamento de espaço de cores, que resultava em imagens mais escuras e na identificação incorreta de sRGB versus RGB. Isso foi corrigido, mas, se você encontrou uma forma de contornar esse bug, na prática você depende dele e, portanto, precisará desfazer essa compensação quando optar por atualizar.
Não oferecemos mais suporte à leitura de arquivos PGX e JPX e, para os formatos BRG, GBR, GRB e RBG, agora é recomendado criar um formato RGB e depois trocar os canais.
Para referência, você encontra o registro de alterações completo no final deste post.
Como atualizar
Esta seção descreve o lançamento de 2017. O stack
v2.0.3foi descontinuado em 2019. Para uma nova integração, use o stack recomendado na documentação atual do /image/resize.
A Transloadit recomenda começar a usar o novo stack do ImageMagick para testes e cargas de trabalho
não críticas. Você pode fazer isso adicionando imagemagick_stack: "v2.0.3" ao seu Step do Robot
/image/resize.
Pedimos desculpas por termos demorado tanto para entregar esta atualização. Você pode esperar atualizações muito mais rápidas graças à nossa nova forma de gerenciar o stack! 😄
Adoraríamos saber o que você descobriu, então conte para nós o que achou pelo conhecido balão de conversa no nosso site!
Registro de alterações
Para referência, aqui está o registro de alterações completo:
- Adicionadas verificações extras ao leitor de DCM para evitar falhas causadas por dados (relato de bug de Hanno Böck)
- Adicionado
ClampToQuantumfinal no loop do mapa de cores sigmoidal - Adicionado suporte a idiomas que exigem layout de texto complexo (referência)
- Adicionado clone tanh/atanh do mapa sigmoidal legado (mais rápido e mais preciso)
- Adicionado o define
psd:additional-infopara preservar as informações adicionais em um arquivo PSD - Adicionado o define
psd:preserve-opacity-maskpara preservar a máscara de opacidade em um arquivo PSD - Adicionada compressão RLE de camadas ao codificador PSD
- Adicionada compressão RLE de camadas ao codificador PSD
- Adicionado suporte à compressão GROUP4 ao coder FAX
- Adicionado suporte a RGB555, RGB565, ARGB4444 e ARGB1555 ao codificador BMP (referência)
- Permitido o uso de set e escapes quando não há imagens na memória (a menos que você tente acessar
metadados por imagem). Atualmente não inclui
%[fx:...]e%[pixel:...] - Aplicados patches do Debian (referência)
- Recuo (backoff) do épsilon de precisão finita (referência)
- Incrementada a versão do shared object (SO) do Magick++. Antes, uma substituição global alterou
matteColorparaalphaColor - É possível ler metadados EXIF relacionados a geolocalização novamente (referência)
- Verificação de estouro de buffer em
magick/draw.c/DrawStrokePolygon() - Travessia de caminho em coders não é autorizada (relato de bug fornecido por Masaaki Chida)
- coders/png.c: adicionado suporte a um novo chunk PNG proposto (exIf leitura e gravação, eXIf somente leitura) que está sendo discutido atualmente na lista de discussão png-mng-misc at lists.sourceforge.net
- coders/png.c: adicionado suporte a um novo chunk PNG proposto (zxIf, somente leitura) que está
sendo discutido atualmente na lista de discussão png-mng-misc at lists.sourceforge.net. Ative exIf
e zxIf com
CPPFLAGS="-DexIf_SUPPORTED -DxzIf_SUPPORTED". Se exIf estiver ativado, somente o chunk exIF não compactado será gravado, e o chunk zTXt codificado em hexadecimal contendo o perfil Exif bruto não será gravado. - Correção de instabilidade numérica (referência)
- Correção no operador de composição
Over(referência) - Define a máscara
CompositeChannelsparaRed,Green,Blue,AlphaeBlack - Leituras indiretas são negadas pela política; remova a política para permiti-las, por exemplo,
convert caption:@mytext.txt - O distort não converte mais a imagem
grayscaleemsRGB(referência) - Não fechar o caminho para linejoins do tipo round (referência)
- Documentada a mudança de comportamento na política de segurança (agradecimentos a yoya)
- Não interpretar argumentos da opção
-fx(referência) - Não retornar uma caixa delimitadora zerada para
QueryMultilineFontMetrics()(referência) - Não definir o fundo para imagens transparentes lado a lado (tiled) (referência)
- Não definir o update trait no canal alfa (e-mail privado sobre a opção
-levels-colors) - Não sincronizar o cache de pixels em
AcquireAuthenticCacheView()(relato de bug de Hanno Böck) - Eliminado aviso do compilador
- Ativar o canal alfa se a cor de fundo não for opaca (referência)
- Avaliar de forma lazy a morfologia do cache de pixels para evitar estouro de buffer (relato de bug de Ibrahim M. El-Sayed)
- Corrigido erro de compilação em
opencl.c(referência) - Corrigida falha de desenho para larguras de traço maiores que 2 (referência)
- Correção de possíveis vulnerabilidades de segurança (referência)
- Corrigido erro off-by-one em
GetNextToken() - Corrigido vazamento de memória no formato MPC
- Corrigido o stroke-opacity do MVG (referência)
- Corrigida regressão do cache de pixels em disco (referência)
- Corrigido possível estouro de buffer ao gravar TIFFs compactados (relato de vulnerabilidade da Cisco Talos, CVE-2016-8707)
- Corrigida a redeclaração de i (no topo e dentro de uma condicional)
- Corrigido pequeno vazamento de memória (patch fornecido por Андрей Черный)
- Corrigido problema de deslocamento do traço para
-annotate(referência) - Corrigido vazamento de fd no coder WebP (referência)
- Corrigido o dimensionamento incorreto de certas imagens FITS (referência)
- Corrigido cálculo incorreto de padding no codificador PSD
- Corrigida análise incorreta com ordered dither (referência)
- Corrigida decodificação RLE incorreta ao ler uma imagem DCM que contém vários segmentos
- Corrigida decodificação RLE incorreta ao ler uma imagem SGI (referência)
- Corrigido problema em que a janela de exibição era usada em vez da janela de dados ao ler arquivos EXR (referência)
- Corrigido vazamento de memória ao criar exceções aninhadas no Magick++ (referência)
- Corrigido o posicionamento da anotação de texto para
east/westgravity - Corrigida a leitura de imagens DXT1 com canal alfa.
- Se não for encontrada uma quebra de linha conveniente, forçá-la para caption: (referência)
- Implementado um chunk PNG privado caNv (canvas) para lembrar as dimensões e os deslocamentos originais quando uma imagem é recortada. Antes, usávamos os chunks oFFs e vpAg para essa finalidade, mas isso tinha potenciais conflitos com outras aplicações que também usam o chunk oFFs
- Aumentada a alocação de memória para pixels TIFF (referência)
- Inicializar o membro alpha de draw_info como OpaqueAlpha
- Inicializar o canal de índice para obter os resultados esperados do coder stegano
- Iterar canais sobre a imagem de origem em vez da de destino (relato de bug de Hanno Böck)
- A composição com máscara produz resultados corretos para o utilitário
convert(referência) - Imagens monocromáticas não têm mais cores invertidas (referência)
- Considerar o canal alfa ao combinar 4 ou mais imagens (referência)
- Erro off-by-1 ao calcular o desvio padrão (referência)
- Alocação de memória off-by-one (referência)
- Patch para que a opção
-kuwaharapossa preservar bordas com mapa de cores - Permitir imagens EPT com apenas uma imagem TIFF ou EPS, não ambas (referência)
- Evitar estouro de buffer (relato de bug de Max Thrane)
- Evitar estouro de buffer e outros problemas nos coders SIXEL, PDB, MAP, TIFF e CALS (relato de bug de Donghai Zhu)
- Evitar estouro de buffer nos coders BMP e SGI (relato de bug de pwchen&rayzhong da tencent)
- Evitar estouro de buffer ao fazer streaming de uma imagem (referência)
- Evitar falha no interpretador MSL
- Evitar uso de memória após a liberação (referência)
- Evitar possível estouro de buffer ao ler imagens TIFF (relato de bug de Shi Pu, da MS509 Team)
- Evitar possível vulnerabilidade de injeção de comandos shell por meio do parâmetro authenticate dos coders PDF, PCL e XPS (relato de Erez Turjeman)
- Evitar dados de pixel aleatórios em imagem JPEG corrompida (relato de bug de Hirokazu Moriguchi, Sony)
- Evitar a remoção espúria de arquivos de cache MPC (referência)
- Processar canais de forma independente para
-channel-equalize(referência) - Ajustar automaticamente o caption de forma adequada (referência)
- Centralizar corretamente o rótulo de texto (referência)
- Inicializar corretamente os blocos PES (referência)
- Colocar senhas entre aspas quando passadas a um programa delegate.
- Em vez de replicar
optionsemartifacts, criar um link da imagem paraimage_infoe consultar uma opção global se nenhum artefato estiver definido. - Reconhecer tags de fechamento da política XML (referência)
- Removido o delegate https
- Removidas chamadas OpenMP dos loops de atualização do mapa de cores
- Removido o suporte ao coder interno ephemeral
- Renomeada a função
read_vpag_chunk_callback()parapng_user_chunk_callback()emcoders/png.c - Renderizar na máscara de recorte em vez da imagem para a primitiva gráfica MVG clip-path
- Substituir o título do delegate show pelo nome do arquivo da imagem em vez do rótulo
- Substituído
CoderSeekableStreamFlagporCoderDecoderSeekableStreamFlageCoderEncoderSeekableStreamFlag - Respeitar o define
connected-components:area-threshold(referência) - Respeitar a opção
gravity(referência) - Restaurada a opção
-mattecolor - Retornar o deslocamento correto para índice negativo na opção
-fx(referência) - Retornar o desvio padrão não enviesado nas estatísticas de imagem (referência)
- Revertido o patch que não definia o update trait no canal alfa
- Verificação RLE para deslocamento de pixel menor que 0 (relato de heap overflow de Craig Young)
- Sanitizar todos os caracteres de formatação incorporados em delegates
- Sanitizar comentários que incluem chaves no formato de imagem MIFF (referência)
- Sanitizar o nome do arquivo de entrada para os delegates http / https (patch aprimorado)
- Melhorias de segurança no coder TEXT o quebraram (referência)
- Definir o membro alpha da estrutura draw como
OpaqueAlpha(referência) - Definir o espaço de cores como
sRGBse-appendtiver espaços de cores não homogêneos (referência) - sigmoidal-contrast: cálculo direto, sem LUT
- sigmoidal-contrast: removido
ClampToQuantuminicial desnecessário - Suporte à opção
-region(referência) - Suporte à opção
--enable-indirect-readsdo script configure para ativar leituras indiretas (@) em nomes de arquivo - Suporte a
--enable-pipes optiondo script configure para ativar pipes (|) em nomes de arquivo - Suporte às políticas de segurança pixel-cache e shred
- Suporte a read-masks para a opção
-modulate - Suporte à opção
-read-maskdo compare - Suporte às opções
phash:colorspacesephash:normalize - A opção
-clonenão vaza mais memória - A opção
-extentagora corresponde aos resultados do IMv6 (referência) - A opção
-streamagora incrementa o ponteiro de pixel corretamente (referência) - O coder de histograma agora retorna a extensão correta
- Para cumprir o padrão SVG, usar
stroke-opacitypara traços transparentes - Desativar o canal alfa na imagem de diferença do compare (referência)
- Corrigida a compilação sem suporte a JPEG (referência)
- Dados não inicializados no formato de imagem MAT (referência)
- Os testes unitários voltam a passar após um pequeno patch de imagem SUN
- Usar
CopyMagickString()em vez deCopyMagickMemory()para strings - O teste unitário de validação para MNG funciona novamente
