PerfectVector
โดย Irene Kimอ่าน 4 นาที

แปลง SVG เป็น PNG ด้วย Sharp: ขนาด ความหนาแน่น และการส่งออกที่คมชัด

เรนเดอร์ SVG เป็น PNG โปร่งใสด้วย Sharp ตามขนาดพิกเซลปลายทาง ดูการทดสอบ 1x และ 2x ที่ทำซ้ำได้ เมื่อใดความหนาแน่นอินพุตจึงสำคัญ และทำไมการขยาย PNG จึงไม่ได้ผล

ในหน้านี้

สำหรับกระบวนการทำงานด้วย Sharp รุ่นปัจจุบัน ให้เริ่มจาก SVG ทุกครั้งที่ส่งออก แล้วเรียก resize() โดยระบุ ความกว้างเป็นพิกเซลที่ต้องใช้จริง หากต้องการไฟล์ 2x ให้ขอพิกเซลเอาต์พุตเป็นสองเท่า อย่าสร้าง PNG ขนาดเล็กก่อนแล้วนำไฟล์นั้นมาขยาย และอย่าคัดลอกวิธีแก้ด้วย density จากตัวอย่างเก่ามาใช้กับทุกกระบวนการ: ในการทดสอบ Sharp 0.34.4 ของเรา การเพิ่มความหนาแน่นอินพุตและการไม่เพิ่มให้พิกเซลที่เรนเดอร์เหมือนกัน

คำตอบแบบสั้น
  1. เก็บ SVG ต้นฉบับไว้เป็นไฟล์หลัก
  2. ใช้ Sharp เรนเดอร์ PNG แต่ละขนาดจาก SVG นั้นโดยตรง
  3. ตรวจสอบขนาดเอาต์พุตและช่องอัลฟา แล้วดูไฟล์ตามขนาดที่ตั้งใจจะแสดงจริง
  4. หากผลลัพธ์ยังต่างจากต้นฉบับ ให้แก้ SVG หรือตรวจสอบตัวเรนเดอร์ การตั้งค่าความหนาแน่นไม่สามารถสร้างรายละเอียดภายในบิตแมปที่ฝังอยู่ได้

เรนเดอร์ PNG ขนาด 1x และ 2x จาก SVG

ตัวสร้างอินสแตนซ์ของ Sharp รับไฟล์หรือบัฟเฟอร์ SVG และ API สำหรับปรับขนาด ใช้กำหนดขนาดเอาต์พุต สำหรับไอคอนสี่เหลี่ยมจัตุรัสขนาด 64 หน่วยที่จะปรากฏที่ 256 พิกเซล CSS ให้ส่งออก PNG ขนาด 256 พิกเซลสำหรับการใช้แบบ 1x และ PNG ขนาด 512 พิกเซลสำหรับการใช้แบบ 2x:

import sharp from "sharp";
 
const source = "icon.svg";
 
await Promise.all([
  sharp(source).resize({ width: 256 }).png().toFile("icon-1x.png"),
  sharp(source).resize({ width: 512 }).png().toFile("icon-2x.png"),
]);

การไม่ระบุความสูงจะรักษาอัตราส่วนภาพของต้นฉบับตามพฤติกรรมการปรับขนาดปกติของ Sharp เลือกความกว้างเอาต์พุตจริงให้เหมาะกับปลายทางของคุณ “2x” หมายถึงขนาดพิกเซลเมื่อเทียบกับขนาดแสดงผล CSS ที่ตั้งใจไว้ ไม่ใช่แอตทริบิวต์ SVG หรือคำรับประกันสำหรับทุกหน้าจอ หากต้องการดูไอคอนที่ 256 พิกเซล CSS ให้เปรียบเทียบ PNG ขนาด 256 และ 512 พิกเซลที่ขนาดแสดงผลนั้น แล้วตัดสินใจว่าไฟล์ที่ใหญ่ขึ้นคุ้มกับน้ำหนักไฟล์หรือไม่

ไอคอนต้นฉบับที่เราใช้ทดสอบประกาศ width="64" height="64" viewBox="0 0 64 64" เราเรนเดอร์เป็นขนาด 64, 512 และ 1,024 พิกเซลด้วย Sharp 0.34.4 ที่ล็อกเวอร์ชันไว้ในรีโพซิทอรี เอาต์พุตขนาด 512 และ 1,024 พิกเซลสร้างจาก SVG มีมุมโปร่งใสและส่วนกลางที่วาดสีทึบ และรายงานขนาดตรงตามที่ขอ ข้อมูลเหล่านี้เป็นผลสังเกตสำหรับตัวอย่างและเวอร์ชันนี้ ไม่ใช่ผลทดสอบประสิทธิภาพของงาน SVG ทุกประเภท

การทดสอบเรนเดอร์ Sharp 0.34.4 ของเรา
การทดสอบเรนเดอร์ Sharp ของเรา: SVG ที่เรนเดอร์โดยตรงเป็น 512 พิกเซลและ SVG ที่ปรับความหนาแน่นให้ภาพเหมือนกัน ขณะที่การขยาย PNG ขั้นกลางขนาด 64 พิกเซลให้ภาพไม่คมชัด
ไอคอน SVG ขนาด 64 × 64 เดียวกันถูกเรนเดอร์โดยตรงเป็น 512 พิกเซล เรนเดอร์ด้วยความหนาแน่นอินพุต 576 แล้วปรับเป็น 512 พิกเซล หรือแปลงเป็นภาพราสเตอร์ขนาด 64 พิกเซลก่อนแล้วจึงขยาย มีเพียงวิธีสุดท้ายที่ขยายตารางพิกเซลซึ่งมีอยู่แล้ว

ภาพนี้แสดงสาเหตุของปัญหาอย่างชัดเจน: เมื่อเอาต์พุตแรกเป็น PNG ขนาด 64 พิกเซล การปรับขนาดในภายหลังจะคำนวณพิกเซลใหม่จากพิกเซลเดิม ไฟล์นั้นไม่มีพาธให้วาดใหม่อีกแล้ว ในการทดสอบนี้ ผลลัพธ์ขนาด 512 พิกเซลแบบเรนเดอร์โดยตรงและแบบปรับความหนาแน่นเหมือนกันทุกช่อง RGBA ดิบ เราเก็บ SVG ต้นฉบับ สคริปต์เรนเดอร์ และเมทาดาทาของผลลัพธ์ไว้ในชุดข้อมูลวิจัยของบทความ

จำเป็นต้องใช้ density ใน Sharp หรือไม่?

ตัวเลือก density ของตัวสร้างอินสแตนซ์ Sharp ควบคุม DPI ที่ใช้เมื่อ อ่าน อินพุต SVG หรือ PDF โดยค่าปริยายที่ระบุในเอกสารคือ 72 ตัวเลือกนี้ต่างจากการขอความกว้างปลายทางผ่าน resize() คุณอาจพบตัวอย่าง Sharp รุ่นเก่าที่เพิ่มความหนาแน่นอินพุตเพื่อเลี่ยงภาพไม่คมเมื่อขยาย ตัวอย่างเหล่านั้นกล่าวถึงอาการที่เคยเกิดขึ้นจริง: ปัญหา Sharp #1421 รายงานในปี 2018 ว่า SVG ขนาด 32 พิกเซลเห็นเป็นพิกเซลเมื่อขยายเป็น 512 พิกเซล แต่ไม่ได้พิสูจน์ว่ายังจำเป็นต้องใช้วิธีแก้นี้กับการติดตั้งรุ่นปัจจุบัน

เราทดสอบสองวิธีนี้บน Sharp 0.34.4:

const direct = await sharp("icon.svg")
  .resize({ width: 512 })
  .png()
  .toBuffer();
 
const densityFirst = await sharp("icon.svg", { density: 576 })
  .resize({ width: 512 })
  .png()
  .toBuffer();

สำหรับไอคอนเวกเตอร์ขนาด 64 พิกเซลของเรา ทั้งสองกระบวนการให้พิกเซล RGBA ขนาด 512 × 512 เหมือนกัน ตั้งความหนาแน่นอินพุตเฉพาะเมื่อไฟล์ที่ใช้และเวอร์ชัน Sharp ที่ติดตั้งแสดงให้เห็นว่าจำเป็น แล้วเปรียบเทียบซ้ำหลังอัปเกรด อย่าถือว่าค่า DPI ที่สูงขึ้นเป็นสวิตช์เพิ่มความคมชัดสำหรับทุกกรณี คำถามแรกที่มีประโยชน์คือ ในขณะที่ Sharp เรนเดอร์ PNG ขนาดปลายทางนั้น คุณยังมีรูปทรงเวกเตอร์อยู่หรือไม่

หากคุณทำงานกับ PDF ด้วย ให้ทดสอบอินพุตนั้นแยกต่างหาก: การทดลอง SVG นี้ไม่ได้ยืนยันพฤติกรรมของ PDF เช่นเดียวกัน withDensity() ในเอกสาร Sharp รุ่นใหม่ กล่าวถึง เมทาดาทา DPI ของเอาต์พุต ซึ่งเป็นคนละการทำงานกับความหนาแน่นการเรนเดอร์อินพุต SVG ในตัวสร้างอินสแตนซ์

ตรวจสอบขนาด ความโปร่งใส และเนื้อหาต้นฉบับ

ใช้ API เมทาดาทาของ Sharp ตรวจเอาต์พุต แทนการคาดเดาจากการซูมในหน้าพรีวิว:

const oneX = await sharp("icon-1x.png").metadata();
const twoX = await sharp("icon-2x.png").metadata();
 
console.log({
  oneX: [oneX.width, oneX.height, oneX.hasAlpha],
  twoX: [twoX.width, twoX.height, twoX.hasAlpha],
});

สำหรับตัวอย่างสี่เหลี่ยมจัตุรัสนี้ ขนาดที่คาดหวังคือ 256 × 256 และ 512 × 512 ค่า hasAlpha บ่งบอกว่ามีช่องอัลฟา แต่ ไม่ได้ พิสูจน์ว่ามุมใดมุมหนึ่งโปร่งใส ให้ตรวจดูไฟล์หรืออ่านค่าพิกเซลหากความโปร่งใสสำคัญต่อการส่งมอบงาน อัลฟาที่มุมของตัวอย่างเราเท่ากับศูนย์ และที่กึ่งกลางเท่ากับ 255 ในเอาต์พุตทุกแบบที่ทดสอบ หาก SVG มีสี่เหลี่ยมสีขาวอยู่ภายใน สี่เหลี่ยมนั้นจะถูกวาดลงใน PNG ทุกไฟล์ไม่ว่าจะใช้รูปแบบ PNG ก็ตาม

หากการเรนเดอร์ใหม่ขนาดใหญ่ยังเห็นเป็นพิกเซล ให้ตรวจเนื้อหาภายใน SVG มาตรฐาน SVG อนุญาตให้ใช้ องค์ประกอบ <image> เพื่อฝังหรือลิงก์ภาพราสเตอร์ได้ ดังนั้นไฟล์จึงอาจมีนามสกุล .svg แต่งานภาพภายในยังเป็น PNG ความละเอียดต่ำ คู่มือค้นหาภาพราสเตอร์ภายใน SVG ของเรา แสดงวิธีตรวจวัตถุและ XML การเพิ่มความกว้างเอาต์พุตของ Sharp ไม่สามารถเปลี่ยนพิกเซลที่ฝังอยู่ให้เป็นพาธได้

ตรวจสอบปลายทางด้วย ไฟล์ขนาด 512 พิกเซลที่แสดงที่ 1,024 พิกเซลอาจดูไม่คม โปรแกรมดูภาพที่ซูมเกิน 100% มาก ๆ จะแสดงตารางพิกเซลของ PNG แม้การส่งออกจะถูกต้อง เปรียบเทียบที่ขนาดเรนเดอร์และพื้นหลังจริง แล้วตัดสินใจว่าจะส่ง PNG ที่ใหญ่ขึ้นหรือส่ง SVG โดยตรง สำหรับการส่งภาพบนเว็บ SVG แบบอินไลน์และ SVG ในองค์ประกอบรูปภาพ เป็นตัวเลือกการผสานใช้งานที่ต่างกัน ทั้งสองแบบไม่จำเป็นต้องใช้ PNG เมื่อปลายทางรองรับ SVG

PerfectVector ช่วยในส่วนใด

Sharp แปลง SVG เป็นภาพราสเตอร์ ไม่ได้กู้คืนพาธที่แก้ไขได้จาก PNG หากไฟล์หลักของคุณมีเพียงไอคอนหรือโลโก้ราสเตอร์ ให้สร้างและตรวจสอบ SVG ก่อน เริ่มกระบวนการส่งออกนี้ คู่มือแปลงไอคอน PNG เป็น SVG สำหรับชุด UI อธิบายรูปทรงที่สอดคล้องกันและการตรวจส่งมอบภาพทั้งชุด กระบวนการแปลงภาพเป็นเวกเตอร์ของ PerfectVector สามารถสร้าง SVG สำหรับประเมินจากงานที่มีเพียงราสเตอร์ได้ เปิดผลลัพธ์นั้น ตรวจขอบรูป รายละเอียดเล็ก ๆ และว่ามีพาธจริงหรือไม่ แล้วเรนเดอร์ PNG ขนาด 1x และ 2x จาก SVG ที่ผ่านการตรวจแล้ว หากคุณมี SVG ต้นฉบับที่สะอาดอยู่แล้ว ให้เก็บไว้และใช้ Sharp โดยตรง

คำถามที่พบบ่อย

ทำไม PNG ที่แปลงจาก SVG ด้วย Sharp จึงดูเบลอ? ตรวจสอบว่ากระบวนการสร้าง PNG ขนาดเล็กก่อนแล้วจึงขยายหรือไม่ จากนั้นตรวจ SVG ว่ามีราสเตอร์ฝังอยู่หรือไม่ และเปรียบเทียบ PNG ที่ส่งมอบตามขนาดแสดงผลจริง สำหรับ Sharp รุ่นปัจจุบัน ให้ทดสอบเรนเดอร์จาก SVG เป็นขนาดปลายทางโดยตรงก่อนเพิ่มวิธีแก้ด้วยความหนาแน่นอินพุต

จำเป็นต้องใช้ density: 576 เพื่อขยายแปดเท่าหรือไม่? ไม่จำเป็นสำหรับตัวอย่าง SVG ขนาด 64 พิกเซลที่ขยายเป็น 512 พิกเซลบน Sharp 0.34.4 ของเรา: การปรับขนาดโดยตรงและการปรับขนาดพร้อมเปลี่ยนความหนาแน่นให้พิกเซล RGBA เหมือนกัน SVG และตัวเรนเดอร์ที่ติดตั้งของคุณอาจทำงานต่างออกไป จึงควรทดสอบอินพุตจริงเหล่านั้นแทนการถือว่าตัวเลขนี้เป็นกฎ

สร้าง PNG โปร่งใสขนาด 2x ได้อย่างไร? เรนเดอร์จาก SVG ด้วยขนาดเป็นสองเท่าของขนาด CSS ที่ตั้งใจใช้ แล้วส่งออกเป็น PNG ยืนยันความกว้างและความสูงของผลลัพธ์ จากนั้นตรวจพื้นที่ที่ไม่ได้วาดสีว่ามีความโปร่งใสหรือไม่ ผืนภาพโปร่งใสไม่ได้ลบรูปทรงพื้นหลังที่มีอยู่แล้วใน SVG

แหล่งอ้างอิง

  1. Sharp — ตัวสร้างอินสแตนซ์ — การรองรับอินพุต SVG และตัวเลือกอินพุต density
  2. Sharp — การปรับขนาดรูปภาพ — พฤติกรรมกำหนดขนาดเอาต์พุตที่ใช้ในตัวอย่าง
  3. Sharp — เมทาดาทาอินพุต — การตรวจขนาดและเมทาดาทาอัลฟา
  4. Sharp GitHub — ปัญหา SVG เป็น PNG เห็นเป็นพิกเซล #1421 — อาการที่เคยมีรายงาน โดยแยกอย่างชัดเจนจากการทดสอบรุ่นปัจจุบันที่ล็อกเวอร์ชันไว้
  5. Sharp — ตัวเลือกเอาต์พุต — แยกเมทาดาทาความหนาแน่นเอาต์พุตในรุ่นใหม่ออกจากความหนาแน่นการเรนเดอร์อินพุต
  6. W3C — เนื้อหาที่ฝังใน SVG 2 — เหตุผลที่ SVG อาจมีพิกเซลราสเตอร์อยู่ภายใน

หากไฟล์หลักมีเพียงราสเตอร์ ให้ สร้าง SVG สำหรับประเมินด้วย PerfectVector ตรวจพาธ แล้วใช้ Sharp เรนเดอร์ไฟล์ที่ผ่านการตรวจแล้วเป็น PNG แต่ละขนาดที่ต้องการ

เพิ่มเติมจากบล็อก

เปลี่ยนไฟล์รูปภาพเป็น
SVG ที่แก้ไขได้