PerfectVector
Por Irene Kim

SVG feComposite: compara diseños con in, out y xor

Compara entradas de filtros SVG con in, out y xor, prueba el orden de operandos y el alfa parcial, y conserva los trazados cuando una abertura visual no es geometría editable.

12 min de lectura
En esta página

feComposite de SVG combina dos entradas renderizadas. Para entradas totalmente opacas, in conserva la primera entrada en la superposición, out la conserva fuera de ella y xor combina ambas entradas fuera de su superposición compartida. Estas operaciones cambian la pintura al renderizar. No reescriben los trazados de origen como una intersección o diferencia editable.

Esa distinción importa cuando una superposición parece una abertura, pero la entrega necesita contornos reales. El ejemplo siguiente compara dos formas asimétricas originales, invierte las entradas y comprueba la transparencia parcial. Conserva el diseño de origen junto al resultado filtrado.

Nombra ambas entradas antes de elegir el operador

La referencia de feComposite de MDN describe una operación píxel a píxel en el espacio de imagen. in nombra la primera entrada; in2, la segunda. Da nombres explícitos a los resultados intermedios para que un cambio en la cadena de filtros no cambie silenciosamente los operandos.

Con interiores opacos, estas tres operaciones son fáciles de comparar:

OperaciónPrimera entrada A, segunda entrada BQué cambia al invertir las entradas
inLa pintura de A en la superposiciónLa superposición lleva la pintura de B en su lugar
outLa pintura de A fuera de BLa pintura de B fuera de A es otra región
xorAmbas entradas fuera de su superposición opaca compartidaPermanecen las mismas regiones sin superposición

El mismo atributo operator también aparece en otras primitivas de filtro con valores diferentes. Mantén explícito el contexto de composición; la referencia de operator de MDN lo distingue de las operaciones morfológicas.

Cambia la pintura visible
Comparación conceptual de diseños de color superpuestos y una composición con la superposición sin pintar
Ilustración: las entradas superpuestas pueden dejar una región sin pintar. Estas siluetas decorativas y flechas son conceptuales; los operadores exactos, las muestras alfa y los trazados sin cambios de abajo proceden de un archivo de prueba original ejecutado en el navegador.

Alinea explícitamente el segundo diseño

Nuestro primer operando A es un bloque escalonado turquesa. B es un cometa sesgado ocre. Ambos usan el mismo viewBox 0 0 240 160. B se extiende más allá del cuadro delimitador local de A, así que asumir los límites de A como región de filtro sería un mal punto de partida.

El filtro usa filterUnits="userSpaceOnUse" para su región explícita x=0, y=0, width=240, height=160. La referencia de filterUnits de MDN explica esa elección de coordenadas. También establecemos primitiveUnits="userSpaceOnUse" y damos a la segunda entrada las mismas coordenadas del marco completo.

B es una URL de datos SVG autónoma que se pasa mediante feImage, con el nombre de resultado operandB. Esto la introduce en el filtro como entrada de imagen; no inserta el trazado de B como un nuevo hijo directo del diseño filtrado. La referencia de feImage de MDN describe su comportamiento de entrada de imagen.

El filtro selecciona explícitamente sRGB. El valor predeterminado para estas primitivas es linearRGB, como documenta la referencia de feComposite. Ni ese valor ni el nombre del operador proporcionan una operación de edición de trazados.

Ejecuta la comparación original completa

Guarda esto como starter.html y sirve la carpeta mediante HTTP local, por ejemplo con python3 -m http.server 8766 --bind 127.0.0.1. Abre http://127.0.0.1:8766/starter.html; usa otro puerto libre si hace falta.

La página crea dos originales y siete casos filtrados en cada uno de dos tamaños explícitos. Cada ID de filtro incluye el caso y el ancho para que varios SVG inline del mismo documento tengan referencias únicas. Cada enlace para guardar SVG conserva el filtro autónomo y su 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>

El código conserva el mismo trazado A directo incluso cuando se invierten las entradas del operador. Por ejemplo, b-in-a cambia feComposite a in="operandB" e in2="SourceGraphic"; no sustituye el elemento de origen filtrado por B. Esto hace visible el orden de operandos sin alterar la geometría subyacente de A.

La inspección de píxeles de aquí lee un canvas pintado a partir de cada SVG generado. El entregable descargable sigue siendo el SVG filtrado. No es un nuevo trazado booleano ni una conversión de PerfectVector.

Compara pintura y geometría por separado

Ejecutamos exactamente esta página en un entorno Chrome a 120 × 80 y 240 × 160, guardamos los 18 SVG y volvimos a abrirlos como imágenes en el mismo navegador. Los tamaños mostrados son entradas explícitas, no una afirmación sobre dos dispositivos físicos.

Tres posiciones de muestra interiores usan el sistema de coordenadas original: solo A (40,40), solo B (200,84) y superposición (120,84). Se observaron los siguientes valores alfa en ambos tamaños con operandos opacos:

Entradas del filtro y operadorAlfa de solo AAlfa de solo BAlfa de la superposición
A in B00255
A out B25500
A xor B2552550
B in A00255
B out A02550
B xor A2552550

Las filas in comparten una superposición opaca, pero difieren en color: con A primero era turquesa (23,107,91,255); con B primero era ocre (212,165,58,255). La muestra exterior (230,150) siguió transparente en todos los casos. Son píxeles interiores seleccionados, no una prueba completa de cobertura de bordes ni de compatibilidad.

Cada archivo filtrado conservaba la cadena d original exacta de A y un SHA-256 coincidente. Su getBBox() geométrico local seguía siendo x=24, y=24, width=168, height=112. Ese recuadro describe el trazado de origen; no es el cuadro delimitador de los píxeles visibles del filtro. El trazado B codificado también seguía igual. Por tanto, un resultado de filtro puede verse diferente mientras ambos contornos creados permanecen intactos.

Los originales guardados tienen un trazado directo y ninguna imagen de filtro. Cada archivo filtrado tiene un trazado A directo y un feImage que lleva el SVG separado de un trazado de B. No se introduce ningún elemento image ráster normal. Una operación renderizada en espacio de imagen y unos operandos de origen vectoriales pueden coexistir; el inventario de origen por sí solo no significa que el resultado se haya convertido en geometría combinada editable.

La opacidad parcial cambia el significado de una abertura

El último caso hace B medio opaco mientras mantiene A opaco y después aplica A out B. La superposición no quedó vacía. En ambos tamaños, su RGBA observado fue (22,106,90,127): turquesa parcialmente transparente, con redondeo del proceso de renderizado y lectura.

Para out, el alfa de salida es el primer alfa multiplicado por uno menos el segundo. Para in, es su producto. La definición de composición de Filter Effects define estas operaciones con colores premultiplicados y alfa. B opaco puede eliminar la pintura superpuesta de A; B parcialmente transparente deja parte de ella.

Revisa el resultado sobre el fondo real. Una región clara sobre blanco puede ser pintura translúcida en lugar de un hueco completamente transparente. Los píxeles suavizados de los bordes también necesitan una revisión visual; la muestra interior no promete que todos los píxeles del límite tengan alfa cero. Para una tarea separada de reasignación de opacidad, consulta la guía de umbrales alfa SVG.

Entrega un efecto de filtro o crea contornos reales

Conserva este filtro cuando el renderizador receptor deba reproducir la composición visual. Guarda los archivos originales A y B y revisa el SVG filtrado reabierto en su destino real. Que lo hayamos reabierto correctamente en el navegador no certifica otros renderizadores ni máquinas de producción.

Si la entrega requiere contornos combinados seleccionables o una abertura lista para cortar, realiza e inspecciona una operación real de edición de trazados o booleana en el flujo previsto. La pintura visible modificada aquí no crea esa geometría. La guía de clipPath frente a mask plantea una diferencia relacionada de visibilidad; la guía de desagrupación SVG ayuda cuando el diseño mostrado no es el objeto editable que esperabas.

Si tu clipart solo existe como píxeles y necesitas formas de origen editables, prepara un SVG de clipart con PerfectVector, inspecciona los contornos y las aberturas y conserva el maestro. Aplica y verifica el filtro o la operación de trazado previstos por separado. La vectorización no convierte la superposición visual de este filtro en un contorno de fabricación verificado.

Preguntas frecuentes

¿feComposite out elimina la geometría del trazado superpuesto? No. Cambia la pintura renderizada usando el alfa de la segunda entrada. En nuestros archivos filtrados, los datos del trazado A original y sus límites geométricos locales no cambiaron, ni siquiera donde la pintura superpuesta quedó transparente.

¿Por qué intercambiar in e in2 cambia el resultado? Identifican la primera y la segunda entrada. In conserva la pintura de la primera dentro de la opacidad de la segunda; out conserva la primera fuera de ella. Nuestros casos in invertidos comparten la superposición opaca, pero muestran colores diferentes, mientras que los casos out invertidos conservan regiones distintas.

¿xor siempre deja la superposición completamente vacía? La superposición interior totalmente opaca quedó vacía en nuestros casos xor. El alfa parcial y los bordes suavizados necesitan su propia inspección; los diagramas de conjuntos de formas opacas no describen todos los píxeles translúcidos.

¿Puedo enviar directamente la abertura filtrada como trazado de corte editable? Un resultado visual de filtro no es nueva geometría booleana creada. Conserva las formas originales, crea e inspecciona los contornos requeridos en el flujo de trazados previsto y verifica por separado la aplicación receptora y los requisitos de producción.

Fuentes

  1. MDN: feComposite — Funciones de entrada en espacio de imagen, descripciones de operadores y espacio de color predeterminado del filtro.
  2. MDN: operator — Valores de operadores de composición y contexto morfológico separado.
  3. MDN: filterUnits — Sistema de coordenadas de la región del filtro.
  4. MDN: feImage — Entrada de imagen al proceso del filtro.
  5. W3C: Composición de Filter Effects — Definiciones de composición de color premultiplicado y alfa.

¿Partes de tu propio clipart ráster? Crea un maestro SVG editable, inspecciona sus contornos y aberturas, y consérvalo mientras revisas en el destino la composición visual elegida o el resultado de trazados creado por separado.

Más del blog

PerfectVector

Empieza con un SVG más limpio
y fácil de editar

Sin tarjeta de crédito