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