Feature-gate all image formats (#15586)

# Objective

Bevy supports feature gates for each format it supports, but several
formats that it loads via the `image` crate do not have feature gates.
Additionally, the QOI format is supported by the `image` crate and
wasn't available at all. This fixes that.

## Solution

The following feature gates are added:

* `avif`
* `ff` (Farbfeld)
* `gif`
* `ico`
* `qoi`
* `tiff`

None of these formats are enabled by default, despite the fact that all
these formats appeared to be enabled by default before. Since
`default-features` was disabled for the `image` crate, it's likely that
using any of these formats would have errored by default before this
change, although this probably needs additional testing.

## Testing

The changes seemed minimal enough that a compile test would be
sufficient.

## Migration guide

Image formats that previously weren't feature-gated are now
feature-gated, meaning they will have to be enabled if you use them:

* `avif`
* `ff` (Farbfeld)
* `gif`
* `ico`
* `tiff`

Additionally, the `qoi` feature has been added to support loading QOI
format images.

Previously, these formats appeared in the enum by default, but weren't
actually enabled via the `image` crate, potentially resulting in weird
bugs. Now, you should be able to add these features to your projects to
support them properly.
This commit is contained in:
Clar Fon 2024-10-07 12:37:45 -04:00 committed by GitHub
parent 0a1d60f3b0
commit 8adc9e9d6e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 394 additions and 149 deletions

View file

@ -100,39 +100,40 @@ unused_qualifications = "warn"
[features] [features]
default = [ default = [
"android-game-activity",
"android-game-activity",
"android_shared_stdcxx",
"animation", "animation",
"bevy_asset", "bevy_asset",
"bevy_state",
"bevy_audio", "bevy_audio",
"bevy_color", "bevy_color",
"bevy_gilrs",
"bevy_scene",
"bevy_winit",
"bevy_core_pipeline", "bevy_core_pipeline",
"bevy_gilrs",
"bevy_gizmos",
"bevy_gltf",
"bevy_pbr", "bevy_pbr",
"bevy_picking", "bevy_picking",
"bevy_sprite_picking_backend", "bevy_remote",
"bevy_ui_picking_backend",
"bevy_gltf",
"bevy_render", "bevy_render",
"bevy_scene",
"bevy_sprite", "bevy_sprite",
"bevy_sprite_picking_backend",
"bevy_state",
"bevy_text", "bevy_text",
"bevy_ui", "bevy_ui",
"bevy_remote", "bevy_ui_picking_backend",
"bevy_winit",
"custom_cursor",
"default_font",
"hdr",
"multi_threaded", "multi_threaded",
"png", "png",
"hdr",
"vorbis",
"x11",
"bevy_gizmos",
"android_shared_stdcxx",
"tonemapping_luts",
"smaa_luts", "smaa_luts",
"default_font",
"webgl2",
"sysinfo_plugin", "sysinfo_plugin",
"android-game-activity", "tonemapping_luts",
"custom_cursor", "vorbis",
"webgl2",
"x11",
] ]
# Provides an implementation for picking sprites # Provides an implementation for picking sprites
@ -242,39 +243,57 @@ trace_tracy_memory = [
# Tracing support # Tracing support
trace = ["bevy_internal/trace"] trace = ["bevy_internal/trace"]
# EXR image format support # AVIF image format support
exr = ["bevy_internal/exr"] avif = ["bevy_internal/avif"]
# HDR image format support
hdr = ["bevy_internal/hdr"]
# PNG image format support
png = ["bevy_internal/png"]
# TGA image format support
tga = ["bevy_internal/tga"]
# JPEG image format support
jpeg = ["bevy_internal/jpeg"]
# BMP image format support
bmp = ["bevy_internal/bmp"]
# WebP image format support
webp = ["bevy_internal/webp"]
# Basis Universal compressed texture support # Basis Universal compressed texture support
basis-universal = ["bevy_internal/basis-universal"] basis-universal = ["bevy_internal/basis-universal"]
# BMP image format support
bmp = ["bevy_internal/bmp"]
# DDS compressed texture support # DDS compressed texture support
dds = ["bevy_internal/dds"] dds = ["bevy_internal/dds"]
# EXR image format support
exr = ["bevy_internal/exr"]
# Farbfeld image format support
ff = ["bevy_internal/ff"]
# GIF image format support
gif = ["bevy_internal/gif"]
# HDR image format support
hdr = ["bevy_internal/hdr"]
# KTX2 compressed texture support # KTX2 compressed texture support
ktx2 = ["bevy_internal/ktx2"] ktx2 = ["bevy_internal/ktx2"]
# ICO image format support
ico = ["bevy_internal/ico"]
# JPEG image format support
jpeg = ["bevy_internal/jpeg"]
# PNG image format support
png = ["bevy_internal/png"]
# PNM image format support, includes pam, pbm, pgm and ppm # PNM image format support, includes pam, pbm, pgm and ppm
pnm = ["bevy_internal/pnm"] pnm = ["bevy_internal/pnm"]
# QOI image format support
qoi = ["bevy_internal/qoi"]
# TGA image format support
tga = ["bevy_internal/tga"]
# TIFF image format support
tiff = ["bevy_internal/tiff"]
# WebP image format support
webp = ["bevy_internal/webp"]
# For KTX2 supercompression # For KTX2 supercompression
zlib = ["bevy_internal/zlib"] zlib = ["bevy_internal/zlib"]

View file

@ -13,12 +13,12 @@ license = "MIT OR Apache-2.0"
keywords = ["bevy"] keywords = ["bevy"]
[features] [features]
dds = ["bevy_render/dds"] dds = ["bevy_render/dds", "bevy_image/dds"]
trace = [] trace = []
webgl = [] webgl = []
webgpu = [] webgpu = []
tonemapping_luts = ["bevy_render/ktx2", "bevy_render/zstd"] tonemapping_luts = ["bevy_render/ktx2", "bevy_image/ktx2", "bevy_image/zstd"]
smaa_luts = ["bevy_render/ktx2", "bevy_render/zstd"] smaa_luts = ["bevy_render/ktx2", "bevy_image/ktx2", "bevy_image/zstd"]
[dependencies] [dependencies]
# bevy # bevy
@ -28,6 +28,7 @@ bevy_core = { path = "../bevy_core", version = "0.15.0-dev" }
bevy_color = { path = "../bevy_color", version = "0.15.0-dev" } bevy_color = { path = "../bevy_color", version = "0.15.0-dev" }
bevy_derive = { path = "../bevy_derive", version = "0.15.0-dev" } bevy_derive = { path = "../bevy_derive", version = "0.15.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev" }
bevy_image = { path = "../bevy_image", version = "0.15.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev" } bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev" }
bevy_render = { path = "../bevy_render", version = "0.15.0-dev" } bevy_render = { path = "../bevy_render", version = "0.15.0-dev" }
bevy_transform = { path = "../bevy_transform", version = "0.15.0-dev" } bevy_transform = { path = "../bevy_transform", version = "0.15.0-dev" }

View file

@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0"
keywords = ["bevy"] keywords = ["bevy"]
[features] [features]
dds = ["bevy_render/dds", "bevy_core_pipeline/dds"] dds = ["bevy_render/dds", "bevy_image/dds", "bevy_core_pipeline/dds"]
pbr_transmission_textures = ["bevy_pbr/pbr_transmission_textures"] pbr_transmission_textures = ["bevy_pbr/pbr_transmission_textures"]
pbr_multi_layer_material_textures = [ pbr_multi_layer_material_textures = [
"bevy_pbr/pbr_multi_layer_material_textures", "bevy_pbr/pbr_multi_layer_material_textures",
@ -26,6 +26,7 @@ bevy_core = { path = "../bevy_core", version = "0.15.0-dev" }
bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.15.0-dev" } bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.15.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev" }
bevy_hierarchy = { path = "../bevy_hierarchy", version = "0.15.0-dev" } bevy_hierarchy = { path = "../bevy_hierarchy", version = "0.15.0-dev" }
bevy_image = { path = "../bevy_image", version = "0.15.0-dev" }
bevy_math = { path = "../bevy_math", version = "0.15.0-dev" } bevy_math = { path = "../bevy_math", version = "0.15.0-dev" }
bevy_pbr = { path = "../bevy_pbr", version = "0.15.0-dev" } bevy_pbr = { path = "../bevy_pbr", version = "0.15.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev", features = [ bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev", features = [

View file

@ -9,15 +9,24 @@ license = "MIT OR Apache-2.0"
keywords = ["bevy"] keywords = ["bevy"]
[features] [features]
png = ["image/png"] # Image formats
exr = ["image/exr"] avif = ["image/avif"]
hdr = ["image/hdr"] basis-universal = ["dep:basis-universal"]
tga = ["image/tga"]
jpeg = ["image/jpeg"]
bmp = ["image/bmp"] bmp = ["image/bmp"]
webp = ["image/webp"]
dds = ["ddsfile"] dds = ["ddsfile"]
exr = ["image/exr"]
ff = ["image/ff"]
gif = ["image/gif"]
hdr = ["image/hdr"]
ktx2 = ["dep:ktx2"]
ico = ["image/ico"]
jpeg = ["image/jpeg"]
png = ["image/png"]
pnm = ["image/pnm"] pnm = ["image/pnm"]
qoi = ["image/qoi"]
tga = ["image/tga"]
tiff = ["image/tiff"]
webp = ["image/webp"]
# For ktx2 supercompression # For ktx2 supercompression
zlib = ["flate2"] zlib = ["flate2"]

View file

@ -29,6 +29,7 @@ pub const SAMPLER_ASSET_INDEX: u64 = 1;
#[derive(Debug, Serialize, Deserialize, Copy, Clone)] #[derive(Debug, Serialize, Deserialize, Copy, Clone)]
pub enum ImageFormat { pub enum ImageFormat {
#[cfg(feature = "avif")]
Avif, Avif,
#[cfg(feature = "basis-universal")] #[cfg(feature = "basis-universal")]
Basis, Basis,
@ -36,12 +37,15 @@ pub enum ImageFormat {
Bmp, Bmp,
#[cfg(feature = "dds")] #[cfg(feature = "dds")]
Dds, Dds,
#[cfg(feature = "ff")]
Farbfeld, Farbfeld,
#[cfg(feature = "gif")]
Gif, Gif,
#[cfg(feature = "exr")] #[cfg(feature = "exr")]
OpenExr, OpenExr,
#[cfg(feature = "hdr")] #[cfg(feature = "hdr")]
Hdr, Hdr,
#[cfg(feature = "ico")]
Ico, Ico,
#[cfg(feature = "jpeg")] #[cfg(feature = "jpeg")]
Jpeg, Jpeg,
@ -51,8 +55,11 @@ pub enum ImageFormat {
Png, Png,
#[cfg(feature = "pnm")] #[cfg(feature = "pnm")]
Pnm, Pnm,
#[cfg(feature = "qoi")]
Qoi,
#[cfg(feature = "tga")] #[cfg(feature = "tga")]
Tga, Tga,
#[cfg(feature = "tiff")]
Tiff, Tiff,
#[cfg(feature = "webp")] #[cfg(feature = "webp")]
WebP, WebP,
@ -71,24 +78,261 @@ macro_rules! feature_gate {
} }
impl ImageFormat { impl ImageFormat {
/// Number of image formats, used for computing other constants.
const COUNT: usize = {
let mut count = 0;
#[cfg(feature = "avif")]
{
count += 1;
}
#[cfg(feature = "basis-universal")]
{
count += 1;
}
#[cfg(feature = "bmp")]
{
count += 1;
}
#[cfg(feature = "dds")]
{
count += 1;
}
#[cfg(feature = "ff")]
{
count += 1;
}
#[cfg(feature = "gif")]
{
count += 1;
}
#[cfg(feature = "exr")]
{
count += 1;
}
#[cfg(feature = "hdr")]
{
count += 1;
}
#[cfg(feature = "ico")]
{
count += 1;
}
#[cfg(feature = "jpeg")]
{
count += 1;
}
#[cfg(feature = "ktx2")]
{
count += 1;
}
#[cfg(feature = "pnm")]
{
count += 1;
}
#[cfg(feature = "png")]
{
count += 1;
}
#[cfg(feature = "qoi")]
{
count += 1;
}
#[cfg(feature = "tga")]
{
count += 1;
}
#[cfg(feature = "tiff")]
{
count += 1;
}
#[cfg(feature = "webp")]
{
count += 1;
}
count
};
/// Full list of supported formats.
pub const SUPPORTED: &'static [ImageFormat] = &[
#[cfg(feature = "avif")]
ImageFormat::Avif,
#[cfg(feature = "basis-universal")]
ImageFormat::Basis,
#[cfg(feature = "bmp")]
ImageFormat::Bmp,
#[cfg(feature = "dds")]
ImageFormat::Dds,
#[cfg(feature = "ff")]
ImageFormat::Farbfeld,
#[cfg(feature = "gif")]
ImageFormat::Gif,
#[cfg(feature = "exr")]
ImageFormat::OpenExr,
#[cfg(feature = "hdr")]
ImageFormat::Hdr,
#[cfg(feature = "ico")]
ImageFormat::Ico,
#[cfg(feature = "jpeg")]
ImageFormat::Jpeg,
#[cfg(feature = "ktx2")]
ImageFormat::Ktx2,
#[cfg(feature = "png")]
ImageFormat::Png,
#[cfg(feature = "pnm")]
ImageFormat::Pnm,
#[cfg(feature = "qoi")]
ImageFormat::Qoi,
#[cfg(feature = "tga")]
ImageFormat::Tga,
#[cfg(feature = "tiff")]
ImageFormat::Tiff,
#[cfg(feature = "webp")]
ImageFormat::WebP,
];
/// Total count of file extensions, for computing supported file extensions list.
const COUNT_FILE_EXTENSIONS: usize = {
let mut count = 0;
let mut idx = 0;
while idx < ImageFormat::COUNT {
count += ImageFormat::SUPPORTED[idx].to_file_extensions().len();
idx += 1;
}
count
};
/// Gets the list of file extensions for all formats.
pub const SUPPORTED_FILE_EXTENSIONS: &'static [&'static str] = &{
let mut exts = [""; ImageFormat::COUNT_FILE_EXTENSIONS];
let mut ext_idx = 0;
let mut fmt_idx = 0;
while fmt_idx < ImageFormat::COUNT {
let mut off = 0;
let fmt_exts = ImageFormat::SUPPORTED[fmt_idx].to_file_extensions();
while off < fmt_exts.len() {
exts[ext_idx] = fmt_exts[off];
off += 1;
ext_idx += 1;
}
fmt_idx += 1;
}
exts
};
/// Gets the file extensions for a given format.
pub const fn to_file_extensions(&self) -> &'static [&'static str] {
match self {
#[cfg(feature = "avif")]
ImageFormat::Avif => &["avif"],
#[cfg(feature = "basis-universal")]
ImageFormat::Basis => &["basis"],
#[cfg(feature = "bmp")]
ImageFormat::Bmp => &["bmp"],
#[cfg(feature = "dds")]
ImageFormat::Dds => &["dds"],
#[cfg(feature = "ff")]
ImageFormat::Farbfeld => &["ff", "farbfeld"],
#[cfg(feature = "gif")]
ImageFormat::Gif => &["gif"],
#[cfg(feature = "exr")]
ImageFormat::OpenExr => &["exr"],
#[cfg(feature = "hdr")]
ImageFormat::Hdr => &["hdr"],
#[cfg(feature = "ico")]
ImageFormat::Ico => &["ico"],
#[cfg(feature = "jpeg")]
ImageFormat::Jpeg => &["jpg", "jpeg"],
#[cfg(feature = "ktx2")]
ImageFormat::Ktx2 => &["ktx2"],
#[cfg(feature = "pnm")]
ImageFormat::Pnm => &["pam", "pbm", "pgm", "ppm"],
#[cfg(feature = "png")]
ImageFormat::Png => &["png"],
#[cfg(feature = "qoi")]
ImageFormat::Qoi => &["qoi"],
#[cfg(feature = "tga")]
ImageFormat::Tga => &["tga"],
#[cfg(feature = "tiff")]
ImageFormat::Tiff => &["tif", "tiff"],
#[cfg(feature = "webp")]
ImageFormat::WebP => &["webp"],
// FIXME: https://github.com/rust-lang/rust/issues/129031
#[allow(unreachable_patterns)]
_ => &[],
}
}
/// Gets the MIME types for a given format.
///
/// If a format doesn't have any dedicated MIME types, this list will be empty.
pub const fn to_mime_types(&self) -> &'static [&'static str] {
match self {
#[cfg(feature = "avif")]
ImageFormat::Avif => &["image/avif"],
#[cfg(feature = "basis-universal")]
ImageFormat::Basis => &["image/basis", "image/x-basis"],
#[cfg(feature = "bmp")]
ImageFormat::Bmp => &["image/bmp", "image/x-bmp"],
#[cfg(feature = "dds")]
ImageFormat::Dds => &["image/vnd-ms.dds"],
#[cfg(feature = "hdr")]
ImageFormat::Hdr => &["image/vnd.radiance"],
#[cfg(feature = "gif")]
ImageFormat::Gif => &["image/gif"],
#[cfg(feature = "ff")]
ImageFormat::Farbfeld => &[],
#[cfg(feature = "ico")]
ImageFormat::Ico => &["image/x-icon"],
#[cfg(feature = "jpeg")]
ImageFormat::Jpeg => &["image/jpeg"],
#[cfg(feature = "ktx2")]
ImageFormat::Ktx2 => &["image/ktx2"],
#[cfg(feature = "png")]
ImageFormat::Png => &["image/png"],
#[cfg(feature = "qoi")]
ImageFormat::Qoi => &["image/qoi", "image/x-qoi"],
#[cfg(feature = "exr")]
ImageFormat::OpenExr => &["image/x-exr"],
#[cfg(feature = "pnm")]
ImageFormat::Pnm => &[
"image/x-portable-bitmap",
"image/x-portable-graymap",
"image/x-portable-pixmap",
"image/x-portable-anymap",
],
#[cfg(feature = "tga")]
ImageFormat::Tga => &["image/x-targa", "image/x-tga"],
#[cfg(feature = "tiff")]
ImageFormat::Tiff => &["image/tiff"],
#[cfg(feature = "webp")]
ImageFormat::WebP => &["image/webp"],
// FIXME: https://github.com/rust-lang/rust/issues/129031
#[allow(unreachable_patterns)]
_ => &[],
}
}
pub fn from_mime_type(mime_type: &str) -> Option<Self> { pub fn from_mime_type(mime_type: &str) -> Option<Self> {
Some(match mime_type.to_ascii_lowercase().as_str() { Some(match mime_type.to_ascii_lowercase().as_str() {
"image/avif" => ImageFormat::Avif, // note: farbfeld does not have a MIME type
"image/avif" => feature_gate!("avif", Avif),
"image/basis" | "image/x-basis" => feature_gate!("basis-universal", Basis),
"image/bmp" | "image/x-bmp" => feature_gate!("bmp", Bmp), "image/bmp" | "image/x-bmp" => feature_gate!("bmp", Bmp),
"image/vnd-ms.dds" => feature_gate!("dds", Dds), "image/vnd-ms.dds" => feature_gate!("dds", Dds),
"image/vnd.radiance" => feature_gate!("hdr", Hdr), "image/vnd.radiance" => feature_gate!("hdr", Hdr),
"image/gif" => ImageFormat::Gif, "image/gif" => feature_gate!("gif", Gif),
"image/x-icon" => ImageFormat::Ico, "image/x-icon" => feature_gate!("ico", Ico),
"image/jpeg" => feature_gate!("jpeg", Jpeg), "image/jpeg" => feature_gate!("jpeg", Jpeg),
"image/ktx2" => feature_gate!("ktx2", Ktx2), "image/ktx2" => feature_gate!("ktx2", Ktx2),
"image/png" => feature_gate!("png", Png), "image/png" => feature_gate!("png", Png),
"image/qoi" | "image/x-qoi" => feature_gate!("qoi", Qoi),
"image/x-exr" => feature_gate!("exr", OpenExr), "image/x-exr" => feature_gate!("exr", OpenExr),
"image/x-portable-bitmap" "image/x-portable-bitmap"
| "image/x-portable-graymap" | "image/x-portable-graymap"
| "image/x-portable-pixmap" | "image/x-portable-pixmap"
| "image/x-portable-anymap" => feature_gate!("pnm", Pnm), | "image/x-portable-anymap" => feature_gate!("pnm", Pnm),
"image/x-targa" | "image/x-tga" => feature_gate!("tga", Tga), "image/x-targa" | "image/x-tga" => feature_gate!("tga", Tga),
"image/tiff" => ImageFormat::Tiff, "image/tiff" => feature_gate!("tiff", Tiff),
"image/webp" => feature_gate!("webp", WebP), "image/webp" => feature_gate!("webp", WebP),
_ => return None, _ => return None,
}) })
@ -96,21 +340,22 @@ impl ImageFormat {
pub fn from_extension(extension: &str) -> Option<Self> { pub fn from_extension(extension: &str) -> Option<Self> {
Some(match extension.to_ascii_lowercase().as_str() { Some(match extension.to_ascii_lowercase().as_str() {
"avif" => ImageFormat::Avif, "avif" => feature_gate!("avif", Avif),
"basis" => feature_gate!("basis-universal", Basis), "basis" => feature_gate!("basis-universal", Basis),
"bmp" => feature_gate!("bmp", Bmp), "bmp" => feature_gate!("bmp", Bmp),
"dds" => feature_gate!("dds", Dds), "dds" => feature_gate!("dds", Dds),
"ff" | "farbfeld" => ImageFormat::Farbfeld, "ff" | "farbfeld" => feature_gate!("ff", Farbfeld),
"gif" => ImageFormat::Gif, "gif" => feature_gate!("gif", Gif),
"exr" => feature_gate!("exr", OpenExr), "exr" => feature_gate!("exr", OpenExr),
"hdr" => feature_gate!("hdr", Hdr), "hdr" => feature_gate!("hdr", Hdr),
"ico" => ImageFormat::Ico, "ico" => feature_gate!("ico", Ico),
"jpg" | "jpeg" => feature_gate!("jpeg", Jpeg), "jpg" | "jpeg" => feature_gate!("jpeg", Jpeg),
"ktx2" => feature_gate!("ktx2", Ktx2), "ktx2" => feature_gate!("ktx2", Ktx2),
"pbm" | "pam" | "ppm" | "pgm" => feature_gate!("pnm", Pnm), "pam" | "pbm" | "pgm" | "ppm" => feature_gate!("pnm", Pnm),
"png" => feature_gate!("png", Png), "png" => feature_gate!("png", Png),
"qoi" => feature_gate!("qoi", Qoi),
"tga" => feature_gate!("tga", Tga), "tga" => feature_gate!("tga", Tga),
"tif" | "tiff" => ImageFormat::Tiff, "tif" | "tiff" => feature_gate!("tiff", Tiff),
"webp" => feature_gate!("webp", WebP), "webp" => feature_gate!("webp", WebP),
_ => return None, _ => return None,
}) })
@ -118,17 +363,21 @@ impl ImageFormat {
pub fn as_image_crate_format(&self) -> Option<image::ImageFormat> { pub fn as_image_crate_format(&self) -> Option<image::ImageFormat> {
Some(match self { Some(match self {
#[cfg(feature = "avif")]
ImageFormat::Avif => image::ImageFormat::Avif, ImageFormat::Avif => image::ImageFormat::Avif,
#[cfg(feature = "bmp")] #[cfg(feature = "bmp")]
ImageFormat::Bmp => image::ImageFormat::Bmp, ImageFormat::Bmp => image::ImageFormat::Bmp,
#[cfg(feature = "dds")] #[cfg(feature = "dds")]
ImageFormat::Dds => image::ImageFormat::Dds, ImageFormat::Dds => image::ImageFormat::Dds,
#[cfg(feature = "ff")]
ImageFormat::Farbfeld => image::ImageFormat::Farbfeld, ImageFormat::Farbfeld => image::ImageFormat::Farbfeld,
#[cfg(feature = "gif")]
ImageFormat::Gif => image::ImageFormat::Gif, ImageFormat::Gif => image::ImageFormat::Gif,
#[cfg(feature = "exr")] #[cfg(feature = "exr")]
ImageFormat::OpenExr => image::ImageFormat::OpenExr, ImageFormat::OpenExr => image::ImageFormat::OpenExr,
#[cfg(feature = "hdr")] #[cfg(feature = "hdr")]
ImageFormat::Hdr => image::ImageFormat::Hdr, ImageFormat::Hdr => image::ImageFormat::Hdr,
#[cfg(feature = "ico")]
ImageFormat::Ico => image::ImageFormat::Ico, ImageFormat::Ico => image::ImageFormat::Ico,
#[cfg(feature = "jpeg")] #[cfg(feature = "jpeg")]
ImageFormat::Jpeg => image::ImageFormat::Jpeg, ImageFormat::Jpeg => image::ImageFormat::Jpeg,
@ -136,8 +385,11 @@ impl ImageFormat {
ImageFormat::Png => image::ImageFormat::Png, ImageFormat::Png => image::ImageFormat::Png,
#[cfg(feature = "pnm")] #[cfg(feature = "pnm")]
ImageFormat::Pnm => image::ImageFormat::Pnm, ImageFormat::Pnm => image::ImageFormat::Pnm,
#[cfg(feature = "qoi")]
ImageFormat::Qoi => image::ImageFormat::Qoi,
#[cfg(feature = "tga")] #[cfg(feature = "tga")]
ImageFormat::Tga => image::ImageFormat::Tga, ImageFormat::Tga => image::ImageFormat::Tga,
#[cfg(feature = "tiff")]
ImageFormat::Tiff => image::ImageFormat::Tiff, ImageFormat::Tiff => image::ImageFormat::Tiff,
#[cfg(feature = "webp")] #[cfg(feature = "webp")]
ImageFormat::WebP => image::ImageFormat::WebP, ImageFormat::WebP => image::ImageFormat::WebP,
@ -145,24 +397,28 @@ impl ImageFormat {
ImageFormat::Basis => return None, ImageFormat::Basis => return None,
#[cfg(feature = "ktx2")] #[cfg(feature = "ktx2")]
ImageFormat::Ktx2 => return None, ImageFormat::Ktx2 => return None,
// FIXME: https://github.com/rust-lang/rust/issues/129031
#[allow(unreachable_patterns)]
_ => return None,
}) })
} }
pub fn from_image_crate_format(format: image::ImageFormat) -> Option<ImageFormat> { pub fn from_image_crate_format(format: image::ImageFormat) -> Option<ImageFormat> {
Some(match format { Some(match format {
image::ImageFormat::Avif => ImageFormat::Avif, image::ImageFormat::Avif => feature_gate!("avif", Avif),
image::ImageFormat::Bmp => feature_gate!("bmp", Bmp), image::ImageFormat::Bmp => feature_gate!("bmp", Bmp),
image::ImageFormat::Dds => feature_gate!("dds", Dds), image::ImageFormat::Dds => feature_gate!("dds", Dds),
image::ImageFormat::Farbfeld => ImageFormat::Farbfeld, image::ImageFormat::Farbfeld => feature_gate!("ff", Farbfeld),
image::ImageFormat::Gif => ImageFormat::Gif, image::ImageFormat::Gif => feature_gate!("gif", Gif),
image::ImageFormat::OpenExr => feature_gate!("exr", OpenExr), image::ImageFormat::OpenExr => feature_gate!("exr", OpenExr),
image::ImageFormat::Hdr => feature_gate!("hdr", Hdr), image::ImageFormat::Hdr => feature_gate!("hdr", Hdr),
image::ImageFormat::Ico => ImageFormat::Ico, image::ImageFormat::Ico => feature_gate!("ico", Ico),
image::ImageFormat::Jpeg => feature_gate!("jpeg", Jpeg), image::ImageFormat::Jpeg => feature_gate!("jpeg", Jpeg),
image::ImageFormat::Png => feature_gate!("png", Png), image::ImageFormat::Png => feature_gate!("png", Png),
image::ImageFormat::Pnm => feature_gate!("pnm", Pnm), image::ImageFormat::Pnm => feature_gate!("pnm", Pnm),
image::ImageFormat::Qoi => feature_gate!("qoi", Qoi),
image::ImageFormat::Tga => feature_gate!("tga", Tga), image::ImageFormat::Tga => feature_gate!("tga", Tga),
image::ImageFormat::Tiff => ImageFormat::Tiff, image::ImageFormat::Tiff => feature_gate!("tiff", Tiff),
image::ImageFormat::WebP => feature_gate!("webp", WebP), image::ImageFormat::WebP => feature_gate!("webp", WebP),
_ => return None, _ => return None,
}) })

View file

@ -28,21 +28,35 @@ detailed_trace = ["bevy_utils/detailed_trace"]
sysinfo_plugin = ["bevy_diagnostic/sysinfo_plugin"] sysinfo_plugin = ["bevy_diagnostic/sysinfo_plugin"]
# Image format support for texture loading (PNG and HDR are enabled by default) # Texture formats that have specific rendering support (HDR enabled by default)
exr = ["bevy_render/exr"] basis-universal = ["bevy_image/basis-universal", "bevy_render/basis-universal"]
hdr = ["bevy_render/hdr"] dds = [
png = ["bevy_render/png"] "bevy_image/dds",
tga = ["bevy_render/tga"] "bevy_render/dds",
jpeg = ["bevy_render/jpeg"] "bevy_core_pipeline/dds",
bmp = ["bevy_render/bmp"] "bevy_gltf/dds",
webp = ["bevy_render/webp"] ]
basis-universal = ["bevy_render/basis-universal"] exr = ["bevy_image/exr", "bevy_render/exr"]
dds = ["bevy_render/dds", "bevy_core_pipeline/dds", "bevy_gltf/dds"] hdr = ["bevy_image/hdr", "bevy_render/hdr"]
pnm = ["bevy_render/pnm"] ktx2 = ["bevy_image/ktx2", "bevy_render/ktx2"]
ktx2 = ["bevy_render/ktx2"]
# For ktx2 supercompression # For ktx2 supercompression
zlib = ["bevy_render/zlib"] zlib = ["bevy_image/zlib"]
zstd = ["bevy_render/zstd"] zstd = ["bevy_image/zstd"]
# Image format support (PNG enabled by default)
avif = ["bevy_image/avif"]
bmp = ["bevy_image/bmp"]
ff = ["bevy_image/ff"]
gif = ["bevy_image/gif"]
ico = ["bevy_image/ico"]
jpeg = ["bevy_image/jpeg"]
png = ["bevy_image/png"]
pnm = ["bevy_image/pnm"]
qoi = ["bevy_image/qoi"]
tga = ["bevy_image/tga"]
tiff = ["bevy_image/tiff"]
webp = ["bevy_image/webp"]
# Enable SPIR-V passthrough # Enable SPIR-V passthrough
spirv_shader_passthrough = ["bevy_render/spirv_shader_passthrough"] spirv_shader_passthrough = ["bevy_render/spirv_shader_passthrough"]
@ -258,6 +272,7 @@ bevy_dev_tools = { path = "../bevy_dev_tools", optional = true, version = "0.15.
bevy_gilrs = { path = "../bevy_gilrs", optional = true, version = "0.15.0-dev" } bevy_gilrs = { path = "../bevy_gilrs", optional = true, version = "0.15.0-dev" }
bevy_gizmos = { path = "../bevy_gizmos", optional = true, version = "0.15.0-dev", default-features = false } bevy_gizmos = { path = "../bevy_gizmos", optional = true, version = "0.15.0-dev", default-features = false }
bevy_gltf = { path = "../bevy_gltf", optional = true, version = "0.15.0-dev" } bevy_gltf = { path = "../bevy_gltf", optional = true, version = "0.15.0-dev" }
bevy_image = { path = "../bevy_image", optional = true, version = "0.15.0-dev" }
bevy_pbr = { path = "../bevy_pbr", optional = true, version = "0.15.0-dev" } bevy_pbr = { path = "../bevy_pbr", optional = true, version = "0.15.0-dev" }
bevy_picking = { path = "../bevy_picking", optional = true, version = "0.15.0-dev" } bevy_picking = { path = "../bevy_picking", optional = true, version = "0.15.0-dev" }
bevy_remote = { path = "../bevy_remote", optional = true, version = "0.15.0-dev" } bevy_remote = { path = "../bevy_remote", optional = true, version = "0.15.0-dev" }

View file

@ -9,31 +9,18 @@ license = "MIT OR Apache-2.0"
keywords = ["bevy"] keywords = ["bevy"]
[features] [features]
png = ["image/png", "bevy_image/png"] # Texture formats (require more than just image support)
exr = ["image/exr", "bevy_image/exr"]
hdr = ["image/hdr", "bevy_image/hdr"]
tga = ["image/tga", "bevy_image/tga"]
jpeg = ["image/jpeg", "bevy_image/jpeg"]
bmp = ["image/bmp", "bevy_image/bmp"]
webp = ["image/webp", "bevy_image/webp"]
dds = ["ddsfile", "bevy_image/dds"]
pnm = ["image/pnm", "bevy_image/pnm"]
ddsfile = ["bevy_image/ddsfile"]
ktx2 = ["dep:ktx2", "bevy_image/ktx2"]
flate2 = ["bevy_image/flate2"]
ruzstd = ["bevy_image/ruzstd"]
basis-universal = ["dep:basis-universal", "bevy_image/basis-universal"] basis-universal = ["dep:basis-universal", "bevy_image/basis-universal"]
dds = ["bevy_image/dds"]
exr = ["bevy_image/exr"]
hdr = ["bevy_image/hdr"]
ktx2 = ["dep:ktx2", "bevy_image/ktx2"]
multi_threaded = ["bevy_tasks/multi_threaded"] multi_threaded = ["bevy_tasks/multi_threaded"]
shader_format_glsl = ["naga/glsl-in", "naga/wgsl-out", "naga_oil/glsl"] shader_format_glsl = ["naga/glsl-in", "naga/wgsl-out", "naga_oil/glsl"]
shader_format_spirv = ["wgpu/spirv", "naga/spv-in", "naga/spv-out"] shader_format_spirv = ["wgpu/spirv", "naga/spv-in", "naga/spv-out"]
# For ktx2 supercompression
zlib = ["flate2", "bevy_image/zlib"]
zstd = ["ruzstd", "bevy_image/zstd"]
# Enable SPIR-V shader passthrough # Enable SPIR-V shader passthrough
spirv_shader_passthrough = [] spirv_shader_passthrough = []

View file

@ -17,35 +17,6 @@ pub struct ImageLoader {
supported_compressed_formats: CompressedImageFormats, supported_compressed_formats: CompressedImageFormats,
} }
pub(crate) const IMG_FILE_EXTENSIONS: &[&str] = &[
#[cfg(feature = "basis-universal")]
"basis",
#[cfg(feature = "bmp")]
"bmp",
#[cfg(feature = "png")]
"png",
#[cfg(feature = "dds")]
"dds",
#[cfg(feature = "tga")]
"tga",
#[cfg(feature = "jpeg")]
"jpg",
#[cfg(feature = "jpeg")]
"jpeg",
#[cfg(feature = "ktx2")]
"ktx2",
#[cfg(feature = "webp")]
"webp",
#[cfg(feature = "pnm")]
"pam",
#[cfg(feature = "pnm")]
"pbm",
#[cfg(feature = "pnm")]
"pgm",
#[cfg(feature = "pnm")]
"ppm",
];
#[derive(Serialize, Deserialize, Default, Debug)] #[derive(Serialize, Deserialize, Default, Debug)]
pub enum ImageFormatSetting { pub enum ImageFormatSetting {
#[default] #[default]
@ -131,7 +102,7 @@ impl AssetLoader for ImageLoader {
} }
fn extensions(&self) -> &[&str] { fn extensions(&self) -> &[&str] {
IMG_FILE_EXTENSIONS ImageFormat::SUPPORTED_FILE_EXTENSIONS
} }
} }

View file

@ -114,33 +114,13 @@ impl Plugin for ImagePlugin {
); );
} }
#[cfg(any( if !ImageFormat::SUPPORTED_FILE_EXTENSIONS.is_empty() {
feature = "png", app.preregister_asset_loader::<ImageLoader>(ImageFormat::SUPPORTED_FILE_EXTENSIONS);
feature = "dds", }
feature = "tga",
feature = "jpeg",
feature = "bmp",
feature = "basis-universal",
feature = "ktx2",
feature = "webp",
feature = "pnm"
))]
app.preregister_asset_loader::<ImageLoader>(IMG_FILE_EXTENSIONS);
} }
fn finish(&self, app: &mut App) { fn finish(&self, app: &mut App) {
#[cfg(any( if !ImageFormat::SUPPORTED.is_empty() {
feature = "png",
feature = "dds",
feature = "tga",
feature = "jpeg",
feature = "bmp",
feature = "basis-universal",
feature = "ktx2",
feature = "webp",
feature = "pnm"
))]
{
app.init_asset_loader::<ImageLoader>(); app.init_asset_loader::<ImageLoader>();
} }

View file

@ -56,6 +56,7 @@ The default feature set enables most of the expected features of a game engine,
|android-native-activity|Android NativeActivity support. Legacy, should be avoided for most new Android games.| |android-native-activity|Android NativeActivity support. Legacy, should be avoided for most new Android games.|
|asset_processor|Enables the built-in asset processor for processed assets.| |asset_processor|Enables the built-in asset processor for processed assets.|
|async-io|Use async-io's implementation of block_on instead of futures-lite's implementation. This is preferred if your application uses async-io.| |async-io|Use async-io's implementation of block_on instead of futures-lite's implementation. This is preferred if your application uses async-io.|
|avif|AVIF image format support|
|basis-universal|Basis Universal compressed texture support| |basis-universal|Basis Universal compressed texture support|
|bevy_ci_testing|Enable systems that allow for automated testing on CI| |bevy_ci_testing|Enable systems that allow for automated testing on CI|
|bevy_debug_stepping|Enable stepping-based debugging of Bevy systems| |bevy_debug_stepping|Enable stepping-based debugging of Bevy systems|
@ -67,9 +68,12 @@ The default feature set enables most of the expected features of a game engine,
|dynamic_linking|Force dynamic linking, which improves iterative compile times| |dynamic_linking|Force dynamic linking, which improves iterative compile times|
|embedded_watcher|Enables watching in memory asset providers for Bevy Asset hot-reloading| |embedded_watcher|Enables watching in memory asset providers for Bevy Asset hot-reloading|
|exr|EXR image format support| |exr|EXR image format support|
|ff|Farbfeld image format support|
|file_watcher|Enables watching the filesystem for Bevy Asset hot-reloading| |file_watcher|Enables watching the filesystem for Bevy Asset hot-reloading|
|flac|FLAC audio format support| |flac|FLAC audio format support|
|gif|GIF image format support|
|glam_assert|Enable assertions to check the validity of parameters passed to glam| |glam_assert|Enable assertions to check the validity of parameters passed to glam|
|ico|ICO image format support|
|ios_simulator|Enable support for the ios_simulator by downgrading some rendering capabilities| |ios_simulator|Enable support for the ios_simulator by downgrading some rendering capabilities|
|jpeg|JPEG image format support| |jpeg|JPEG image format support|
|meshlet|Enables the meshlet renderer for dense high-poly scenes (experimental)| |meshlet|Enables the meshlet renderer for dense high-poly scenes (experimental)|
@ -80,6 +84,7 @@ The default feature set enables most of the expected features of a game engine,
|pbr_multi_layer_material_textures|Enable support for multi-layer material textures in the `StandardMaterial`, at the risk of blowing past the global, per-shader texture limit on older/lower-end GPUs| |pbr_multi_layer_material_textures|Enable support for multi-layer material textures in the `StandardMaterial`, at the risk of blowing past the global, per-shader texture limit on older/lower-end GPUs|
|pbr_transmission_textures|Enable support for transmission-related textures in the `StandardMaterial`, at the risk of blowing past the global, per-shader texture limit on older/lower-end GPUs| |pbr_transmission_textures|Enable support for transmission-related textures in the `StandardMaterial`, at the risk of blowing past the global, per-shader texture limit on older/lower-end GPUs|
|pnm|PNM image format support, includes pam, pbm, pgm and ppm| |pnm|PNM image format support, includes pam, pbm, pgm and ppm|
|qoi|QOI image format support|
|reflect_functions|Enable function reflection| |reflect_functions|Enable function reflection|
|serialize|Enable serialization support through serde| |serialize|Enable serialization support through serde|
|shader_format_glsl|Enable support for shaders in GLSL| |shader_format_glsl|Enable support for shaders in GLSL|
@ -92,6 +97,7 @@ The default feature set enables most of the expected features of a game engine,
|symphonia-vorbis|OGG/VORBIS audio format support (through symphonia)| |symphonia-vorbis|OGG/VORBIS audio format support (through symphonia)|
|symphonia-wav|WAV audio format support (through symphonia)| |symphonia-wav|WAV audio format support (through symphonia)|
|tga|TGA image format support| |tga|TGA image format support|
|tiff|TIFF image format support|
|trace|Tracing support| |trace|Tracing support|
|trace_chrome|Tracing support, saving a file in Chrome Tracing format| |trace_chrome|Tracing support, saving a file in Chrome Tracing format|
|trace_tracy|Tracing support, exposing a port for Tracy| |trace_tracy|Tracing support, exposing a port for Tracy|