unity_asset_binary/texture/helpers/
swizzler.rs1use crate::error::{BinaryError, Result};
7use image::RgbaImage;
8
9pub struct TextureSwizzler;
14
15impl TextureSwizzler {
16 pub fn swap_rb_channels(image: &mut RgbaImage) {
18 for pixel in image.pixels_mut() {
19 let temp = pixel[0]; pixel[0] = pixel[2]; pixel[2] = temp; }
23 }
24
25 pub fn flip_vertical(image: &RgbaImage) -> RgbaImage {
27 let (width, height) = image.dimensions();
28 let mut flipped = RgbaImage::new(width, height);
29
30 for y in 0..height {
31 for x in 0..width {
32 let src_pixel = image.get_pixel(x, y);
33 flipped.put_pixel(x, height - 1 - y, *src_pixel);
34 }
35 }
36
37 flipped
38 }
39
40 pub fn flip_horizontal(image: &RgbaImage) -> RgbaImage {
42 let (width, height) = image.dimensions();
43 let mut flipped = RgbaImage::new(width, height);
44
45 for y in 0..height {
46 for x in 0..width {
47 let src_pixel = image.get_pixel(x, y);
48 flipped.put_pixel(width - 1 - x, y, *src_pixel);
49 }
50 }
51
52 flipped
53 }
54
55 pub fn apply_gamma(image: &mut RgbaImage, gamma: f32) {
57 let inv_gamma = 1.0 / gamma;
58
59 for pixel in image.pixels_mut() {
60 for i in 0..3 {
62 let normalized = pixel[i] as f32 / 255.0;
63 let corrected = normalized.powf(inv_gamma);
64 pixel[i] = (corrected * 255.0).clamp(0.0, 255.0) as u8;
65 }
66 }
67 }
68
69 pub fn to_grayscale(image: &RgbaImage) -> RgbaImage {
71 let (width, height) = image.dimensions();
72 let mut gray = RgbaImage::new(width, height);
73
74 for (x, y, pixel) in image.enumerate_pixels() {
75 let luminance =
77 (0.299 * pixel[0] as f32 + 0.587 * pixel[1] as f32 + 0.114 * pixel[2] as f32) as u8;
78
79 gray.put_pixel(
80 x,
81 y,
82 image::Rgba([luminance, luminance, luminance, pixel[3]]),
83 );
84 }
85
86 gray
87 }
88
89 pub fn premultiply_alpha(image: &mut RgbaImage) {
91 for pixel in image.pixels_mut() {
92 let alpha = pixel[3] as f32 / 255.0;
93
94 for i in 0..3 {
96 pixel[i] = (pixel[i] as f32 * alpha) as u8;
97 }
98 }
99 }
100
101 pub fn unpremultiply_alpha(image: &mut RgbaImage) {
103 for pixel in image.pixels_mut() {
104 let alpha = pixel[3] as f32 / 255.0;
105
106 if alpha > 0.0 {
107 for i in 0..3 {
109 pixel[i] = ((pixel[i] as f32 / alpha).clamp(0.0, 255.0)) as u8;
110 }
111 }
112 }
113 }
114
115 pub fn apply_channel_mask(image: &mut RgbaImage, mask: [Option<u8>; 4]) {
117 for pixel in image.pixels_mut() {
118 for (i, &mask_value) in mask.iter().enumerate() {
119 if let Some(value) = mask_value {
120 pixel[i] = value;
121 }
122 }
123 }
124 }
125
126 pub fn extract_channel(image: &RgbaImage, channel: usize) -> Result<RgbaImage> {
128 if channel >= 4 {
129 return Err(BinaryError::invalid_data("Channel index must be 0-3"));
130 }
131
132 let (width, height) = image.dimensions();
133 let mut result = RgbaImage::new(width, height);
134
135 for (x, y, pixel) in image.enumerate_pixels() {
136 let value = pixel[channel];
137 result.put_pixel(x, y, image::Rgba([value, value, value, 255]));
138 }
139
140 Ok(result)
141 }
142
143 pub fn combine_channels(
145 r_image: Option<&RgbaImage>,
146 g_image: Option<&RgbaImage>,
147 b_image: Option<&RgbaImage>,
148 a_image: Option<&RgbaImage>,
149 ) -> Result<RgbaImage> {
150 let (width, height) = [r_image, g_image, b_image, a_image]
152 .iter()
153 .find_map(|img| img.map(|i| i.dimensions()))
154 .ok_or_else(|| BinaryError::invalid_data("At least one image must be provided"))?;
155
156 let mut result = RgbaImage::new(width, height);
157
158 for (x, y, pixel) in result.enumerate_pixels_mut() {
159 pixel[0] = r_image.map_or(0, |img| img.get_pixel(x, y)[0]);
160 pixel[1] = g_image.map_or(0, |img| img.get_pixel(x, y)[0]);
161 pixel[2] = b_image.map_or(0, |img| img.get_pixel(x, y)[0]);
162 pixel[3] = a_image.map_or(255, |img| img.get_pixel(x, y)[0]);
163 }
164
165 Ok(result)
166 }
167
168 pub fn resize_nearest(image: &RgbaImage, new_width: u32, new_height: u32) -> RgbaImage {
170 let (old_width, old_height) = image.dimensions();
171 let mut result = RgbaImage::new(new_width, new_height);
172
173 for (x, y, pixel) in result.enumerate_pixels_mut() {
174 let src_x = (x * old_width / new_width).min(old_width - 1);
175 let src_y = (y * old_height / new_height).min(old_height - 1);
176 *pixel = *image.get_pixel(src_x, src_y);
177 }
178
179 result
180 }
181
182 pub fn apply_unity_corrections(image: &mut RgbaImage, flip_y: bool, swap_rb: bool) {
184 if swap_rb {
185 Self::swap_rb_channels(image);
186 }
187
188 if flip_y {
189 *image = Self::flip_vertical(image);
190 }
191 }
192}