SVG feComposite: compare arte com in, out e xor
Compare entradas de filtro SVG com in, out e xor, teste ordem dos operandos e alfa parcial e preserve os caminhos quando uma abertura visual não é geometria editável.
Nesta página
O SVG feComposite combina duas entradas renderizadas. Para entradas totalmente opacas, in mantém a primeira na sobreposição, out a mantém fora da sobreposição e xor combina ambas fora da sobreposição compartilhada. Essas operações mudam a pintura na renderização. Não reescrevem os caminhos de origem em uma interseção ou diferença editável.
Essa distinção importa quando uma sobreposição parece uma abertura, mas a entrega precisa de contornos reais. O exemplo abaixo compara duas formas assimétricas originais, inverte as entradas e verifica transparência parcial. Mantenha a arte de origem junto do resultado filtrado.
Nomeie as duas entradas antes de escolher o operador
A referência de feComposite da MDN descreve uma operação por pixel no espaço da imagem. in nomeia a primeira entrada; in2, a segunda. Dê nomes explícitos aos resultados intermediários para que uma mudança na cadeia de filtros não altere os operandos silenciosamente.
Para interiores opacos, estas três operações são fáceis de comparar:
| Operação | Primeira entrada A, segunda B | O que muda ao inverter |
|---|---|---|
in | Pintura de A na sobreposição | A sobreposição passa a ter a pintura de B |
out | Pintura de A fora de B | A pintura de B fora de A é outra região |
xor | As duas entradas fora da sobreposição opaca compartilhada | Permanecem as mesmas regiões sem sobreposição |
O mesmo atributo operator também aparece em outras primitivas de filtro com valores diferentes. Explicite o contexto de composição; a referência de operator da MDN o distingue das operações de morfologia.

Alinhe a segunda arte explicitamente
Nosso primeiro operando A é um bloco em degraus verde-petróleo. B é uma pipa inclinada ocre. Ambos usam o mesmo viewBox 0 0 240 160. B ultrapassa a caixa delimitadora local de A, portanto usar os limites de A como região do filtro seria uma suposição inicial ruim.
O filtro usa filterUnits="userSpaceOnUse" para a região explícita x=0, y=0, width=240, height=160. A referência de filterUnits da MDN explica essa escolha de coordenadas. Também definimos primitiveUnits="userSpaceOnUse" e damos à segunda entrada as mesmas coordenadas do quadro inteiro.
B é uma URL de dados SVG independente passada por feImage, com o nome de resultado operandB. Isso o traz ao filtro como entrada de imagem; não insere o caminho de B como novo filho direto da arte filtrada. A referência de feImage da MDN descreve esse comportamento de entrada de imagem.
O filtro escolhe explicitamente sRGB. O padrão dessas primitivas é linearRGB, como documenta a referência de feComposite. Nem esse padrão nem o nome do operador fornecem uma operação de edição de caminhos.
Execute a comparação original completa
Salve como starter.html e sirva a pasta por HTTP local, por exemplo com python3 -m http.server 8766 --bind 127.0.0.1. Abra http://127.0.0.1:8766/starter.html; use outra porta livre se necessário.
A página cria dois originais mais sete casos filtrados em cada um de dois tamanhos explícitos. Cada ID de filtro inclui o caso e a largura, para que vários SVGs inline no mesmo documento tenham referências únicas. Cada link Save SVG mantém o filtro independente e sua entrada B codificada.
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<title>Original SVG composite artwork</title>
<style>
body { margin: 24px; font: 16px system-ui; background: #f6f2e8; }
main { display: grid; grid-template-columns: repeat(3, 260px); gap: 18px; }
figure { margin: 0; }
svg { display: block; background: white; border: 1px solid #bbb; }
figcaption { margin: 6px 0; }
pre { white-space: pre-wrap; font-size: 12px; }
</style>
<h1>Original operands and rendered composites</h1>
<main id="cases"></main>
<pre id="report"></pre>
<script>
const A = 'M24 24 H132 V56 H192 V136 H24 Z';
const B = 'M92 8 L216 84 L92 152 L60 84 Z';
const svgNS = 'http://www.w3.org/2000/svg';
const cases = [
{ id: 'a' }, { id: 'b' },
{ id: 'a-in-b', operator: 'in' },
{ id: 'a-out-b', operator: 'out' },
{ id: 'a-xor-b', operator: 'xor' },
{ id: 'b-in-a', operator: 'in', reverse: true },
{ id: 'b-out-a', operator: 'out', reverse: true },
{ id: 'b-xor-a', operator: 'xor', reverse: true },
{ id: 'a-out-half-b', operator: 'out', opacity: 0.5 },
];
function documentSVG(test, width) {
const height = width * 2 / 3;
const id = test.id + '-' + width;
const second = '<svg xmlns="' + svgNS + '" width="240" height="160" viewBox="0 0 240 160">'
+ '<path d="' + B + '" fill="#d4a53a" opacity="' + (test.opacity ?? 1) + '"/></svg>';
const href = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(second);
const first = test.reverse ? 'operandB' : 'SourceGraphic';
const other = test.reverse ? 'SourceGraphic' : 'operandB';
const defs = test.operator ? '<defs><filter id="' + id + '" filterUnits="userSpaceOnUse" primitiveUnits="userSpaceOnUse" x="0" y="0" width="240" height="160" color-interpolation-filters="sRGB">'
+ '<feImage href="' + href + '" x="0" y="0" width="240" height="160" preserveAspectRatio="none" result="operandB"/>'
+ '<feComposite in="' + first + '" in2="' + other + '" operator="' + test.operator + '"/></filter></defs>' : '';
return '<svg xmlns="' + svgNS + '" width="' + width + '" height="' + height + '" viewBox="0 0 240 160">'
+ defs + '<path d="' + (test.id === 'b' ? B : A) + '" fill="' + (test.id === 'b' ? '#d4a53a' : '#176b5b')
+ '"' + (test.operator ? ' filter="url(#' + id + ')"' : '') + '/></svg>';
}
async function run() {
const results = [];
for (const width of [120, 240]) {
for (const test of cases) {
const source = documentSVG(test, width);
const svg = new DOMParser().parseFromString(source, 'image/svg+xml').documentElement;
const figure = document.createElement('figure');
const caption = document.createElement('figcaption');
caption.textContent = test.id + ', ' + width + ' × ' + width * 2 / 3;
figure.append(document.importNode(svg, true), caption);
document.getElementById('cases').append(figure);
const path = figure.querySelector('path');
const bbox = path.getBBox();
const image = new Image();
image.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(source);
await image.decode();
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = width * 2 / 3;
const ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0);
const sample = (x, y) => [...ctx.getImageData(Math.floor(x * width / 240), Math.floor(y * width / 240), 1, 1).data];
const hash = [...new Uint8Array(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(path.getAttribute('d'))))]
.map(v => v.toString(16).padStart(2, '0')).join('');
const download = document.createElement('a');
download.href = image.src;
download.download = test.id + '-' + width + '.svg';
download.textContent = 'Save SVG';
figure.append(download);
results.push({ id: test.id, width, height: canvas.height, d: path.getAttribute('d'), dSHA256: hash,
bbox: { x: bbox.x, y: bbox.y, width: bbox.width, height: bbox.height },
directPaths: svg.querySelectorAll('path').length, filterImages: svg.querySelectorAll('feImage').length,
aOnly: sample(40, 40), bOnly: sample(200, 84), overlap: sample(120, 84), outside: sample(230, 150) });
}
}
document.getElementById('report').textContent = JSON.stringify({ A, B, results }, null, 2);
}
run();
</script>
</html>O código mantém o mesmo caminho A direto mesmo quando as entradas do operador são invertidas. Por exemplo, b-in-a muda feComposite para in="operandB" e in2="SourceGraphic"; não substitui o elemento de origem filtrado por B. Isso torna visível a ordem dos operandos sem alterar a geometria A subjacente.
A inspeção de pixels aqui lê um canvas pintado a partir de cada SVG gerado. A entrega disponível para download continua sendo o SVG filtrado. Não é um novo caminho booleano nem uma conversão PerfectVector.
Compare pintura e geometria separadamente
Executamos exatamente essa página em um ambiente Chrome a 120 × 80 e 240 × 160, salvamos os 18 SVGs e os reabrimos como imagens no mesmo navegador. Os tamanhos exibidos são entradas explícitas, não uma afirmação sobre dois dispositivos físicos.
Três posições de amostras internas usam o sistema de coordenadas original: só A (40,40), só B (200,84) e sobreposição (120,84). Observamos os seguintes valores alfa nos dois tamanhos com operandos opacos:
| Entradas de filtro e operador | Alfa só A | Alfa só B | Alfa da sobreposição |
|---|---|---|---|
A in B | 0 | 0 | 255 |
A out B | 255 | 0 | 0 |
A xor B | 255 | 255 | 0 |
B in A | 0 | 0 | 255 |
B out A | 0 | 255 | 0 |
B xor A | 255 | 255 | 0 |
As linhas in compartilham uma sobreposição opaca, mas diferem na cor: A primeiro ficou verde-petróleo (23,107,91,255); B primeiro ficou ocre (212,165,58,255). A amostra externa (230,150) permaneceu transparente em todos os casos. São pixels internos selecionados, não um teste completo de cobertura de borda ou compatibilidade.
Cada arquivo filtrado ainda tinha a string d original exata de A e SHA-256 correspondente. Seu getBBox() geométrico local continuou x=24, y=24, width=168, height=112. Essa caixa descreve o caminho de origem; não é a caixa delimitadora dos pixels visíveis do filtro. O caminho B codificado também permaneceu inalterado. Portanto, o resultado de um filtro pode parecer diferente enquanto os dois contornos criados continuam intactos.
Os originais salvos têm um caminho direto e nenhuma imagem de filtro. Cada arquivo filtrado tem um caminho A direto mais um feImage que carrega o SVG separado de B, de um caminho. Nenhum elemento raster image comum é introduzido. Uma operação renderizada no espaço da imagem e operandos vetoriais de origem podem coexistir; só o inventário da origem não significa que o resultado se tornou geometria combinada editável.
Opacidade parcial muda o significado de uma abertura
O último caso torna B meio opaco, mantém A opaco e aplica A out B. A sobreposição não ficou vazia. Nos dois tamanhos, o RGBA observado foi (22,106,90,127): verde-petróleo parcialmente transparente, com arredondamento do processo de renderização e leitura.
Para out, o alfa de saída é o primeiro alfa multiplicado por um menos o segundo alfa. Para in, é o produto deles. A definição de composição Filter Effects define essas operações com cores pré-multiplicadas e alfa. B opaco pode remover a pintura sobreposta de A; B parcialmente transparente deixa parte dela.
Examine o resultado sobre o fundo real. Uma região clara sobre branco pode ser pintura translúcida em vez de um furo totalmente transparente. Pixels de borda com antisserrilhamento também precisam de inspeção visual; a amostra interna não promete que todo pixel de limite tenha alfa zero. Para uma tarefa separada de remapeamento de opacidade, veja o guia de limiar alfa SVG.
Entregue um efeito de filtro ou crie contornos reais
Mantenha esse filtro quando o renderizador receptor deve reproduzir a composição visual. Preserve os arquivos A e B originais e examine o SVG filtrado reaberto no destino real. Nossa reabertura bem-sucedida no navegador não certifica outros renderizadores ou máquinas de produção.
Se a entrega exige contornos combinados selecionáveis ou uma abertura pronta para corte, execute e examine uma edição real de caminhos ou operação booleana no fluxo pretendido. A pintura visível alterada aqui não cria essa geometria. O guia de clipPath versus máscara faz uma distinção relacionada de visibilidade; o guia de desagrupamento SVG ajuda quando a arte exibida não é o objeto editável esperado.
Se seu clipart existe só como pixels e você precisa de formas de origem editáveis, prepare um SVG de clipart com o PerfectVector, examine contornos e aberturas e mantenha o original mestre. Aplique e verifique o filtro ou a operação de caminhos pretendidos separadamente. A vetorização não transforma a sobreposição visual deste filtro em um contorno de fabricação verificado.
Perguntas frequentes
feComposite out apaga a geometria do caminho sobreposto? Não. Ele muda a pintura renderizada usando o alfa da segunda entrada. Nos nossos arquivos filtrados, os dados originais do caminho A e os limites geométricos locais permaneceram inalterados mesmo onde a pintura sobreposta ficou transparente.
Por que trocar in e in2 muda o resultado? Eles identificam a primeira e a segunda entradas. In mantém a pintura da primeira dentro da opacidade da segunda; out mantém a primeira fora dela. Nossos casos in invertidos compartilham a sobreposição opaca, mas mostram cores diferentes; os out invertidos mantêm regiões diferentes.
xor sempre cria uma sobreposição completamente vazia? A sobreposição interna totalmente opaca ficou vazia nos nossos casos xor. Alfa parcial e bordas com antisserrilhamento exigem inspeção própria; diagramas de conjuntos de formas opacas não descrevem todo pixel translúcido.
Posso enviar a abertura filtrada diretamente como caminho de corte editável? Um resultado visual de filtro não é uma nova geometria booleana criada. Mantenha as formas originais, crie e examine os contornos exigidos no fluxo de caminhos pretendido e verifique separadamente a aplicação receptora e os requisitos de produção.
Fontes
- MDN: feComposite — Papéis das entradas no espaço da imagem, operadores e espaço de cor padrão do filtro.
- MDN: operator — Valores de operadores de composição e o contexto separado de morfologia.
- MDN: filterUnits — Sistema de coordenadas da região do filtro.
- MDN: feImage — Entrada de imagem no processo de filtros.
- W3C: Composição Filter Effects — Definições de composição de cor pré-multiplicada e alfa.
Começando com seu próprio clipart raster? Crie um original SVG editável, examine contornos e aberturas e preserve-o enquanto confere no destino a composição visual escolhida ou o resultado de caminhos criado separadamente.
Mais do blog

Recorte um SVG permanentemente, sem caminhos ocultos
Recorte preenchimentos SVG com interseções reais, preserve cores separadas e uma abertura e confira no arquivo a geometria que um clip apenas esconderia.

Inkscape: Object to Path ou Stroke to Path?
Compare Object to Path e Stroke to Path do Inkscape no mesmo círculo. Veja qual mantém um traço editável, qual cria uma faixa preenchida e o que conferir depois.
