SVG feComposite: เทียบภาพด้วย in, out และ xor
เทียบอินพุตฟิลเตอร์ SVG ด้วย in, out และ xor ทดสอบลำดับกับอัลฟาบางส่วน และเก็บเส้นต้นฉบับเมื่อช่องที่เห็นไม่ใช่รูปทรงแก้ไขได้
ในหน้านี้
SVG feComposite รวมอินพุตเรนเดอร์สองชุด สำหรับอินพุตทึบเต็ม in เก็บชุดแรกในส่วนทับกัน out เก็บนอกส่วนทับ และ xor รวมทั้งสองนอกส่วนทับร่วม คำสั่งเหล่านี้เปลี่ยนสีขณะเรนเดอร์ ไม่เขียนเส้นต้นฉบับใหม่เป็นจุดตัดหรือผลต่างที่แก้ไขได้
ความต่างสำคัญเมื่อส่วนทับดูเป็นช่อง แต่งานส่งต้องการเส้นขอบจริง ตัวอย่างด้านล่างเทียบรูปไม่สมมาตรต้นฉบับสองรูป สลับอินพุต และตรวจความโปร่งใสบางส่วน เก็บภาพต้นฉบับคู่ผลใส่ฟิลเตอร์
ตั้งชื่อสองอินพุตก่อนเลือกตัวดำเนินการ
เอกสาร feComposite ของ MDN อธิบายการทำงานทีละพิกเซลในพื้นที่ภาพ in ระบุอินพุตแรก in2 ระบุที่สอง ตั้งชื่อผลระหว่างทางชัดเจนเพื่อไม่ให้แก้สายฟิลเตอร์แล้วเปลี่ยนตัวถูกดำเนินการเงียบ ๆ
สำหรับพื้นที่ทึบด้านใน สามคำสั่งเทียบง่าย:
| คำสั่ง | อินพุตแรก A ที่สอง B | ผลเมื่อสลับอินพุต |
|---|---|---|
in | สี A ในส่วนทับ | ส่วนทับใช้สี B แทน |
out | สี A นอก B | สี B นอก A เป็นอีกบริเวณ |
xor | ทั้งสองนอกส่วนทับทึบร่วม | เหลือบริเวณไม่ทับเดิม |
แอตทริบิวต์ operator เดียวกันยังปรากฏในองค์ประกอบฟิลเตอร์อื่นด้วยค่าต่างกัน ระบุบริบทประกอบภาพให้ชัด เอกสาร operator ของ MDN แยกจากคำสั่ง morphology

จัดภาพที่สองให้ตรงอย่างชัดเจน
ตัวถูกดำเนินการแรก A เป็นบล็อกขั้นบันไดสีเขียวน้ำทะเล B เป็นว่าวเอียงสีเหลืองดิน ทั้งคู่ใช้ viewBox 0 0 240 160 B ยื่นเกินกรอบเฉพาะที่ของ A การใช้ขอบเขต A เป็นพื้นที่ฟิลเตอร์จึงเป็นสมมติฐานตั้งต้นไม่ดี
ฟิลเตอร์ใช้ filterUnits="userSpaceOnUse" กับพื้นที่ชัดเจน x=0, y=0, width=240, height=160 เอกสาร filterUnits ของ MDN อธิบายตัวเลือกพิกัด เราตั้ง primitiveUnits="userSpaceOnUse" และให้อินพุตที่สองพิกัดเต็มกรอบเดียวกันด้วย
B เป็น SVG data URL ครบในตัวที่ส่งผ่าน feImage พร้อมชื่อผล operandB สิ่งนี้นำเข้าเป็นภาพของฟิลเตอร์ ไม่ได้แทรกเส้น B เป็นลูกโดยตรงใหม่ของภาพรับฟิลเตอร์ เอกสาร feImage ของ MDN อธิบายอินพุตภาพ
ฟิลเตอร์เลือก sRGB ชัดเจน ค่าเริ่มต้นขององค์ประกอบเหล่านี้คือ linearRGB ตามเอกสาร feComposite ทั้งค่าเริ่มต้นและชื่อคำสั่งไม่ให้การแก้เส้น
รันการเทียบต้นฉบับครบชุด
บันทึกเป็น starter.html และให้บริการโฟลเดอร์ผ่าน HTTP ในเครื่อง เช่น python3 -m http.server 8766 --bind 127.0.0.1 เปิด http://127.0.0.1:8766/starter.html ใช้พอร์ตว่างอื่นหากจำเป็น
หน้าสร้างต้นฉบับสองชุดกับกรณีฟิลเตอร์เจ็ดชุดที่แต่ละขนาดชัดเจนสองขนาด ID ฟิลเตอร์แต่ละตัวรวมกรณีและความกว้าง เพื่อให้หลาย SVG inline ในเอกสารเดียวอ้างอิงไม่ซ้ำ ลิงก์ Save SVG แต่ละอันเก็บฟิลเตอร์ครบในตัวกับอินพุต B เข้ารหัส
<!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>โค้ดเก็บเส้น A โดยตรงเดิมแม้สลับอินพุต เช่น b-in-a เปลี่ยน feComposite เป็น in="operandB" และ in2="SourceGraphic" ไม่ได้แทนองค์ประกอบต้นทางที่ใส่ฟิลเตอร์ด้วย B จึงเห็นลำดับโดยไม่เปลี่ยนรูปทรง A พื้นฐาน
การตรวจพิกเซลอ่าน canvas ที่วาดจาก SVG สร้างแต่ละชุด งานดาวน์โหลดยังเป็น SVG ใส่ฟิลเตอร์ ไม่ใช่เส้นบูลีนใหม่หรือการแปลง PerfectVector
เทียบสีกับรูปทรงแยก
เรารันหน้านี้ตรง ๆ ใน Chrome หนึ่งสภาพแวดล้อมที่ 120 × 80 และ 240 × 160 บันทึก SVG ทั้ง 18 และเปิดเป็นภาพใหม่ในเบราว์เซอร์เดิม ขนาดแสดงเป็นอินพุตชัดเจน ไม่ใช่ข้อกล่าวอ้างอุปกรณ์จริงสองเครื่อง
จุดตัวอย่างด้านในสามจุดใช้ระบบพิกัดเดิม ได้แก่ A เท่านั้น (40,40) B เท่านั้น (200,84) และส่วนทับ (120,84) พบอัลฟาต่อไปนี้ทั้งสองขนาดเมื่ออินพุตทึบ:
| อินพุตและคำสั่ง | อัลฟาเฉพาะ A | อัลฟาเฉพาะ B | อัลฟาส่วนทับ |
|---|---|---|---|
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 |
แถว in มีส่วนทับทึบร่วมแต่สีต่าง A ก่อนเป็นเขียวน้ำทะเล (23,107,91,255) B ก่อนเป็นเหลืองดิน (212,165,58,255) จุดนอก (230,150) โปร่งใสตลอด นี่เป็นพิกเซลในที่เลือก ไม่ใช่ทดสอบครอบคลุมขอบหรือความเข้ากันได้ทั้งหมด
ทุกไฟล์ฟิลเตอร์ยังมีสตริง d ต้นฉบับ A ตรงค่าและ SHA-256 ตรงกัน ค่า getBBox() รูปทรงเฉพาะที่ยังเป็น x=24, y=24, width=168, height=112 กรอบนี้อธิบายเส้นต้นฉบับ ไม่ใช่กรอบพิกเซลฟิลเตอร์ที่เห็น เส้น B เข้ารหัสก็ไม่เปลี่ยน ผลฟิลเตอร์จึงดูต่างได้ขณะที่สองเส้นขอบที่สร้างยังอยู่ครบ
ต้นฉบับบันทึกมีเส้นโดยตรงหนึ่งเส้นและไม่มีภาพฟิลเตอร์ ไฟล์ฟิลเตอร์แต่ละชุดมี A โดยตรงหนึ่งเส้นบวก feImage หนึ่งตัวที่บรรจุ SVG หนึ่งเส้นของ B แยก ไม่มีองค์ประกอบแรสเตอร์ image ปกติถูกเพิ่ม การทำงานพื้นที่ภาพเรนเดอร์กับอินพุตเวกเตอร์อยู่ร่วมกันได้ รายการต้นฉบับอย่างเดียวไม่ได้แปลว่าผลกลายเป็นรูปทรงรวมแก้ไขได้
ความทึบบางส่วนเปลี่ยนความหมายของช่อง
กรณีสุดท้ายทำ B ทึบครึ่งหนึ่งโดยคง A ทึบ แล้วใช้ A out B ส่วนทับไม่ได้ว่าง ทั้งสองขนาดมี RGBA (22,106,90,127) เป็นเขียวน้ำทะเลโปร่งใสบางส่วนพร้อมการปัดจากกระบวนการเรนเดอร์/อ่าน
สำหรับ out อัลฟาผลคืออัลฟาแรกคูณหนึ่งลบอัลฟาที่สอง สำหรับ in คือผลคูณทั้งคู่ นิยามการประกอบ Filter Effects นิยามผ่านสี premultiplied และอัลฟา B ทึบลบสี A ส่วนทับได้ ส่วน B โปร่งบางส่วนเหลือสีบางส่วน
ตรวจผลบนฉากหลังจริง บริเวณอ่อนบนขาวอาจเป็นสีโปร่งแสง ไม่ใช่ช่องโปร่งใสเต็ม พิกเซลขอบลบรอยหยักก็ต้องตรวจด้วยตา จุดด้านในไม่ได้รับปากว่าทุกพิกเซลขอบมีอัลฟาศูนย์ สำหรับงานปรับความทึบแยก ดูคู่มือเกณฑ์อัลฟา SVG
ส่งผลฟิลเตอร์หรือสร้างเส้นขอบจริง
เก็บฟิลเตอร์นี้เมื่อตัวเรนเดอร์ผู้รับควรทำภาพประกอบซ้ำ รักษาไฟล์ A และ B เดิมและตรวจ SVG ฟิลเตอร์ที่เปิดใหม่ในปลายทางจริง การเปิดสำเร็จในเบราว์เซอร์เราไม่รับรองตัวเรนเดอร์หรือเครื่องผลิตอื่น
หากส่งต้องการเส้นรวมที่เลือกได้หรือช่องพร้อมตัด ให้ทำและตรวจการแก้เส้นจริงหรือบูลีนในขั้นตอนเป้าหมาย สีที่เห็นเปลี่ยนที่นี่ไม่สร้างรูปทรงนั้น คู่มือ clipPath กับ mask แยกเรื่องการมองเห็นที่เกี่ยวข้อง คู่มือแยกกลุ่ม SVG ช่วยเมื่อภาพที่แสดงไม่ใช่วัตถุแก้ไขได้ที่คาด
หากคลิปอาร์ตมีเพียงพิกเซลและต้องการรูปต้นทางแก้ไขได้ ให้เตรียมคลิปอาร์ต SVG ด้วย PerfectVector ตรวจเส้นขอบและช่อง แล้วเก็บต้นฉบับหลัก ใช้และตรวจฟิลเตอร์หรือคำสั่งเส้นที่ตั้งใจแยก การแปลงเวกเตอร์ไม่เปลี่ยนส่วนทับที่เห็นจากฟิลเตอร์นี้เป็นเส้นผลิตที่ตรวจแล้ว
คำถามที่พบบ่อย
feComposite out ลบรูปทรงเส้นส่วนทับหรือไม่? ไม่ มันเปลี่ยนสีเรนเดอร์โดยใช้อัลฟาอินพุตสอง ในไฟล์ฟิลเตอร์ของเรา ข้อมูลเส้น A เดิมและขอบเขตรูปทรงเฉพาะที่ไม่เปลี่ยน แม้สีส่วนทับโปร่งใส
ทำไมสลับ in กับ in2 แล้วผลเปลี่ยน? มันระบุอินพุตแรกและสอง In เก็บสีแรกในความทึบของสอง out เก็บแรกภายนอก กรณี in สลับของเรามีส่วนทับทึบร่วมแต่คนละสี ส่วน out สลับเก็บคนละบริเวณ
xor ทำส่วนทับว่างเต็มเสมอหรือไม่? ส่วนทับด้านในทึบเต็มว่างในกรณี xor ของเรา อัลฟาบางส่วนและขอบลบรอยหยักต้องตรวจเอง แผนภาพเซตของรูปทึบไม่อธิบายทุกพิกเซลโปร่งแสง
ส่งช่องฟิลเตอร์เป็นเส้นตัดแก้ไขได้โดยตรงได้หรือไม่? ผลฟิลเตอร์ที่เห็นไม่ใช่รูปทรงบูลีนสร้างใหม่ เก็บรูปเดิม สร้างและตรวจเส้นที่ต้องใช้ในขั้นเส้นเป้าหมาย แล้วตรวจแอปผู้รับกับข้อกำหนดผลิตแยก
แหล่งข้อมูล
- MDN: feComposite — บทบาทอินพุตพื้นที่ภาพ คำสั่ง และปริภูมิสีฟิลเตอร์เริ่มต้น
- MDN: operator — ค่าคำสั่งประกอบและบริบท morphology แยก
- MDN: filterUnits — ระบบพิกัดพื้นที่ฟิลเตอร์
- MDN: feImage — อินพุตภาพในสายฟิลเตอร์
- W3C: การประกอบ Filter Effects — นิยามประกอบสี premultiplied กับอัลฟา
เริ่มจากคลิปอาร์ตแรสเตอร์ของคุณหรือไม่? สร้างต้นฉบับ SVG แก้ไขได้ ตรวจเส้นขอบและช่อง แล้วเก็บต้นฉบับขณะตรวจภาพประกอบที่เลือกหรือผลเส้นที่สร้างแยกในปลายทาง
เพิ่มเติมจากบล็อก

ครอป SVG ถาวรโดยไม่เหลือพาธที่ซ่อนอยู่
ครอปพาธ SVG ที่เติมสีด้วยการตัดส่วนร่วมจริง เก็บสีแยกและช่องเปิด แล้วตรวจไฟล์ที่บันทึกว่ามีรูปทรงที่การคลิปเพียงซ่อนไว้หรือไม่

Inkscape: Object to Path กับ Stroke to Path ต่างอย่างไร
เทียบ Object to Path และ Stroke to Path ใน Inkscape บนวงกลมเดียวกัน ดูคำสั่งที่คงเส้นปรับได้ คำสั่งที่สร้างแถบเติมสี และสิ่งที่ต้องตรวจต่อ
