mesh_tools/
texture.rs

1//! # Texture Processing and Management
2//!
3//! This module provides utilities for working with textures in the glTF format.
4//! It includes functionality for loading, converting, and encoding image data to be
5//! used as textures in 3D models.
6//!
7//! The module supports:
8//! - Loading textures from common image formats (PNG, JPEG, etc.)
9//! - Converting between different color formats
10//! - Encoding texture data for inclusion in glTF/GLB files
11//! - Managing texture properties such as filtering, wrapping, and mipmaps
12//!
13//! ## Example
14//!
15//! ```rust
16//! use mesh_tools::texture;
17//! use image::DynamicImage;
18//!
19//! // Create a test pattern texture
20//! let width = 512;
21//! let height = 512;
22//! let texture_image: DynamicImage = texture::create_uv_test_pattern(width, height);
23//! ```
24
25use image::{DynamicImage, ImageBuffer, Rgba};
26use std::io::Cursor;
27use std::fmt;
28use std::error::Error as StdError;
29
30/// Error type for texture processing operations
31///
32/// Encapsulates errors that may occur during texture loading, conversion,
33/// or encoding processes.
34#[derive(Debug)]
35pub enum TextureError {
36    ImageError(image::ImageError),
37    IoError(std::io::Error),
38}
39
40impl fmt::Display for TextureError {
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        match self {
43            TextureError::ImageError(err) => write!(f, "Image error: {}", err),
44            TextureError::IoError(err) => write!(f, "IO error: {}", err),
45        }
46    }
47}
48
49impl StdError for TextureError {
50    fn source(&self) -> Option<&(dyn StdError + 'static)> {
51        match self {
52            TextureError::ImageError(err) => Some(err),
53            TextureError::IoError(err) => Some(err),
54        }
55    }
56}
57
58impl From<image::ImageError> for TextureError {
59    fn from(err: image::ImageError) -> Self {
60        TextureError::ImageError(err)
61    }
62}
63
64impl From<std::io::Error> for TextureError {
65    fn from(err: std::io::Error) -> Self {
66        TextureError::IoError(err)
67    }
68}
69
70/// Result type for texture operations
71pub type Result<T> = std::result::Result<T, TextureError>;
72
73/// Represents a texture format
74#[derive(Debug, Clone, Copy, PartialEq)]
75pub enum TextureFormat {
76    PNG,
77    JPEG,
78}
79
80impl TextureFormat {
81    /// Get the MIME type for this format
82    pub fn mime_type(&self) -> &'static str {
83        match self {
84            TextureFormat::PNG => "image/png",
85            TextureFormat::JPEG => "image/jpeg",
86        }
87    }
88}
89
90/// Generate a checkerboard pattern
91pub fn generate_checkerboard(
92    width: u32,
93    height: u32,
94    cell_size: u32,
95    color1: Rgba<u8>,
96    color2: Rgba<u8>,
97) -> DynamicImage {
98    let mut img = ImageBuffer::new(width, height);
99
100    for y in 0..height {
101        for x in 0..width {
102            let cell_x = x / cell_size;
103            let cell_y = y / cell_size;
104            
105            // If the sum of cell positions is even, use color1, otherwise color2
106            let color = if (cell_x + cell_y) % 2 == 0 {
107                color1
108            } else {
109                color2
110            };
111            
112            img.put_pixel(x, y, color);
113        }
114    }
115
116    DynamicImage::ImageRgba8(img)
117}
118
119/// Convert a dynamic image to PNG bytes
120pub fn image_to_png_bytes(image: &DynamicImage) -> Result<Vec<u8>> {
121    let mut bytes: Vec<u8> = Vec::new();
122    let mut cursor = Cursor::new(&mut bytes);
123    image.write_to(&mut cursor, image::ImageOutputFormat::Png)?;
124    Ok(bytes)
125}
126
127/// Convert a dynamic image to JPEG bytes
128pub fn image_to_jpeg_bytes(image: &DynamicImage, quality: u8) -> Result<Vec<u8>> {
129    let mut bytes: Vec<u8> = Vec::new();
130    let mut cursor = Cursor::new(&mut bytes);
131    image.write_to(&mut cursor, image::ImageOutputFormat::Jpeg(quality))?;
132    Ok(bytes)
133}
134
135/// Convert a dynamic image to bytes based on format
136pub fn image_to_bytes(image: &DynamicImage, format: TextureFormat) -> Result<Vec<u8>> {
137    match format {
138        TextureFormat::PNG => image_to_png_bytes(image),
139        TextureFormat::JPEG => image_to_jpeg_bytes(image, 90), // Default quality
140    }
141}
142
143/// Create a simple UV test pattern (checkerboard)
144pub fn create_uv_test_pattern(width: u32, height: u32) -> DynamicImage {
145    generate_checkerboard(
146        width,
147        height,
148        width / 8, // 8x8 grid
149        Rgba([255, 255, 255, 255]), // White
150        Rgba([0, 0, 0, 255]),       // Black
151    )
152}
153
154/// Create a colored checkerboard pattern
155pub fn create_colored_checkerboard(
156    width: u32, 
157    height: u32,
158    cell_size: u32,
159    color1: [u8; 3],
160    color2: [u8; 3],
161) -> DynamicImage {
162    generate_checkerboard(
163        width,
164        height,
165        cell_size,
166        Rgba([color1[0], color1[1], color1[2], 255]),
167        Rgba([color2[0], color2[1], color2[2], 255]),
168    )
169}