PerfectVector
Autor: Irene Kim7 min czytania

SVG do PNG w Sharp: rozmiar, gęstość i ostre obrazy wynikowe

Renderuj pliki SVG do przezroczystych plików PNG w docelowym rozmiarze w pikselach za pomocą Sharp. Zobacz odtwarzalny test 1x i 2x, kiedy gęstość wejściowa ma znaczenie i dlaczego powiększanie PNG zawodzi.

Na tej stronie

W potoku korzystającym z aktualnej wersji Sharp przy każdym eksporcie zaczynaj od SVG i wywołuj resize() z szerokością w pikselach, której faktycznie potrzebujesz. Jeśli potrzebujesz zasobu 2x, zażądaj dwukrotnie większej liczby pikseli na wyjściu. Nie twórz najpierw małego PNG, aby potem powiększyć ten plik. Nie kopiuj też starego obejścia z density do każdego potoku: w naszym teście Sharp 0.34.4 wyrenderował te same piksele zarówno z wyższą gęstością wejściową, jak i bez niej.

Krótka odpowiedź
  1. Zachowaj oryginalny SVG jako plik wzorcowy.
  2. Renderuj każdy rozmiar PNG bezpośrednio z tego SVG za pomocą Sharp.
  3. Sprawdź wymiary wyjściowe i kanał alfa, a następnie obejrzyj plik w docelowym rozmiarze wyświetlania.
  4. Zmień SVG lub sprawdź mechanizm renderujący, jeśli wynik nadal różni się od źródła. Ustawienie gęstości nie może utworzyć szczegółów wewnątrz osadzonej bitmapy.

Wyrenderuj PNG 1x i 2x z SVG

Konstruktor Sharp przyjmuje plik SVG lub bufor, a jego API zmiany rozmiaru ustawia wymiary wyjściowe. Dla kwadratowej ikony o boku 64 jednostek, która będzie wyświetlana w rozmiarze 256 pikseli CSS, wyeksportuj PNG o szerokości 256 pikseli do użycia w skali 1x i PNG o szerokości 512 pikseli do użycia w skali 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"),
]);

Pominięcie wysokości zachowuje proporcje źródła przy standardowym sposobie zmiany rozmiaru w Sharp. Dobierz rzeczywiste szerokości wyjściowe do miejsca docelowego; „2x” odnosi się do wymiarów w pikselach względem docelowego rozmiaru wyświetlania w CSS, a nie do atrybutu SVG ani gwarancji dotyczącej każdego ekranu. Jeśli chcesz oglądać ikonę w rozmiarze 256 pikseli CSS, porównaj pliki PNG o szerokości 256 i 512 pikseli w tym rozmiarze wyświetlania i zdecyduj, czy korzyść z większego pliku uzasadnia jego większy rozmiar.

Nasza oryginalna ikona testowa miała deklarację width="64" height="64" viewBox="0 0 64 64". Wyrenderowaliśmy ją w rozmiarach 64, 512 i 1024 pikseli za pomocą przypiętej w repozytorium wersji Sharp 0.34.4. Pliki wyjściowe o szerokości 512 i 1024 pikseli zostały wygenerowane z SVG, miały przezroczyste narożniki i nieprzezroczyste zamalowane środki, a ich metadane wskazywały żądane wymiary. Są to obserwacje dotyczące tego pliku testowego i tej wersji, a nie test porównawczy wszystkich grafik SVG.

Oryginalny test renderowania w Sharp 0.34.4
Oryginalny test renderowania w Sharp: bezpośredni render SVG do 512 pikseli i render SVG ze zmienioną gęstością wyglądają identycznie, natomiast powiększenie pośredniego PNG o szerokości 64 pikseli daje nieostry obraz
Ta sama ikona SVG 64 × 64 została wyrenderowana bezpośrednio w rozmiarze 512 pikseli, wyrenderowana z gęstością wejściową 576 i przeskalowana do 512 pikseli albo najpierw zrasteryzowana w rozmiarze 64 pikseli, a następnie powiększona. Tylko ostatnia metoda powiększa istniejącą siatkę pikseli.

Ilustracja pokazuje konkretnie, na czym polega problem: gdy pierwszy wynik jest PNG o szerokości 64 pikseli, późniejsza zmiana rozmiaru interpoluje te piksele. Nie ma już ścieżek, które można ponownie narysować. W tym teście nasze wyniki o szerokości 512 pikseli — bezpośredni i ze zmienioną gęstością — były identyczne we wszystkich surowych kanałach RGBA. Źródłowy SVG, skrypt renderowania i metadane wyników są zachowane w pakiecie materiałów badawczych do artykułu.

Czy potrzebujesz density w Sharp?

Opcja density konstruktora Sharp steruje DPI używanym podczas odczytywania wejściowego SVG lub PDF; jej udokumentowana wartość domyślna to 72. To co innego niż określenie docelowej szerokości w resize(). Możesz natknąć się na starsze przykłady użycia Sharp, które zwiększają gęstość wejściową, aby uniknąć nieostrego powiększenia. Te przykłady opisują rzeczywisty problem występujący w przeszłości: zgłoszenie Sharp #1421 z 2018 roku opisuje pikselizację SVG o szerokości 32 pikseli po powiększeniu do 512 pikseli. Nie dowodzą one, że to obejście jest nadal potrzebne w aktualnej instalacji.

Przetestowaliśmy dokładnie tę parę w 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();

Dla naszej 64-pikselowej ikony wektorowej oba potoki dały te same piksele RGBA w rozmiarze 512 × 512. Ustawiaj gęstość wejściową tylko wtedy, gdy konkretny plik i zainstalowana wersja Sharp wykazują taką potrzebę, a po aktualizacjach powtarzaj porównanie. Nie traktuj wyższej wartości DPI jako uniwersalnego przełącznika ostrości. Pierwsze użyteczne pytanie brzmi: czy w momencie renderowania docelowego PNG przez Sharp nadal masz geometrię wektorową?

Jeśli pracujesz również z PDF, przetestuj to wejście osobno: ten eksperyment z SVG nie określa zachowania PDF. Podobnie withDensity() w nowszej dokumentacji Sharp dotyczy metadanych DPI na wyjściu; to inna operacja niż gęstość renderowania wejściowego SVG ustawiana w konstruktorze.

Sprawdź wymiary, przezroczystość i zawartość źródła

Użyj API metadanych Sharp na pliku wyjściowym, zamiast zgadywać na podstawie powiększenia podglądu:

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],
});

Dla użytego tutaj kwadratowego pliku testowego oczekiwane wymiary to 256 × 256 i 512 × 512. hasAlpha wskazuje obecność kanału alfa, ale nie dowodzi, że konkretny narożnik jest przezroczysty; sprawdź plik lub wartości pikseli, jeśli przezroczystość ma znaczenie przy przekazywaniu pliku. We wszystkich testowanych wynikach wartość alfa w narożniku naszego pliku testowego wynosiła zero, a w środku 255. Biały prostokąt wewnątrz SVG zostałby natomiast namalowany w każdym PNG, niezależnie od formatu PNG.

Jeśli świeżo wyrenderowany duży obraz nadal jest spikselizowany, sprawdź zawartość SVG. Standard SVG dopuszcza element <image>, który osadza obraz rastrowy lub zawiera odnośnik do niego. Plik może więc mieć rozszerzenie .svg, podczas gdy znajdująca się w nim grafika nadal jest PNG o niskiej rozdzielczości. Nasz poradnik znajdowania obrazów rastrowych wewnątrz SVG pokazuje, jak sprawdzić obiekty i XML. Zwiększenie szerokości wyjściowej w Sharp nie może zamienić tych osadzonych pikseli w ścieżki.

Sprawdź też miejsce docelowe. Plik o szerokości 512 pikseli wyświetlany w rozmiarze 1024 pikseli może wyglądać nieostro. Przeglądarka obrazów z powiększeniem znacznie przekraczającym 100% ujawni siatkę pikseli PNG, nawet gdy eksport jest poprawny. Porównaj obraz w rzeczywistym rozmiarze renderowania i na docelowym tle, a następnie zdecyduj, czy dostarczyć większy PNG, czy sam SVG. Przy publikowaniu w internecie SVG osadzony bezpośrednio w kodzie i SVG w elemencie obrazu oferują różne możliwości integracji; żadna z nich nie wymaga PNG, gdy środowisko docelowe obsługuje SVG.

Gdzie przydaje się PerfectVector

Sharp rasteryzuję SVG; nie odzyskuje edytowalnych ścieżek z PNG. Jeśli Twoja grafika wzorcowa to wyłącznie rastrowa ikona lub logo, utwórz i sprawdź SVG przed uruchomieniem tego potoku eksportu. Poradnik konwersji ikon PNG do SVG dla zestawu UI omawia spójną geometrię i kontrole przed przekazaniem całego zestawu. Proces konwersji obrazu na grafikę wektorową w PerfectVector może utworzyć wstępną wersję SVG z grafiki dostępnej wyłącznie w postaci rastrowej. Otwórz wynik, sprawdź jego sylwetkę, drobne szczegóły i to, czy zawiera rzeczywiste ścieżki, a następnie wyrenderuj PNG 1x i 2x z zatwierdzonego SVG. Jeśli masz już poprawny oryginalny SVG, zachowaj go i użyj Sharp bezpośrednio.

FAQ

Dlaczego mój wynik konwersji SVG do PNG w Sharp wygląda na rozmyty? Sprawdź, czy potok najpierw utworzył mały PNG, a następnie go powiększył. Potem sprawdź SVG pod kątem osadzonych obrazów rastrowych i porównaj dostarczony PNG w jego rzeczywistym rozmiarze wyświetlania. W aktualnej wersji Sharp przetestuj bezpośrednie renderowanie SVG do docelowego rozmiaru, zanim dodasz obejście ze zmianą gęstości wejściowej.

Czy density: 576 jest wymagane przy ośmiokrotnym powiększeniu? Nie było wymagane dla naszego testowego SVG powiększanego z 64 do 512 pikseli w Sharp 0.34.4: bezpośrednia zmiana rozmiaru i zmiana rozmiaru ze zmodyfikowaną gęstością dały identyczne piksele RGBA. Twój SVG i zainstalowany mechanizm renderujący mogą zachowywać się inaczej, więc przetestuj te konkretne dane wejściowe, zamiast przyjmować tę liczbę jako regułę.

Jak utworzyć przezroczysty PNG 2x? Wyrenderuj SVG w wymiarach dwukrotnie większych niż docelowe wymiary CSS i wyeksportuj go jako PNG. Potwierdź wynikową szerokość i wysokość, a następnie sprawdź przezroczystość niezamalowanego obszaru. Przezroczyste płótno nie usuwa kształtu tła, który już znajduje się w SVG.

Źródła

  1. Sharp — Constructor — Obsługa wejściowego SVG i opcja wejściowa density.
  2. Sharp — Resizing images — Sposób ustalania rozmiaru wyjściowego używany w przykładach.
  3. Sharp — Input metadata — Kontrole wymiarów i metadanych kanału alfa.
  4. Sharp GitHub — SVG to PNG pixelation issue #1421 — Zgłoszony w przeszłości objaw, wyraźnie oddzielony od testu przeprowadzonego z aktualną wersją Sharp przypiętą w repozytorium.
  5. Sharp — Output options — Rozróżnienie nowszych metadanych gęstości wyjściowej i gęstości renderowania wejścia.
  6. W3C — SVG 2 embedded content — Wyjaśnienie, dlaczego SVG może zawierać piksele rastrowe.

Jeśli plik wzorcowy jest dostępny wyłącznie w postaci rastrowej, utwórz wstępną wersję SVG za pomocą PerfectVector, sprawdź jej ścieżki i użyj Sharp, aby wyrenderować zatwierdzony plik w każdym wymaganym rozmiarze PNG.

Więcej z bloga

Zamień plik obrazu w
edytowalny SVG