Nonaktifkan Interpolasi saat Menskalakan <canvas>

126

CATATAN : Ini ada hubungannya dengan bagaimana elemen kanvas yang ada dirender saat diperbesar , bukan dengan bagaimana garis atau grafik dirender ke permukaan kanvas . Dengan kata lain, ini memiliki semuanya harus dilakukan dengan interpolasi dari elemen skala , dan tidak ada hubungannya dengan antialiasing grafis yang digambar di atas kanvas. Saya tidak peduli dengan bagaimana browser menggambar garis; Saya peduli tentang bagaimana browser merender elemen kanvas itu sendiri saat diskalakan.


Apakah ada properti kanvas atau setelan browser yang dapat saya ubah secara terprogram untuk menonaktifkan interpolasi saat menskalakan <canvas>elemen? Solusi lintas-browser sangat ideal tetapi tidak penting; Browser berbasis webkit adalah target utama saya. Performa sangat penting.

Pertanyaan ini paling mirip tetapi tidak cukup menggambarkan masalah. Untuk apa nilainya, saya telah mencoba image-rendering: -webkit-optimize-contrasttidak berhasil.

Aplikasi ini akan menjadi game bergaya "retro" 8-bit yang ditulis dalam HTML5 + JS untuk memperjelas apa yang saya butuhkan.


Sebagai ilustrasi, berikut adalah sebuah contoh. ( versi langsung )

Misalkan saya memiliki kanvas 21x21 ...

<canvas id='b' width='21' height='21'></canvas>

... yang memiliki css yang membuat elemen 5 kali lebih besar (105x105):

canvas { border: 5px solid #ddd; }
canvas#b { width: 105px; height: 105px; } /* 5 * 21 = 105 */

Saya menggambar 'X' sederhana di kanvas seperti ini:

$('canvas').each(function () {
    var ctx = this.getContext("2d");
    ctx.moveTo(0,0);
    ctx.lineTo(21,21);
    ctx.moveTo(0,21);
    ctx.lineTo(21,0);
    ctx.stroke();
});

Gambar di sebelah kiri adalah gambar yang dibuat oleh Chromium (14.0). Gambar di sebelah kanan adalah yang saya inginkan (digambar dengan tangan untuk tujuan ilustrasi).

Chrome menginterpolasi elemen kanvas berskala Versi non-interpolasi

namuol
sumber
1
Agak terkait, meski tidak identik: stackoverflow.com/questions/195262/…
HostileFork mengatakan jangan percaya SE
1
Saya percaya pertanyaan itu mengacu pada fungsi penggambaran garis yang menggambar garis anti-alias. Saya mengacu pada bagaimana elemen kanvas yang diubah ukurannya diberikan dengan interpolasi secara default.
namuol
Ya, maaf ... Saya pertama kali berpikir pertanyaannya sama kemudian segera menyadari kesalahan saya. : - / Bagaimana jika Anda mencoba menggunakan filter pixelation dari jsmanipulate? joelb.me/jsmanipulate
HostileFork bilang jangan percaya SE
1
Itu lebih merupakan filter gambar yang dimaksudkan untuk foto.
namuol
Ya, tetapi jika itu mengubah gambar kiri Anda menjadi versi yang cukup baik dari gambar kanan Anda untuk membuat Anda bahagia, maka itu dapat memberikan solusi lintas-browser (mengingat tidak terlalu menjanjikan untuk menemukan bendera untuk mode gambar ini, universal atau sebaliknya , di WebKit). Apakah itu pengganti yang layak tergantung pada masalah yang Anda coba selesaikan ... jika Anda benar-benar membutuhkan gambar piksel diskrit atau jika Anda hanya mencoba untuk memastikan hasilnya pixelated dengan perincian tertentu.
HostileFork bilang jangan percaya SE

Jawaban:

126

Terakhir Diperbarui: 2014-09-12

Apakah ada properti kanvas atau setelan browser yang dapat saya ubah secara terprogram untuk menonaktifkan interpolasi saat menskalakan elemen?

Jawabannya mungkin suatu hari nanti . Untuk saat ini, Anda harus melakukan peretasan untuk mendapatkan apa yang Anda inginkan.


image-rendering

Draf kerja CSS3 menguraikan properti baru,image-rendering yang harus melakukan apa yang saya inginkan:

Properti rendering gambar memberikan petunjuk kepada agen-pengguna tentang aspek gambar apa yang paling penting untuk dipertahankan saat gambar diskalakan, untuk membantu agen-pengguna dalam memilih algoritme penskalaan yang sesuai.

Spesifikasi menguraikan tiga nilai yang diterima: auto, crisp-edges, dan pixelated.

pixelated:

Saat memperbesar gambar, "tetangga terdekat" atau algoritme serupa harus digunakan, sehingga gambar tampak hanya terdiri dari piksel yang sangat besar. Saat memperkecil, ini sama dengan otomatis.

Standar? Lintas browser?

Karena ini baru draft kerja , tidak ada jaminan akan menjadi standar. Dukungan browser saat ini sangat buruk.

Jaringan Pengembang Mozilla memiliki halaman yang cukup menyeluruh yang didedikasikan untuk keadaan seni saat ini yang sangat saya sarankan untuk dibaca.

Pengembang Webkit awalnya memilih untuk menerapkan ini sebagai tentatif-webkit-optimize-contrast , tetapi Chromium / Chrome tampaknya tidak menggunakan versi Webkit yang menerapkan ini.

Perbarui: 2014-09-12

Chrome 38 sekarang mendukungimage-rendering: pixelated !

Firefox memiliki laporan bug yang terbuka untuk image-rendering: pixelatedditerapkan, tetapi -moz-crisp-edgesberfungsi untuk saat ini.

Larutan?

Solusi paling lintas platform dan khusus CSS sejauh ini adalah:

canvas {
  image-rendering: optimizeSpeed;             /* Older versions of FF          */
  image-rendering: -moz-crisp-edges;          /* FF 6.0+                       */
  image-rendering: -webkit-optimize-contrast; /* Safari                        */
  image-rendering: -o-crisp-edges;            /* OS X & Windows Opera (12.02+) */
  image-rendering: pixelated;                 /* Awesome future-browsers       */
  -ms-interpolation-mode: nearest-neighbor;   /* IE                            */
}

Sayangnya ini belum berfungsi pada semua platform HTML5 utama (Chrome, khususnya).

Tentu saja, seseorang dapat secara manual memperbesar gambar menggunakan interpolasi tetangga terdekat ke permukaan kanvas resolusi tinggi dalam javascript, atau bahkan gambar skala sebelumnya di sisi server, tetapi dalam kasus saya ini akan sangat mahal sehingga ini bukan opsi yang layak.

ImpactJS menggunakan teknik penskalaan tekstur untuk mengatasi semua FUD ini. Pengembang Impact, Dominic Szablewski, menulis artikel yang sangat mendalam tentang ini (dia bahkan mengutip pertanyaan ini dalam penelitiannya).

Lihat jawaban Simon untuk solusi berbasis kanvas yang bergantung pada imageSmoothingEnabledproperti (tidak tersedia di browser lama, tetapi lebih sederhana daripada penskalaan awal dan cukup banyak didukung).

Demo Langsung

Jika Anda ingin menguji properti CSS yang dibahas dalam artikel MDN tentang canvaselemen, saya telah membuat biola ini yang akan menampilkan sesuatu seperti ini, buram atau tidak, tergantung pada browser Anda: gambar 4: 1 (64x64 hingga 256x256) dari TV gaya piksel isometrik

namuol
sumber
Perenderan gambar tampaknya berfungsi di tempat lain, kecuali di browser webkit. Saya sangat berharap staf Chrome / Chromium / Safari membaca dengan baik apa arti "beralih ke interpolasi EPX dalam situasi beban rendah". Ketika saya pertama kali membaca kalimat tersebut, saya berpikir bahwa mengoptimalkan-kontras memungkinkan warna baru dibuat dalam beban rendah, tetapi TIDAK: en.wikipedia.org/wiki/…
Timo Kähkönen
2
Opera 12.02 di Mac dan Windows mendukung rendering gambar: -o-crisp-edge ( jsfiddle.net/VAXrL/21 ), jadi Anda dapat menambahkannya pada jawaban Anda.
Timo Kähkönen
Apakah ini seharusnya berfungsi saat saya meningkatkan zoom browser? Saya telah menguji gambar dengan FF 22, Chrome 27 dan IE 10 pada Windows 7 ketika zoom browser 150% dan hasilnya kabur di semua browser. Ketika saya menggandakan lebar dan tinggi kanvas dan mengaturnya kembali ke lebar dan tinggi asli dengan CSS itu menggambar garis tajam.
pablo
Itu pertanyaan yang bagus. Saya tidak yakin apakah ada spesifikasi untuk perilaku penskalaan yang ditentukan pengguna.
namuol
1
Saya (dari masa sekarang!) Menggunakan Chrome 78. Saya melihat "image-rendering: pixelated;" kerja. Sepertinya ini ditambahkan pada 2015 ke Chrome 41: developers.google.com/web/updates/2015/01/pixelated
MrColes
61

Jawaban baru 31/7/2012

Ini akhirnya ada di spesifikasi kanvas!

Spesifikasi baru-baru ini menambahkan properti yang dipanggil imageSmoothingEnabled, yang defaultnya truedan menentukan apakah gambar yang digambar pada koordinat non-integer atau skala yang digambar akan menggunakan algoritme yang lebih halus. Jika disetel ke falsetetangga terdekat digunakan, menghasilkan gambar yang kurang halus dan malah membuat piksel tampak lebih besar.

Perataan gambar baru saja ditambahkan ke spesifikasi kanvas dan tidak didukung oleh semua browser, tetapi beberapa browser telah menerapkan versi properti ini yang diawali vendor. Pada konteks yang ada mozImageSmoothingEnableddi Firefox dan webkitImageSmoothingEnableddi Chrome dan Safari, dan pengaturan ini ke false akan menghentikan anti-aliasing agar tidak terjadi. Sayangnya, pada saat penulisan, IE9 dan Opera belum mengimplementasikan properti ini, diawali vendor atau sebaliknya.


Pratinjau: JSFiddle

Hasil:

masukkan deskripsi gambar di sini

Simon Sarris
sumber
1
Sayangnya, sepertinya ini juga belum berfungsi. Mungkin aku melewatkan sesuatu? Ini tesnya: jsfiddle.net/VAXrL/187
namuol
12
Anda harus menskalakan menggunakan API kanvas agar berfungsi, Anda tidak dapat menskalakan dengan CSS dan memiliki API kanvas yang memengaruhinya! Jadi sesuatu seperti ini: jsfiddle.net/VAXrL/190
Simon Sarris
1
Tapi sifat dari masalah ini sebenarnya bukan tentang kanvas. Ini tentang bagaimana browser merender gambar berskala, termasuk elemen <canvas>.
namuol
4
Jika Anda menskalakan menggunakan CSS, solusi namuol akan berfungsi. Jika Anda mengubah skala gambar secara manual ke Canvas, solusi Simon berfungsi. Sangat rapi bahwa ada solusi untuk kedua kasus tersebut, jadi terima kasih untuk Anda berdua!
Shaun Lebron
Untuk IE 11: msImageSmoothingEnabled berfungsi untuk saya! msdn.microsoft.com/en-us/library/dn265062(v=vs.85).aspx
steve
11

Sunting 31/7/2012 - Fungsi ini sekarang ada di spesifikasi kanvas! Lihat jawaban terpisah di sini:

https://stackoverflow.com/a/11751817/154112

Jawaban lama ada di bawah untuk anak cucu.


Bergantung pada efek yang Anda inginkan, Anda memiliki ini sebagai salah satu opsi:

var can = document.getElementById('b');
var ctx = can.getContext('2d');
ctx.scale(5,5);
$('canvas').each(function () {
    var ctx = this.getContext("2d");
    ctx.moveTo(0,0);
    ctx.lineTo(21,21);
    ctx.moveTo(0,21);
    ctx.lineTo(21,0);
    ctx.stroke();
});

http://jsfiddle.net/wa95p/

Yang menciptakan ini:

masukkan deskripsi gambar di sini

Mungkin bukan yang Anda inginkan. Tetapi jika Anda hanya ingin memiliki zero blur maka itu adalah tiketnya, jadi saya akan menawarkannya untuk berjaga-jaga.

Opsi yang lebih sulit adalah menggunakan manipulasi piksel dan menulis algoritme sendiri untuk pekerjaan itu. Setiap piksel dari gambar pertama menjadi blok piksel 5x5 pada gambar baru. Tidak akan terlalu sulit untuk melakukannya dengan imagedata.

Tetapi Canvas dan CSS saja tidak akan membantu Anda di sini untuk menskalakan satu sama lain dengan efek tepat yang Anda inginkan.

Simon Sarris
sumber
Ya, itulah yang saya takutkan. Tampaknya, image-rendering: -webkit-optimize-contrastharus melakukan trik tetapi sepertinya tidak melakukan apa-apa. Saya mungkin melihat ke dalam menggulung fungsi untuk memperbesar konten kanvas menggunakan interpolasi tetangga terdekat.
namuol
Saya awalnya menerima jawaban Anda; Saya mencabutnya untuk saat ini karena sepertinya ada kebingungan apakah ini duplikat atau bukan.
namuol
Saya memiliki jawaban yang lebih lengkap setelah melakukan penelitian dan ingin membuka kembali pertanyaan untuk memberikan jawaban saya.
namuol
3

Di google chrome, pola gambar kanvas tidak diinterpolasi.

Berikut adalah contoh kerja yang diedit dari jawaban namuol http://jsfiddle.net/pGs4f/

ctx.scale(4, 4);
ctx.fillStyle = ctx.createPattern(image, 'repeat');
ctx.fillRect(0, 0, 64, 64);
saviski
sumber
Ini adalah solusi paling menarik yang pernah saya lihat sejauh ini. Belum yakin dengan kinerjanya, tetapi sangat layak untuk dilihat. Saya akan mencobanya.
namuol
menggunakan ulangi alih-alih tidak mengulang lebih cepat. Tidak tahu kenapa. Ini juga cukup cepat, three.js menggunakan ini di CanvasRenderer
saviski
6
pembaruan: tidak lagi berfungsi dengan chrome 21 (karena dukungan tampilan retina ditambahkan?) versi baru jsfiddle.net/saviski/pGs4f/12 menggunakan imageSmoothingEnabled yang ditunjuk oleh Simon
saviski
Ketika resolusi Retina menjadi umum, banyak hal berubah sangat besar. Jika tampilan Retina iPhone4-5 gaya 326dpi (128dpcm) hadir pada monitor 24 inci (52x32cm), ukuran layarnya akan menjadi 6656 x 4096 px. Kemudian antialiasing itu buruk, dan semua vendor browser (bahkan berbasis Webkit) terpaksa mengizinkan antialiasing dinonaktifkan. Antialiasing adalah operasi intensif cpu dan gambar antialiasing 6656 x 4096 px akan terlalu lambat, tetapi untungnya tidak diperlukan dalam tampilan seperti itu.
Timo Kähkönen
1
Sekadar catatan, berikut patokan yang baik tentang pola vs gambar: jsperf.com/drawimage-vs-canvaspattern/9
yckart
1

Solusi Saviski yang dijelaskan di sini cukup menjanjikan, karena berhasil pada:

  • Chrome 22.0.1229.79 Mac OS X 10.6.8
  • Chrome 22.0.1229.79 m Windows 7
  • Chromium 18.0.1025.168 (Pengembang Bangun 134367 Linux) Ubuntu 11.10
  • Firefox 3.6.25 Windows 7

Namun tidak berfungsi berikut ini, tetapi efek yang sama dapat dicapai menggunakan rendering gambar CSS:

  • Firefox 15.0.1 Mac OS X 10.6.8 (rendering gambar: -moz-crisp-edge berfungsi di sini )
  • Opera 12.02 Mac OS X 10.6.8 (rendering gambar: -o-crisp-edge berfungsi di sini )
  • Opera 12.02 Windows 7 (rendering gambar: -o-crisp-edge berfungsi di sini )

Masalahnya adalah ini, karena ctx.XXXImageSmoothingEnabled tidak berfungsi dan rendering gambar tidak berfungsi:

  • Safari 5.1.7 Mac OS X 10.6.8. (image-rendering: -webkit-optimization-contrast TIDAK berfungsi)
  • Safari 5.1.7 Windows 7 (rendering gambar: -webkit-optimalkan-kontras TIDAK berfungsi)
  • IE 9 Windows 7 (-ms-interpolation-mode: terdekat-tetangga TIDAK berfungsi)
Timo Kähkönen
sumber