Gambar Malah Membesar Setelah Dikompres? Ini Sebabnya
Kompresor Anda kemungkinan besar baik-baik saja. Ketika kompresi gambar tidak bekerja, entah hasilnya kembali dengan ukuran yang sama atau justru jelas lebih besar daripada yang Anda masukkan, penyebabnya hampir selalu salah satu dari tiga hal. Alat yang rusak tidak ada dalam daftar itu.
Mulai dari yang paling umum. Alat apa pun yang mengompres lewat elemen canvas browser akan membuang color type PNG Anda dan mengode ulang (re-encoding) setiap piksel menjadi RGBA 32-bit. Kami melewatkan seluruh 83 kartu PNG Open Graph milik situs ini melalui canvas.toBlob('image/png') di Chrome 151.0.0.0. Ke-83-nya kembali dalam ukuran yang lebih besar. Median pertumbuhannya +76.6%; yang terkecil +35.6% dan yang terbesar +237.2%.
Penyebab kedua: JPEG Anda memang sudah terkompresi. Jalankan lima putaran pada kualitas 0.8 dan ukuran filenya berhenti bergerak setelah putaran kedua, sementara setiap putaran terus merusak gambarnya.
Penyebab ketiga: PNG Anda sudah pernah melewati kuantisasi (quantization). Tidak ada lagi redundansi warna yang bisa dibuang putaran kedua.
Tidak satu pun dari ketiganya bisa Anda perbaiki dengan menurunkan slider kualitas. Solusinya adalah format yang berbeda atau encoder yang berbeda. Ke-83 PNG yang sama, setelah kami konversi ke WebP pada kualitas 0.8, semuanya mengecil, dengan median −94.2%.
Cara kami menghasilkan angka-angka ini. Chrome 151.0.0.0 yang dikendalikan Playwright, macOS 26.5.2, Node v25.8.2, upng-js 2.1.0, ImageMagick. Kumpulan 83 file itu adalah seluruh PNG di direktori
public/ogsitus ini, bukan sampel dari direktori tersebut. Empat file terkontrol tambahan berukuran 1200×630 dan 800×600 menutup kasus foto, grafik, palet, dan JPEG secara terpisah.
1. Kompresi gambar tidak bekerja: triase tiga puluh detik
Cari baris Anda, lalu baca bagian yang ditunjuknya.
| Yang Anda masukkan | Yang Anda dapat | Akar penyebab | Baca |
|---|---|---|---|
| Tangkapan layar atau grafik PNG | Lebih besar daripada aslinya | canvas mengode ulangnya jadi RGBA 32-bit | Bagian 2 |
| Foto JPEG | Lebih besar daripada aslinya | File tersimpan sebagai PNG | Bagian 2 |
| Foto JPEG | Nyaris tidak berubah | Sudah mentok di plateau ukurannya | Bagian 3 |
| PNG yang sudah pernah lewat kompresor | Tidak berubah, atau sedikit lebih besar | Tidak ada ruang tersisa | Bagian 4 |
| Apa pun | Mengecil, tetapi buram atau warnanya meleset | Generation loss, atau profil ICC yang terbuang | Bagian 3 dan 6 |
Baris-baris itu tidak saling eksklusif. Foto JPEG yang Anda lempar ke eksportir PNG berbasis canvas kena dua baris pertama sekaligus, dan begitulah file 47,828 byte berubah menjadi 809,415 byte.
Kalau Anda ingin melewati diagnosisnya dan langsung mendapat file yang lebih kecil, kompresor gambar kami menjalankan encoder PNG dengan kuantisasi, bukan perjalanan bolak-balik lewat canvas. Alat itu juga membuang keluarannya sendiri setiap kali hasilnya tidak lebih kecil daripada yang Anda unggah.
2. Penyebab pertama: canvas browser selalu menulis RGBA 32-bit
Apa yang sebenarnya dilakukan canvas.toBlob() pada PNG Anda
Tidak ada langkah kompresi dalam perjalanan bolak-balik lewat canvas. Yang ada adalah satu dekode dan satu pengodean ulang, dan segala sesuatu yang bukan nilai piksel akan hilang di antara keduanya.
Taruh sebuah gambar di atas canvas, dan browser mendekodenya menjadi buffer RGBA yang datar: empat byte per piksel, tanpa palet, tanpa trik kedalaman bit (bit depth), tanpa metadata. HTMLCanvasElement.toBlob() lalu mengodekan buffer itu dari nol. Spesifikasi PNG mendefinisikan enam color type, dan sebuah encoder PNG bebas memilih yang paling hemat untuk merepresentasikan gambarnya. Encoder canvas milik Chrome tidak memilih. Ia selalu mengeluarkan color type 6.
| Color type yang masuk | Yang dikembalikan canvas.toBlob('image/png') |
|---|---|
| RGB (color type 2) | RGBA (color type 6) |
| Palet (color type 3) | RGBA (color type 6) |
| JPEG (tidak punya color type PNG) | RGBA (color type 6) |
Anda bisa membaca ini langsung dari byte-nya. Byte ke-25 sebuah file PNG adalah bit depth dan byte ke-26 adalah color type, keduanya berada di dalam chunk IHDR:
xxd -s 24 -l 2 -p suspect.png
# 0806 -> bit depth 8, color type 6 (RGBA)
Ketiga jenis masukan di atas sama-sama menghasilkan depth=8 type=6. Gambar berpalet 64 warna menyimpan satu byte per piksel plus sebuah tabel kecil; sesudah perjalanan bolak-balik itu, ia menyimpan empat byte per piksel dan tabelnya lenyap. Deflate mengembalikan sebagian dari selisih itu, tidak pernah seluruhnya.
Reproduksi sendiri di browser Anda dengan cuplikan konsol berikut. Cuplikan ini memilih sebuah file, melewatkannya bolak-balik, lalu mencetak kedua ukurannya berikut color type-nya:
const input = document.createElement('input');
input.type = 'file';
input.accept = 'image/png,image/jpeg';
input.onchange = async () => {
const file = input.files[0];
const bitmap = await createImageBitmap(file);
const canvas = document.createElement('canvas');
canvas.width = bitmap.width;
canvas.height = bitmap.height;
canvas.getContext('2d').drawImage(bitmap, 0, 0);
const blob = await new Promise((r) => canvas.toBlob(r, 'image/png'));
const head = new Uint8Array(await blob.slice(0, 26).arrayBuffer());
console.log(file.name, file.size, '->', blob.size);
console.log('bit depth', head[24], 'color type', head[25]);
};
input.click();
Pada image/png, toBlob mengabaikan argumen quality-nya. PNG bersifat lossless (tanpa kehilangan data), jadi sebuah angka kualitas tidak punya apa pun untuk ditukar, dan slider yang tampak mengendalikan kompresi PNG di alat berbasis canvas sebenarnya tidak mengendalikan apa-apa.
83 file terukur: setiap gambar hasil kompresi kembali lebih besar daripada aslinya
Ini hasil lengkapnya, tanpa memilih-milih:
| Pengukuran | Nilai |
|---|---|
| File yang diuji | 83 (seluruh PNG di public/og, mencakup color type RGB maupun palet) |
| File yang membesar | 83 / 83 (100%) |
| Kenaikan terkecil | +35.6% |
| Kenaikan median | +76.6% |
| Kenaikan terbesar | +237.2% |
Satu file, supaya Anda bisa melihat bentuk masalahnya: aes-decrypt.png beranjak dari 505,516 B ke 898,014 B, naik +77.6%. File yang sama, dikodekan sebagai WebP pada kualitas 0.8, berukuran 27,188 B, −94.6%.
Sampel ini punya batasnya sendiri. Ke-83 file itu adalah kartu Open Graph: 1200×630, latar polos, teks besar, segelintir warna merek. Encoder PNG yang bagus menangani grafik semacam ini dengan baik, dan justru karena itulah perjalanan bolak-balik lewat canvas begitu merugikannya. Ini hasil yang kuat untuk PNG bertipe grafik; ini bukan klaim bahwa setiap PNG di dunia membesar setelah melewati canvas. PNG fotografis yang memang sudah tersimpan sebagai RGBA penuh punya jauh lebih sedikit hal untuk dikorbankan.
Yang memang terbukti: kalau gambar hasil kompresi Anda lebih besar daripada aslinya dan alatnya berjalan di dalam tab browser, encoder adalah tempat pertama yang perlu Anda periksa, bukan pengaturan Anda.
Mengapa JPEG yang disimpan sebagai PNG membengkak 16.9×
Empat file terkontrol, keempatnya melewati jalur canvas yang sama. Baris terakhirnya paling ekstrem di seluruh kumpulan data:
| File | Sifat | Asli | canvas PNG | Perubahan | JPEG q92 | JPEG q80 | WebP q80 |
|---|---|---|---|---|---|---|---|
og-a.png | Grafik, 2,351 warna, RGB | 306,302 | 607,481 | +98.3% | 56,459 | 37,695 | 13,852 |
quantized.png | Palet 64 warna | 59,843 | 184,856 | +208.9% | 78,388 | 44,442 | 16,046 |
photo.png | Foto, 479,373 warna | 498,639 | 867,763 | +74.0% | 77,321 | 45,050 | 24,068 |
photo.jpg | JPEG q82 | 47,828 | 809,415 | +1,592% (16.9×) | 58,762 (+22.9%) | 47,152 | 23,862 |
Baris terakhir itu: 47,828 byte masuk, 809,415 byte keluar.
Mekanismenya layak dipahami karena ia menjelaskan satu keluarga utuh laporan “kompresinya malah memperbesar”. JPEG adalah codec lossy berbasis domain frekuensi: ia mengubah blok 8×8 menjadi koefisien DCT, mengkuantisasinya secara agresif, lalu menyimpan yang tersisa. PNG adalah codec lossless berbasis domain spasial: ia memprediksi tiap piksel dari tetangganya lalu men-deflate residualnya. Dekode sebuah JPEG dan Anda mendapat piksel yang membawa setiap artefak yang ditinggalkan kuantizer: ringing di dekat tepi, blocking pada gradien yang halus, derau tipis di tempat yang aslinya bersih.
Simpan piksel-piksel itu sebagai PNG dan Anda sedang meminta codec lossless untuk menyimpan artefak tersebut dengan sempurna. Ia menurut. Derau yang tadinya diciptakan JPEG demi memperkecil file kini justru menjadi hal yang membuat PNG membesar, karena encoder lossless yang bekerja lewat prediksi paling tidak sanggup mengompres derau.
Di mana pun ada pengaturan bawaan “simpan sebagai PNG” yang berdiri di depan sebuah foto, hal ini sedang terjadi: alat tangkapan layar, ekspor dari aplikasi desain, klien chat, sebagian widget unggah.
Piksel yang sama, terpaut 3.46×, sama-sama lossless
Encoder canvas lemah, dan kelemahannya bisa diukur. Ambil buffer piksel dari og-a.png lalu kodekan dengan dua cara, keduanya sepenuhnya lossless:
| Encoder | Keluaran | Color type |
|---|---|---|
Chrome canvas toBlob('image/png') | 607,481 B | RGBA (dipaksa naik) |
upng-js encode(..., cnum=0) | 175,491 B | RGB (color type asli dipertahankan) |
3.46×, untuk piksel yang identik. Kami memverifikasi klaim lossless itu alih-alih mengasumsikannya: mendekode keluaran upng-js pada cnum=0 menghasilkan buffer yang identik byte demi byte dengan buffer RGBA masukan. Anda tidak mengorbankan apa pun untuk 3.46× itu.
Ini juga menjelaskan mengapa dua kompresor online, sama-sama gratis dan sama-sama mengiklankan hal yang sama, bisa memberi hasil yang sama sekali tidak sebanding. “Kompresi di dalam browser” menggambarkan dua implementasi yang sepenuhnya berbeda. Yang satu menyerahkan pikselnya ke canvas.toBlob lalu mengirimkan apa pun yang keluar. Yang lain membawa encoder PNG sungguhan dan mengendalikan color type-nya. Masukan sama, browser sama, terpaut 3.46×.
3. Penyebab kedua: JPEG Anda sudah tidak punya sisa apa pun
Lima putaran kompresi ulang, dan ukurannya berhenti bergerak
Ambil photo.jpg, yang memang sudah tersimpan pada kualitas 82, lalu kompres ulang pada kualitas 0.8 lima kali berturut-turut, setiap generasi menjadi masukan bagi generasi berikutnya:
| Generasi | Byte | vs. sebelumnya |
|---|---|---|
| 0 (asli, q82) | 47,828 | — |
| 1 | 47,152 | −1.4% |
| 2 | 47,164 | +0.0% |
| 3 | 47,156 | −0.0% |
| 4 | 47,156 | 0.0% |
| 5 | 47,156 | 0.0% |
Putaran pertama memberi Anda 1.4%. Sejak generasi 2 dan seterusnya, ukuran filenya terkunci di dalam pita ±12 byte, dan generasi 4 serta 5 berukuran persis sama dengan generasi 3.
Beginilah rupa “kompresi tidak bekerja” ketika masukannya berupa JPEG. Alatnya berjalan. Encoder-nya berjalan. Memang sudah tidak ada apa-apa lagi yang bisa dibuang, karena tabel kuantisasi pada kualitas 80 sudah menolkan kira-kira koefisien yang sama dengan yang tadinya dipertahankan kualitas 82. Begitu sebuah koefisien hilang, ia tidak bisa dihilangkan lagi.
Anda bisa menyaksikannya sendiri secara lokal:
cp photo.jpg gen0.jpg
for i in 1 2 3 4 5; do
magick "gen$((i-1)).jpg" -quality 80 "gen$i.jpg"
done
stat -f%z gen0.jpg gen1.jpg gen2.jpg gen3.jpg gen4.jpg gen5.jpg
Di Linux, gunakan stat -c%s. Jumlah byte persisnya bergantung pada encoder mana yang ditautkan build ImageMagick Anda, jadi jangan berharap tabel di atas tereproduksi digit demi digit. Polanya yang jadi intinya: satu penurunan yang berarti, lalu garis datar.
Menaikkan angka kualitas bukanlah kompresi
Lihat lagi baris photo.jpg di bagian 2. Mengompres ulang file asli berkualitas 82 itu pada kualitas 92 menghasilkan 58,762 byte: +22.9%.
Ini mengejutkan orang yang mengira parameter kualitas adalah tombol putar dari “kecil” ke “besar” yang bebas mereka setel di mana saja. Ia bukan target kualitas absolut. Ia memilih sebuah tabel kuantisasi. Kalau Anda menjalankan gambar yang sudah didekode kembali melalui tabel yang lebih halus daripada tabel yang menghasilkannya, encoder akan menyimpan artefak yang sudah ada dengan lebih presisi sekaligus menambahkan satu putaran kehilangan yang baru di atasnya. File lebih besar, gambar lebih buruk, keduanya sekaligus.
Aturan yang lahir dari sini: jangan pernah mengompres ulang JPEG pada setelan kualitas di atas setelan saat file itu disimpan. Kalau Anda tidak tahu berapa angkanya, jangan kompres ulang sama sekali. Kembalilah ke sumbernya.
Generation loss: penurunan kualitas kompresi ulang JPEG yang tidak bisa Anda lihat
Tabel plateau itu menyimpan sebuah jebakan. Ukurannya berhenti berubah setelah generasi 2, tetapi gambarnya terus berubah. Setiap putaran mendekode menjadi piksel, mentransformasi ulang, lalu mengkuantisasi ulang. Koefisien yang bertahan tepat di ambang batas pada satu generasi akan terdorong melewatinya pada generasi berikutnya.
Kerusakannya tidak muncul di tempat yang Anda cari. Pada ukuran thumbnail, Anda tidak bisa membedakan generasi 5 dari generasi 1. Perbesar ke 100% dan periksa tempat-tempat yang selalu jatuh lebih dulu pada JPEG: tepi tajam di atas latar polos, teks, dan gradien halus tempat blocking muncul sebagai ubin 8×8 yang kasatmata. Di pipeline build yang mengompres ulang pada setiap deploy, hal ini menumpuk diam-diam selama berbulan-bulan.
Kami mengukur ukuran file di sini, bukan kualitas persepsi, jadi tidak ada angka PSNR atau SSIM untuk kelima generasi itu. Kami memang tidak mengukurnya. Data ukurannya sendiri sudah cukup membuktikan satu hal: sejak generasi 2, biayanya sepenuhnya jatuh di sisi kualitas dan manfaatnya nol.
Pergeseran warna adalah kegagalan yang berbeda dengan pemicu yang sama. Canvas tidak membawa metadata, jadi perjalanan bolak-balik toBlob ikut membuang blok EXIF beserta profil ICC-nya. Gambar bertanda Display P3 atau Adobe RGB yang masuk akan keluar tanpa tanda, dan penampil (viewer) menafsirkannya sebagai sRGB. Nilai pikselnya tidak bergeser. Yang hilang adalah instruksi untuk menafsirkannya.
4. Penyebab ketiga: mengecilkan ukuran file PNG tidak berhasil karena ia sudah dikuantisasi
Kalau PNG Anda sudah sekali melewati kompresor, putaran kedua tidak punya bahan untuk digarap. Inilah baris quantized.png, file 59,843 byte yang sudah direduksi menjadi palet 64 warna, dijalankan lewat tiga jalur terpisah:
| Jalur | Hasil | vs. asli |
|---|---|---|
Pengodean ulang lossless (upng cnum=0) | 61,377 | +2.6% |
| Kuantisasi ke 256 warna | 61,366 | +2.5% |
| Kuantisasi ke 64 warna | 61,366 | +2.5% |
Semua jalur keluar lebih besar daripada aslinya. Tidak banyak, tetapi tetap lebih besar, dan itu termasuk mengkuantisasi ke 64 warna sebuah file yang memang sudah punya 64 warna.
Kompresi PNG bekerja dengan membuang redundansi: warna yang berulang, tetangga yang bisa diprediksi, palet yang kecil. Putaran sebelumnya sudah memungut semuanya. Yang tersisa nyaris tidak bisa dikompres, dan kenaikan kecil itu adalah overhead encoder-nya sendiri: urutan palet yang sedikit berbeda, pilihan filter yang berbeda per scanline, deflate yang sedikit kurang beruntung.
Konsekuensi praktisnya: “0% dihemat” pada PNG yang sudah dioptimalkan adalah hasil yang benar, bukan kegagalan. Alat yang melaporkan kenaikan kecil lalu mempertahankan file asli Anda berperilaku sebagaimana mestinya. Alat yang tetap menyodorkan file yang lebih besar itu kepada Anda, tidak.
5. Kuantisasi adalah tuas utama untuk PNG, bukan pengodean ulang
Pengodean ulang lossless versus 256 warna versus 64 warna
Tiga file, tiga strategi, terukur:
| File | Asli | Lossless (cnum=0) | 256 warna | 64 warna |
|---|---|---|---|---|
og-a.png | 306,302 | 175,491 (−42.7%) | 110,772 (−63.8%) | 76,230 (−75.1%) |
photo.png | 498,639 | 587,863 (+17.9%) | 109,284 (−78.1%) | 61,397 (−87.7%) |
quantized.png | 59,843 | 61,377 (+2.6%) | 61,366 (+2.5%) | 61,366 (+2.5%) |
Pengodean ulang lossless adalah alat terlemah yang tersedia. Ia menang 42.7% pada grafik, mengembalikan 17.9% pada foto, dan kalah 2.6% pada file yang sudah dikuantisasi. Bahkan encoder PNG lossless yang kompeten pun kewalahan menghadapi konten fotografis, karena tidak ada palet yang bisa ditemukan dan piksel bertetangga tidak saling memprediksi dengan baik.
Reduksi yang nyata datang dari kuantisasi, dan selisihnya jauh: 63.8% berbanding 42.7% pada grafik yang sama di 256 warna, 75.1% di 64 warna. Di baris perintah:
magick logo.png -colors 64 PNG8:logo-64.png
magick logo.png -colors 256 PNG8:logo-256.png
Awalan PNG8: memaksa PNG berpalet. Tanpa awalan itu, ImageMagick bisa saja mereduksi warnanya lalu tetap menulis file truecolor, yang membuang sebagian besar manfaatnya.
Kapan palet aman, dan kapan Anda mendapat banding
Kuantisasi bersifat lossy. Ia memetakan setiap piksel ke entri terdekat dalam palet yang terbatas, jadi pertanyaannya adalah apakah konten Anda punya cukup banyak warna berbeda sampai hal itu kelihatan.
Aman: ikon, logo, tangkapan layar UI, ilustrasi datar, diagram, apa pun yang punya area luas berwarna seragam dan tepi yang tajam. Konten seperti ini biasanya paling banyak hanya berisi beberapa ratus warna berbeda, jadi palet 256 entri nyaris gratis dan bahkan 64 pun sering kali selamat.
Berisiko: foto, gradien halus, bayangan lembut, dan lapisan semi-transparan. Mereduksi sebuah gradien menjadi 64 tingkat menghasilkan pita warna (banding) yang kasatmata, dan dithering menukar pita itu dengan derau yang lalu memakan kembali sebagian penghematan ukuran file Anda. Transparansi parsial di atas gradien adalah kasus tersulit dari semuanya.
Transparansi layak Anda periksa tersendiri, karena seberapa banyak yang selamat lebih bergantung pada encoder-nya ketimbang pada kuantisasinya. PNG8: milik ImageMagick menulis transparansi biner, jadi sebuah piksel hanya bisa sepenuhnya buram atau sepenuhnya bening, dan tepi anti-aliased yang lembut kembali dalam keadaan kasar. Kuantizer PNG khusus mempertahankan kanal alpha yang utuh berikut tepi lembutnya. Kalau aset Anda punya bayangan atau tepi yang di-feather, bandingkan keduanya sebelum Anda memutuskan.
Periksa hasilnya pada 100%, bukan di thumbnail. Pratinjau berukuran kecil selalu berhasil menyembunyikan banding.
6. Solusinya: ganti format, jangan kompres ulang
Tabel keputusan format
| Konten | Pakai | Alasan |
|---|---|---|
| Foto | WebP, atau JPEG untuk kompatibilitas maksimum | Pengodean frekuensi yang lossy memang yang dibutuhkan foto |
| Tangkapan layar, grafik UI | PNG terkuantisasi, atau WebP | Warna datar, tepi tajam, palet kecil |
| Ikon dan logo | SVG kalau Anda punya vektornya, selain itu PNG terkuantisasi | Vektor tidak punya masalah resolusi |
| Apa pun yang butuh transparansi | WebP atau PNG | Keduanya membawa kanal alpha yang utuh |
| Animasi | WebP | Satu format saja, bukan GIF |
| Arsip dengan presisi piksel | PNG, lossless | Satu-satunya kasus saat lossless memang syaratnya |
Ini versi ringkasnya, dan memang disengaja. Efisiensi pengodean dan dukungan browser di antara format-format modern punya artikelnya sendiri: WebP vs AVIF vs JPEG.
Apa yang WebP lakukan pada 83 file yang sama
Kumpulan 83 file yang 100% membesar lewat canvas PNG, kali ini kami konversi ke WebP pada kualitas 0.8: 83 dari 83 mengecil, median −94.2%. File tunggal yang tadi disebut, aes-decrypt.png, turun dari 505,516 B ke 27,188 B, −94.6%.
File terkontrolnya sepakat. og-a.png: 306,302 sebagai PNG, 13,852 sebagai WebP q80. photo.png: 498,639 sebagai PNG, 24,068 sebagai WebP q80. photo.jpg: 47,828 sebagai JPEG, 23,862 sebagai WebP q80.
Satu catatan untuk perbandingan terakhir itu. WebP pada kualitas 0.8 bersifat lossy, jadi ia tidak bersaing dengan PNG di atas dasar yang setara, dan mengodekan ulang JPEG yang sudah ada menjadi WebP tetap memakan satu generasi. Bandingkan dengan apa yang tadinya hendak Anda kirimkan, bukan dengan file asli sempurna yang hipotetis.
Kapan Anda sebaiknya tetap mempertahankan PNG
Lossless kadang memang syarat, bukan preferensi. Pertahankan PNG untuk aset yang akan kembali ke pipeline desain dan disunting lagi, untuk tangkapan layar yang Anda pakai dalam pengujian perbandingan piksel, untuk potongan UI yang diff visualnya rusak hanya karena satu warna bergeser, dan untuk apa pun yang nanti akan dikomposisi lagi sehingga artefak kuantisasi akan menumpuk. Dalam kasus-kasus itu, jalankan encoder dengan kuantisasi yang benar kalau kontennya memungkinkan, dan terima ukuran filenya kalau tidak.
Satu jenis “membesar” lagi yang sama sekali tidak berkaitan dengan kompresi
Kalau Anda menyematkan gambar sebagai data URI, ukuran di dalam CSS atau HTML Anda bukanlah ukuran di disk. Base64 mengodekan setiap 3 byte menjadi 4 karakter, ditambah padding, sehingga teksnya secara aritmetis +33% lebih besar daripada byte yang dibawanya, bahkan sebelum kompresi transfer apa pun. Gambar yang sudah dioptimalkan dengan sempurna pun tetap bertambah sepertiga begitu Anda menyematkannya. Panduan kami tentang penyematan data URI membahas kapan pertukaran itu layak Anda ambil.
7. Mengerjakannya: browser, baris perintah, pipeline build
Di browser
Saat memilih alat berbasis browser, tanyakan apakah PNG-nya melewati canvas. Di kompresor gambar kami, jawabannya tidak. Alat itu mengkuantisasi masukan PNG menjadi palet warna lalu menulisnya kembali sebagai PNG sungguhan dengan kanal alpha yang utuh, sehingga transparansi dan tepi lembut tetap selamat; slider kualitasnya memetakan ke ukuran palet, bukan ke argumen toBlob yang toh akan diabaikan PNG. Kualitas 100 memetakan ke pengodean ulang lossless. JPEG dan WebP memang melewati canvas, dan di sana parameter kualitasnya nyata serta bekerja seperti yang Anda harapkan.
Ada satu perilaku yang perlu Anda ketahui karena ia tampak seperti bug padahal bukan: kalau hasil kompresinya tidak lebih kecil daripada file yang Anda unggah, alat itu membuang keluarannya sendiri dan mempertahankan byte asli Anda. Pada PNG yang sudah dioptimalkan, Anda akan melihat “0% dihemat”. Itu bagian 4 yang sedang bekerja sebagaimana mestinya.
Pemrosesannya berlangsung secara lokal, jadi tidak ada yang terunggah ke mana pun.
Di baris perintah
cwebp hadir bersama libwebp dan merupakan cara tercepat untuk menguji apakah pergantian format menyelesaikan masalah Anda:
# WebP lossy, kualitas 0-100
cwebp -q 80 photo.png -o photo.webp
# WebP lossless, upaya kompresi 0-9
cwebp -z 9 logo.png -o logo.webp
ImageMagick 7 menutup kasus konversi dan kuantisasi:
# PNG ke JPEG pada kualitas pilihan Anda
magick photo.png -quality 80 photo.jpg
# Kuantisasi menjadi PNG berpalet 64 warna
magick logo.png -colors 64 PNG8:logo-64.png
# Buang EXIF dan metadata lainnya
magick photo.jpg -strip photo-clean.jpg
Di macOS, sips sudah terpasang dan tidak butuh dependensi apa pun:
# Konversi ke JPEG; formatOptions menerima 0-100 atau low/normal/high/best
sips -s format jpeg -s formatOptions 80 photo.png --out photo.jpg
# Ubah ukuran agar sisi terpanjang menjadi 1200 px, rasio aspek dipertahankan
sips -Z 1200 photo.jpg --out photo-1200.jpg
# Baca kembali dimensinya
sips -g pixelWidth -g pixelHeight photo-1200.jpg
Ubah ukurannya dulu, baru kompres. Encoder bekerja pada piksel, dan piksel termurah adalah piksel yang tidak ada.
Di dalam pipeline build
Begitu ini berjalan otomatis alih-alih manual, keputusannya bergeser ke di mana pekerjaan itu terjadi dan pustaka mana yang mengerjakannya: kompresi gambar di browser versus Node.js membahas perbandingan tersebut. Satu aturan dari artikel ini yang tetap berlaku di sana: kompres dari file sumber asli pada setiap build, jangan pernah dari keluaran build sebelumnya. Begitulah caranya sebuah pipeline menuruni kurva generation loss sendiri sementara ukuran filenya terlihat stabil sempurna.
8. Lima keyakinan yang tidak didukung hasil pengukuran
“Mengompres dua kali membuatnya lebih kecil.” Generasi 1 memberi 1.4%. Generasi 2 sampai 5 bertahan dalam ±12 byte sementara gambarnya terus memburuk. Putaran kedua murni biaya.
“PNG itu lossless, jadi ia format yang lebih baik.” Lossless adalah sebuah sifat, bukan keutamaan. Foto uji kami berukuran 498,639 byte sebagai PNG dan 24,068 sebagai WebP q80. Mempertahankan sebuah foto sampai ke tingkat bit padahal ia hanya akan dilihat di layar tidak memberi keuntungan apa pun dan memakan sebagian besar ukuran filenya.
“Kualitas 100 adalah pilihan yang aman.” Mengompres ulang JPEG berkualitas 82 pada kualitas 92 menghasilkan +22.9% dan gambar yang lebih buruk. Di atas setelan kualitas file aslinya, angka itu berhenti berarti “lebih aman” dan mulai berarti “lebih besar”.
“Filenya besar karena resolusinya tinggi.” Resolusi memang berpengaruh, tetapi pada resolusi yang sama format lebih berpengaruh. og-a.png sama-sama 1200×630: 607,481 byte sebagai PNG dari canvas, 13,852 byte sebagai WebP q80. Jumlah pikselnya identik.
“Kompresor online semuanya sama saja.” Piksel sama, browser sama, sama-sama lossless: 607,481 byte dari canvas, 175,491 dari upng-js. Rentang 3.46× antara dua alat yang mendeskripsikan dirinya dengan cara yang identik.
9. FAQ
Mengapa gambar hasil kompresi saya lebih besar daripada aslinya?
Karena alatnya mengode ulang gambar itu, bukan mengompresnya. Keluaran canvas browser selalu berupa PNG RGBA 32-bit, yang membuang palet dan menyimpan empat byte per piksel. Dalam pengujian kami terhadap 83 PNG asli, ke-83-nya membesar, dengan kenaikan median +76.6%. Konversikan ke WebP atau JPEG saja.
Mengapa PNG saya tidak mengecil ketika saya kompres?
PNG bersifat lossless, jadi slider kualitas tidak punya apa pun untuk ditukar. Reduksi yang nyata datang dari memangkas jumlah warna, dan kalau file itu sudah pernah dikuantisasi, tidak ada lagi yang bisa dipangkas. PNG uji 64 warna kami kembali di +2.5% setelah dikuantisasi ke 64 warna untuk kedua kalinya.
Apakah mengompres JPEG dua kali menurunkan kualitas?
Ya, dan Anda nyaris tidak mendapat apa-apa sebagai gantinya. Lima putaran pada kualitas 0.8: 47,828 byte turun ke 47,152 pada putaran pertama, lalu terkunci dalam 12 byte selama empat putaran berikutnya. Ukurannya berhenti bergerak sementara setiap putaran terus mengkuantisasi ulang gambarnya. Simpan file asli Anda.
Sebaiknya pakai PNG atau JPEG untuk memperkecil file?
Untuk foto, JPEG atau WebP, tanpa kecuali. Foto uji kami terukur 498,639 byte sebagai PNG, 45,050 sebagai JPEG q80, dan 24,068 sebagai WebP q80. Simpan PNG untuk grafik datar, teks yang tajam, dan transparansi; di sana palet kecillah yang mengerjakan kompresinya.
Mengapa gambar saya jadi buram setelah dikompres?
Ada dua penyebab yang berbeda. Encoder lossy pada setelan kualitas rendah menghasilkan blocking yang kasatmata di sekitar tepi dan teks. Putaran yang berulang menambahkan generation loss bahkan ketika ukuran filenya berhenti berubah. Kalau warnanya yang bergeser alih-alih melembut, perjalanan bolak-balik lewat canvas sudah membuang profil ICC Anda, karena canvas sama sekali tidak membawa metadata.
Bisakah saya mengompres gambar tanpa kehilangan kualitas?
Bisa, tetapi jangan berharap banyak. Pengodean ulang lossless hanya menulis ulang piksel yang identik dengan cara yang lebih efisien: 306,302 ke 175,491 byte pada grafik kami, terverifikasi identik byte demi byte setelah didekode. Pendekatan yang sama pada foto justru berbalik arah, +17.9%. Untuk penghematan nyata tanpa kehilangan yang kasatmata, pakai WebP pada kualitas 80.
Mengapa PNG saya begitu besar padahal cuma tangkapan layar?
Tangkapan layar tersimpan sebagai PNG RGBA penuh, empat byte per piksel sebelum kompresi, dan layar Retina menggandakan jumlah piksel di tiap dimensi. Konten datar merespons reduksi palet dengan baik: grafik kartu Open Graph kami turun 63.8% di 256 warna dan 75.1% di 64 warna.
Apakah mengubah ukuran lebih menurunkan ukuran file daripada mengompres?
Biasanya iya, dan keduanya saling menguatkan. Memangkas kedua dimensi menjadi separuh membuang tiga perempat pikselnya sebelum encoder mulai bekerja, dan ukuran file secara garis besar mengikuti jumlah piksel. Ubah dulu ke dimensi yang benar-benar Anda tampilkan, baru kompres sekali. File kamera beresolusi penuh yang ditaruh begitu saja di slot thumbnail menyia-nyiakan kedua langkah itu.