Sepertinya jawaban yang tepat untuk ini adalah dengan melewatkan ContentPipeline dan menggunakan Texture2D.FromStream untuk memuat tekstur saat runtime. Metode ini berfungsi dengan baik di PC dan meskipun akan ada kinerja kecil hit ini adalah sesuatu yang bisa saya optimalkan begitu saya lebih dekat dengan tanggal rilis. Untuk saat ini, memiliki kemampuan memodifikasi konten untuk editor dan game secara dinamis adalah hal yang saya butuhkan. Setelah konten dibekukan, saya dapat mengoptimalkan ini dengan kembali ke ContentPipeline.
Karena Anda telah memilih rute ini, saya harus memperingatkan Anda bahwa itu sebenarnya tidak sesederhana hanya menggunakan Texture2D.FromStream
karena dua alasan:
Masalah # 1 - Kurangnya dukungan alpha pra-dikalikan
XNA4 sekarang menangani tekstur dengan warna dalam format alfa pra-ditanamkan secara default. Ketika Anda memuat tekstur melalui pipa konten, pemrosesan itu dilakukan untuk Anda secara otomatis. Sayangnya Texture2D.FromStream
tidak melakukan hal yang sama, sehingga tekstur apa pun yang memerlukan tingkat transparansi tertentu akan dimuat dan ditampilkan secara tidak benar. Di bawah ini adalah tangkapan layar untuk menggambarkan masalah:
Jadi untuk mendapatkan hasil yang benar, Anda perlu melakukan pemrosesan sendiri. Metode yang akan saya tunjukkan menggunakan GPU untuk melakukan pemrosesan sehingga cukup cepat. Itu didasarkan pada artikel bagus ini . Tentu saja Anda juga bisa menginstruksikan SpriteBatch
untuk merender dalam mode NonPremultiplyAlpha lama tapi saya tidak benar-benar merekomendasikan melakukan itu.
Masalah # 2 - Format yang tidak didukung
Pipa konten mendukung lebih banyak format daripada Texture2D.FromStream
. Secara khusus, Texture2D.FromStream
hanya mendukung png, jpg, dan gif. Di sisi lain, pipa konten mendukung bmp, dds, dib, hdr, jpg, pfm, png, ppm, dan tga. Jika Anda mencoba memuat format yang kami gunakan, Texture2D.FromStream
Anda akan mendapat InvalidOperationException
sedikit informasi tambahan.
Saya benar-benar membutuhkan dukungan bmp di mesin saya jadi untuk kasus khusus itu saya menemukan solusi yang tampaknya berfungsi dengan baik. Saya tidak tahu tentang format lainnya. Tangkapan dengan metode saya adalah bahwa Anda perlu menambahkan referensi ke System.Drawing
perakitan ke proyek Anda, karena menggunakan GDI Image.FromStream
yang mendukung lebih banyak format daripada Texture2D.FromStream
.
Jika Anda tidak peduli tentang dukungan bmp, Anda dapat dengan mudah menjatuhkan bagian dari solusi saya dan cukup melakukan pemrosesan alfa yang telah dikalikan.
Solusi - Versi Sederhana (Lebih Lambat)
Pertama-tama, inilah solusi paling sederhana jika Anda tidak peduli tentang dukungan bmps. Dalam contoh ini, tahap pemrosesan dilakukan sepenuhnya pada CPU. Ini sedikit lebih lambat daripada alternatif yang akan saya tunjukkan di bawah ini (saya melakukan benchmark kedua solusi) tetapi lebih mudah untuk dipahami:
public static Texture2D FromStream(GraphicsDevice graphicsDevice, Stream stream)
{
Texture2D texture = Texture2D.FromStream(graphicsDevice, stream);
Color[] data = new Color[texture.Width * texture.Height];
texture.GetData(data);
for (int i = 0; i != data.Length; ++i)
data[i] = Color.FromNonPremultiplied(data[i].ToVector4());
texture.SetData(data);
return texture;
}
Jika Anda peduli tentang bmps maka hal yang perlu Anda lakukan adalah memuat gambar dengan GDI terlebih dahulu dan kemudian dikonversi menjadi PNG secara internal sebelum meneruskannya Texture2D.FromStream
. Berikut kode yang melakukan itu:
// Load image using GDI because Texture2D.FromStream doesn't support BMP
using (Image image = Image.FromStream(stream))
{
// Now create a MemoryStream which will be passed to Texture2D after converting to PNG internally
using (MemoryStream ms = new MemoryStream())
{
image.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
ms.Seek(0, SeekOrigin.Begin);
texture = Texture2D.FromStream(_graphicsDevice, ms);
}
}
Solusi - Versi Kompleks (Lebih Cepat)
Akhirnya, pendekatan yang saya gunakan pada proyek saya adalah menggunakan GPU untuk melakukan pemrosesan. Dalam metode ini Anda perlu membuat target render, mengatur beberapa negara campuran dengan benar, dan menggambar dua kali gambar dengan SpriteBatch. Pada akhirnya saya membahas seluruh RenderTarget2D dan mengkloning konten menjadi objek Texture2D yang terpisah karena RenderTarget2D mudah berubah dan tidak akan bertahan seperti mengubah ukuran backbuffer sehingga lebih aman untuk membuat salinan.
Yang lucu adalah bahwa bahkan dengan semua ini, pada tes saya pendekatan ini dilakukan sekitar 3 kali lebih cepat daripada pendekatan CPU. Jadi secara definitif lebih cepat daripada melewati setiap piksel dan menghitung warnanya sendiri. Kode ini agak panjang jadi saya menempatkannya di pastebin:
http://pastie.org/3651642
Cukup tambahkan kelas itu ke proyek Anda, dan gunakan sesederhana itu:
TextureLoader textureLoader = new TextureLoader(GraphicsDevice);
Texture2D texture = textureLoader.FromFile("Content/texture.png");
Catatan: Anda hanya perlu membuat satu TextureLoader
instance untuk seluruh game. Saya juga menggunakan perbaikan BMP tetapi Anda bisa mengeluarkannya jika Anda tidak perlu dan mendapatkan banyak kinerja, atau biarkan saja needsBmp
parameternya salah.