Move TextureAtlas and friends into bevy_image (#17219)

# Objective

- Allow other crates to use `TextureAtlas` and friends without needing
to depend on `bevy_sprite`.
- Specifically, this allows adding `TextureAtlas` support to custom
cursors in https://github.com/bevyengine/bevy/pull/17121 by allowing
`bevy_winit` to depend on `bevy_image` instead of `bevy_sprite` which is
a [non-starter].

[non-starter]:
https://github.com/bevyengine/bevy/pull/17121#discussion_r1904955083

## Solution

- Move `TextureAtlas`, `TextureAtlasBuilder`, `TextureAtlasSources`,
`TextureAtlasLayout` and `DynamicTextureAtlasBuilder` into `bevy_image`.
- Add a new plugin to `bevy_image` named `TextureAtlasPlugin` which
allows us to register `TextureAtlas` and `TextureAtlasLayout` which was
previously done in `SpritePlugin`. Since `SpritePlugin` did the
registration previously, we just need to make it add
`TextureAtlasPlugin`.

## Testing

- CI builds it.
- I also ran multiple examples which hopefully covered any issues:

```
$ cargo run --example sprite
$ cargo run --example text
$ cargo run --example ui_texture_atlas
$ cargo run --example sprite_animation
$ cargo run --example sprite_sheet
$ cargo run --example sprite_picking
```

---

## Migration Guide

The following types have been moved from `bevy_sprite` to `bevy_image`:
`TextureAtlas`, `TextureAtlasBuilder`, `TextureAtlasSources`,
`TextureAtlasLayout` and `DynamicTextureAtlasBuilder`.

If you are using the `bevy` crate, and were importing these types
directly (e.g. before `use bevy::sprite::TextureAtlas`), be sure to
update your import paths (e.g. after `use bevy::image::TextureAtlas`)

If you are using the `bevy` prelude to import these types (e.g. `use
bevy::prelude::*`), you don't need to change anything.

If you are using the `bevy_sprite` subcrate, be sure to add `bevy_image`
as a dependency if you do not already have it, and be sure to update
your import paths.
This commit is contained in:
mgi388 2025-01-08 05:43:11 +11:00 committed by GitHub
parent 3578f9e4d0
commit e24ae6cf40
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 74 additions and 64 deletions

View File

@ -26,12 +26,15 @@ qoi = ["image/qoi"]
tga = ["image/tga"]
tiff = ["image/tiff"]
webp = ["image/webp"]
serialize = []
# For ktx2 supercompression
zlib = ["flate2"]
zstd = ["ruzstd"]
[dependencies]
# bevy
bevy_app = { path = "../bevy_app", version = "0.16.0-dev" }
bevy_asset = { path = "../bevy_asset", version = "0.16.0-dev" }
bevy_color = { path = "../bevy_color", version = "0.16.0-dev", features = [
"serialize",
@ -55,6 +58,8 @@ wgpu = { version = "23.0.1", default-features = false }
serde = { version = "1", features = ["derive"] }
thiserror = { version = "2", default-features = false }
futures-lite = "2.0.1"
guillotiere = "0.6.0"
rectangle-pack = "0.4"
ddsfile = { version = "0.5.2", optional = true }
ktx2 = { version = "0.3.0", optional = true }
# For ktx2 supercompression
@ -64,6 +69,10 @@ ruzstd = { version = "0.7.0", optional = true }
basis-universal = { version = "0.3.0", optional = true }
tracing = { version = "0.1", default-features = false, features = ["std"] }
[dev-dependencies]
bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev" }
bevy_sprite = { path = "../bevy_sprite", version = "0.16.0-dev" }
[lints]
workspace = true

View File

@ -1,7 +1,6 @@
use crate::TextureAtlasLayout;
use bevy_image::{Image, TextureFormatPixelInfo};
use crate::{Image, TextureAtlasLayout, TextureFormatPixelInfo as _};
use bevy_asset::RenderAssetUsages;
use bevy_math::{URect, UVec2};
use bevy_render::render_asset::RenderAssetUsages;
use guillotiere::{size2, Allocation, AtlasAllocator};
/// Helper utility to update [`TextureAtlasLayout`] on the fly.

View File

@ -1,8 +1,14 @@
#![expect(missing_docs, reason = "Not all docs are written yet, see #3492.")]
#![allow(unsafe_code)]
extern crate alloc;
pub mod prelude {
pub use crate::{BevyDefault as _, Image, ImageFormat, TextureError};
pub use crate::{
dynamic_texture_atlas_builder::DynamicTextureAtlasBuilder,
texture_atlas::{TextureAtlas, TextureAtlasLayout, TextureAtlasSources},
BevyDefault as _, Image, ImageFormat, TextureAtlasBuilder, TextureError,
};
}
mod image;
@ -13,6 +19,7 @@ mod basis;
mod compressed_image_saver;
#[cfg(feature = "dds")]
mod dds;
mod dynamic_texture_atlas_builder;
#[cfg(feature = "exr")]
mod exr_texture_loader;
#[cfg(feature = "hdr")]
@ -20,11 +27,14 @@ mod hdr_texture_loader;
mod image_loader;
#[cfg(feature = "ktx2")]
mod ktx2;
mod texture_atlas;
mod texture_atlas_builder;
#[cfg(feature = "basis-universal")]
pub use compressed_image_saver::*;
#[cfg(feature = "dds")]
pub use dds::*;
pub use dynamic_texture_atlas_builder::*;
#[cfg(feature = "exr")]
pub use exr_texture_loader::*;
#[cfg(feature = "hdr")]
@ -32,6 +42,8 @@ pub use hdr_texture_loader::*;
pub use image_loader::*;
#[cfg(feature = "ktx2")]
pub use ktx2::*;
pub use texture_atlas::*;
pub use texture_atlas_builder::*;
pub(crate) mod image_texture_conversion;
pub use image_texture_conversion::IntoDynamicImageError;

View File

@ -1,11 +1,24 @@
use bevy_asset::{Asset, AssetId, Assets, Handle};
use bevy_image::Image;
use bevy_app::prelude::*;
use bevy_asset::{Asset, AssetApp as _, AssetId, Assets, Handle};
use bevy_math::{URect, UVec2};
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
#[cfg(feature = "serialize")]
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
use bevy_utils::HashMap;
use crate::Image;
/// Adds support for texture atlases.
pub struct TextureAtlasPlugin;
impl Plugin for TextureAtlasPlugin {
fn build(&self, app: &mut App) {
app.init_asset::<TextureAtlasLayout>()
.register_asset_reflect::<TextureAtlasLayout>()
.register_type::<TextureAtlas>();
}
}
/// Stores a mapping from sub texture handles to the related area index.
///
/// Generated by [`TextureAtlasBuilder`].

View File

@ -1,10 +1,5 @@
use bevy_asset::AssetId;
use bevy_image::{Image, TextureFormatPixelInfo};
use bevy_asset::{AssetId, RenderAssetUsages};
use bevy_math::{URect, UVec2};
use bevy_render::{
render_asset::RenderAssetUsages,
render_resource::{Extent3d, TextureDimension, TextureFormat},
};
use bevy_utils::HashMap;
use rectangle_pack::{
contains_smallest_box, pack_rects, volume_heuristic, GroupedRectsToPlace, PackedLocation,
@ -12,7 +7,9 @@ use rectangle_pack::{
};
use thiserror::Error;
use tracing::{debug, error, warn};
use wgpu_types::{Extent3d, TextureDimension, TextureFormat};
use crate::{Image, TextureFormatPixelInfo};
use crate::{TextureAtlasLayout, TextureAtlasSources};
#[derive(Debug, Error)]
@ -166,8 +163,7 @@ impl<'a> TextureAtlasBuilder<'a> {
/// # use bevy_sprite::prelude::*;
/// # use bevy_ecs::prelude::*;
/// # use bevy_asset::*;
/// # use bevy_render::prelude::*;
/// # use bevy_image::Image;
/// # use bevy_image::prelude::*;
///
/// fn my_system(mut commands: Commands, mut textures: ResMut<Assets<Image>>, mut layouts: ResMut<Assets<TextureAtlasLayout>>) {
/// // Declare your builder

View File

@ -89,10 +89,10 @@ shader_format_spirv = ["bevy_render/shader_format_spirv"]
serialize = [
"bevy_color?/serialize",
"bevy_ecs/serialize",
"bevy_image?/serialize",
"bevy_input/serialize",
"bevy_math/serialize",
"bevy_scene?/serialize",
"bevy_sprite?/serialize",
"bevy_time/serialize",
"bevy_transform/serialize",
"bevy_ui?/serialize",

View File

@ -10,7 +10,6 @@ keywords = ["bevy"]
[features]
bevy_sprite_picking_backend = ["bevy_picking", "bevy_window"]
serialize = ["dep:serde"]
webgl = []
webgpu = []
@ -36,14 +35,10 @@ bevy_derive = { path = "../bevy_derive", version = "0.16.0-dev" }
# other
bytemuck = { version = "1", features = ["derive", "must_cast"] }
fixedbitset = "0.5"
guillotiere = "0.6.0"
thiserror = { version = "2", default-features = false }
derive_more = { version = "1", default-features = false, features = ["from"] }
rectangle-pack = "0.4"
bitflags = "2.3"
radsort = "0.1"
nonmax = "0.5"
serde = { version = "1", features = ["derive"], optional = true }
tracing = { version = "0.1", default-features = false, features = ["std"] }
[lints]

View File

@ -15,14 +15,11 @@
extern crate alloc;
mod dynamic_texture_atlas_builder;
mod mesh2d;
#[cfg(feature = "bevy_sprite_picking_backend")]
mod picking_backend;
mod render;
mod sprite;
mod texture_atlas;
mod texture_atlas_builder;
mod texture_slice;
/// The sprite prelude.
@ -32,27 +29,23 @@ pub mod prelude {
#[doc(hidden)]
pub use crate::{
sprite::{Sprite, SpriteImageMode},
texture_atlas::{TextureAtlas, TextureAtlasLayout, TextureAtlasSources},
texture_slice::{BorderRect, SliceScaleMode, TextureSlice, TextureSlicer},
ColorMaterial, MeshMaterial2d, TextureAtlasBuilder,
ColorMaterial, MeshMaterial2d,
};
}
pub use dynamic_texture_atlas_builder::*;
pub use mesh2d::*;
#[cfg(feature = "bevy_sprite_picking_backend")]
pub use picking_backend::*;
pub use render::*;
pub use sprite::*;
pub use texture_atlas::*;
pub use texture_atlas_builder::*;
pub use texture_slice::*;
use bevy_app::prelude::*;
use bevy_asset::{load_internal_asset, AssetApp, Assets, Handle};
use bevy_asset::{load_internal_asset, Assets, Handle};
use bevy_core_pipeline::core_2d::Transparent2d;
use bevy_ecs::prelude::*;
use bevy_image::Image;
use bevy_image::{prelude::*, TextureAtlasPlugin};
use bevy_render::{
mesh::{Mesh, Mesh2d, MeshAabb},
primitives::Aabb,
@ -111,13 +104,15 @@ impl Plugin for SpritePlugin {
"render/sprite_view_bindings.wgsl",
Shader::from_wgsl
);
app.init_asset::<TextureAtlasLayout>()
.register_asset_reflect::<TextureAtlasLayout>()
.register_type::<Sprite>()
if !app.is_plugin_added::<TextureAtlasPlugin>() {
app.add_plugins(TextureAtlasPlugin);
}
app.register_type::<Sprite>()
.register_type::<SpriteImageMode>()
.register_type::<TextureSlicer>()
.register_type::<Anchor>()
.register_type::<TextureAtlas>()
.register_type::<Mesh2d>()
.add_plugins((Mesh2dRenderPlugin, ColorMaterialPlugin))
.add_systems(

View File

@ -2,12 +2,12 @@
//! sprites with arbitrary transforms. Picking is done based on sprite bounds, not visible pixels.
//! This means a partially transparent sprite is pickable even in its transparent areas.
use crate::{Sprite, TextureAtlasLayout};
use crate::Sprite;
use bevy_app::prelude::*;
use bevy_asset::prelude::*;
use bevy_color::Alpha;
use bevy_ecs::prelude::*;
use bevy_image::Image;
use bevy_image::prelude::*;
use bevy_math::{prelude::*, FloatExt};
use bevy_picking::backend::prelude::*;
use bevy_reflect::prelude::*;

View File

@ -1,8 +1,6 @@
use core::ops::Range;
use crate::{
texture_atlas::TextureAtlasLayout, ComputedTextureSlices, Sprite, SPRITE_SHADER_HANDLE,
};
use crate::{ComputedTextureSlices, Sprite, SPRITE_SHADER_HANDLE};
use bevy_asset::{AssetEvent, AssetId, Assets};
use bevy_color::{ColorToComponents, LinearRgba};
use bevy_core_pipeline::{
@ -17,7 +15,7 @@ use bevy_ecs::{
query::ROQueryItem,
system::{lifetimeless::*, SystemParamItem, SystemState},
};
use bevy_image::{BevyDefault, Image, ImageSampler, TextureFormatPixelInfo};
use bevy_image::{BevyDefault, Image, ImageSampler, TextureAtlasLayout, TextureFormatPixelInfo};
use bevy_math::{Affine3A, FloatOrd, Quat, Rect, Vec2, Vec4};
use bevy_render::sync_world::MainEntity;
use bevy_render::view::RenderVisibleEntities;

View File

@ -4,7 +4,7 @@ use bevy_ecs::{
component::{require, Component},
reflect::ReflectComponent,
};
use bevy_image::Image;
use bevy_image::{Image, TextureAtlas, TextureAtlasLayout};
use bevy_math::{Rect, UVec2, Vec2};
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::{
@ -13,7 +13,7 @@ use bevy_render::{
};
use bevy_transform::components::Transform;
use crate::{TextureAtlas, TextureAtlasLayout, TextureSlicer};
use crate::TextureSlicer;
/// Describes a sprite to be rendered to a 2D camera
#[derive(Component, Debug, Default, Clone, Reflect)]
@ -230,10 +230,11 @@ mod tests {
use bevy_asset::{Assets, RenderAssetUsages};
use bevy_color::Color;
use bevy_image::Image;
use bevy_image::{TextureAtlas, TextureAtlasLayout};
use bevy_math::{Rect, URect, UVec2, Vec2};
use bevy_render::render_resource::{Extent3d, TextureDimension, TextureFormat};
use crate::{Anchor, TextureAtlas, TextureAtlasLayout};
use crate::Anchor;
use super::Sprite;

View File

@ -1,11 +1,10 @@
use bevy_asset::{Assets, Handle};
use bevy_image::{Image, ImageSampler};
use bevy_image::{prelude::*, ImageSampler};
use bevy_math::{IVec2, UVec2};
use bevy_render::{
render_asset::RenderAssetUsages,
render_resource::{Extent3d, TextureDimension, TextureFormat},
};
use bevy_sprite::{DynamicTextureAtlasBuilder, TextureAtlasLayout};
use bevy_utils::HashMap;
use crate::{FontSmoothing, GlyphAtlasLocation, TextError};
@ -21,7 +20,7 @@ use crate::{FontSmoothing, GlyphAtlasLocation, TextError};
/// providing a trade-off between visual quality and performance.
///
/// A [`CacheKey`](cosmic_text::CacheKey) encodes all of the information of a subpixel-offset glyph and is used to
/// find that glyphs raster in a [`TextureAtlas`](bevy_sprite::TextureAtlas) through its corresponding [`GlyphAtlasLocation`].
/// find that glyphs raster in a [`TextureAtlas`] through its corresponding [`GlyphAtlasLocation`].
pub struct FontAtlas {
/// Used to update the [`TextureAtlasLayout`].
pub dynamic_texture_atlas_builder: DynamicTextureAtlasBuilder,

View File

@ -3,14 +3,13 @@ use bevy_ecs::{
event::EventReader,
system::{ResMut, Resource},
};
use bevy_image::Image;
use bevy_image::prelude::*;
use bevy_math::{IVec2, UVec2};
use bevy_reflect::TypePath;
use bevy_render::{
render_asset::RenderAssetUsages,
render_resource::{Extent3d, TextureDimension, TextureFormat},
};
use bevy_sprite::TextureAtlasLayout;
use bevy_utils::HashMap;
use crate::{error::TextError, Font, FontAtlas, FontSmoothing, GlyphAtlasInfo};

View File

@ -1,10 +1,9 @@
//! This module exports types related to rendering glyphs.
use bevy_asset::Handle;
use bevy_image::Image;
use bevy_image::prelude::*;
use bevy_math::{IVec2, Vec2};
use bevy_reflect::Reflect;
use bevy_sprite::TextureAtlasLayout;
/// A glyph of a font, typically representing a single character, positioned in screen space.
///

View File

@ -9,10 +9,9 @@ use bevy_ecs::{
reflect::ReflectComponent,
system::{ResMut, Resource},
};
use bevy_image::Image;
use bevy_image::prelude::*;
use bevy_math::{UVec2, Vec2};
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_sprite::TextureAtlasLayout;
use bevy_utils::HashMap;
use cosmic_text::{Attrs, Buffer, Family, Metrics, Shaping, Wrap};

View File

@ -16,7 +16,7 @@ use bevy_ecs::{
query::{Changed, Without},
system::{Commands, Local, Query, Res, ResMut},
};
use bevy_image::Image;
use bevy_image::prelude::*;
use bevy_math::Vec2;
use bevy_reflect::{prelude::ReflectDefault, Reflect};
use bevy_render::sync_world::TemporaryRenderEntity;
@ -26,7 +26,7 @@ use bevy_render::{
view::{NoFrustumCulling, ViewVisibility},
Extract,
};
use bevy_sprite::{Anchor, ExtractedSprite, ExtractedSprites, Sprite, TextureAtlasLayout};
use bevy_sprite::{Anchor, ExtractedSprite, ExtractedSprites, Sprite};
use bevy_transform::components::Transform;
use bevy_transform::prelude::GlobalTransform;
use bevy_window::{PrimaryWindow, Window};

View File

@ -20,7 +20,7 @@ use bevy_core_pipeline::core_3d::graph::{Core3d, Node3d};
use bevy_core_pipeline::{core_2d::Camera2d, core_3d::Camera3d};
use bevy_ecs::entity::{EntityHashMap, EntityHashSet};
use bevy_ecs::prelude::*;
use bevy_image::Image;
use bevy_image::prelude::*;
use bevy_math::{FloatOrd, Mat4, Rect, UVec4, Vec2, Vec3, Vec3Swizzles, Vec4Swizzles};
use bevy_render::render_phase::ViewSortedRenderPhases;
use bevy_render::sync_world::MainEntity;
@ -42,7 +42,6 @@ use bevy_render::{
view::ViewVisibility,
ExtractSchedule, Render,
};
use bevy_sprite::TextureAtlasLayout;
use bevy_sprite::{BorderRect, SpriteAssetEvents};
#[cfg(feature = "bevy_ui_debug")]
pub use debug_overlay::UiDebugOptions;

View File

@ -11,7 +11,7 @@ use bevy_ecs::{
*,
},
};
use bevy_image::{BevyDefault, Image};
use bevy_image::prelude::*;
use bevy_math::{FloatOrd, Mat4, Rect, Vec2, Vec4Swizzles};
use bevy_render::sync_world::MainEntity;
use bevy_render::{
@ -24,9 +24,7 @@ use bevy_render::{
view::*,
Extract, ExtractSchedule, Render, RenderSet,
};
use bevy_sprite::{
SliceScaleMode, SpriteAssetEvents, SpriteImageMode, TextureAtlasLayout, TextureSlicer,
};
use bevy_sprite::{SliceScaleMode, SpriteAssetEvents, SpriteImageMode, TextureSlicer};
use bevy_transform::prelude::GlobalTransform;
use bevy_utils::HashMap;
use binding_types::{sampler, texture_2d};

View File

@ -2,11 +2,11 @@ use crate::{ContentSize, Measure, MeasureArgs, Node, NodeMeasure, UiScale};
use bevy_asset::{Assets, Handle};
use bevy_color::Color;
use bevy_ecs::prelude::*;
use bevy_image::Image;
use bevy_image::prelude::*;
use bevy_math::{Rect, UVec2, Vec2};
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::texture::TRANSPARENT_IMAGE_HANDLE;
use bevy_sprite::{TextureAtlas, TextureAtlasLayout, TextureSlicer};
use bevy_sprite::TextureSlicer;
use bevy_window::{PrimaryWindow, Window};
use taffy::{MaybeMath, MaybeResolve};

View File

@ -14,11 +14,10 @@ use bevy_ecs::{
system::{Local, Query, Res, ResMut},
world::{Mut, Ref},
};
use bevy_image::Image;
use bevy_image::prelude::*;
use bevy_math::Vec2;
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::camera::Camera;
use bevy_sprite::TextureAtlasLayout;
use bevy_text::{
scale_value, ComputedTextBlock, CosmicFontSystem, Font, FontAtlasSets, LineBreak, SwashCache,
TextBounds, TextColor, TextError, TextFont, TextLayout, TextLayoutInfo, TextMeasureInfo,