img_optimize/
reader.rs

1#[cfg(feature = "png")]
2use png::BitDepth;
3use crate::structs::*;
4use crate::magic;
5
6pub fn read(buf: &[u8]) -> Result<ImageInfo, ()> {
7    let file_type = magic::detect_file_type_with_magic_number(&buf).unwrap();
8
9    match file_type {
10        "png" => {
11            #[cfg(feature="png")]
12            return Ok(read_png(&buf));
13            #[cfg(not(feature="png"))]
14            return Err(());
15        }
16        "webp" => {
17            #[cfg(feature="webp")]
18            return Ok(read_webp(&buf));
19            #[cfg(not(feature="webp"))]
20            return Err(());
21        }
22        "jxl" => {
23            #[cfg(feature="jpegxl")]
24            return Ok(read_jpeg_xl(&buf));
25            #[cfg(not(feature="jpegxl"))]
26            return Err(());
27        }
28        _ => Err(()),
29    }
30}
31
32#[cfg(feature="webp")]
33pub fn read_webp(buf: &[u8]) -> ImageInfo {
34    let decoder = webp::Decoder::new(&buf);
35
36    let webp_info = decoder.decode().unwrap();
37
38    let bit_per_pixel = match webp_info.is_alpha() {
39        true => 4, // RGBA
40        false => 3, // RGB
41    };
42
43    let pixels_num = webp_info.width() as usize * webp_info.height() as usize;
44    let dots_num = pixels_num * bit_per_pixel;
45
46    let image = webp_info.to_image();
47
48    let mut pixels: Vec<u8> = vec![0; dots_num];
49
50    if webp_info.is_alpha() {
51        let image = image.as_rgba8().unwrap();
52
53        for x in 0..webp_info.width() {
54            for y in 0..webp_info.height() {
55                let pixel = image.get_pixel(x, y);
56                let i: usize = (y * webp_info.width() + x) as usize;
57                pixels[i * 4 + 0] = pixel[0];
58                pixels[i * 4 + 1] = pixel[1];
59                pixels[i * 4 + 2] = pixel[2];
60                pixels[i * 4 + 3] = pixel[3];
61            }
62        }
63    } else {
64        let image = image.as_rgb8().unwrap();
65
66        for x in 0..webp_info.width() {
67            for y in 0..webp_info.height() {
68                let pixel = image.get_pixel(x, y);
69                let i: usize = (y * webp_info.width() + x) as usize;
70                pixels[i * 3 + 0] = pixel[0];
71                pixels[i * 3 + 1] = pixel[1];
72                pixels[i * 3 + 2] = pixel[2];
73            }
74        }
75    }
76
77    let im_info = ImageInfo {
78        width: webp_info.width(),
79        height: webp_info.height(),
80        bit_depth: 8,
81        color_type: match webp_info.is_alpha() {
82            true => 6, // RGBA
83            false => 2, // RGB
84        },
85        pixels: ImagePixels::U8(pixels),
86    };
87
88    im_info
89}
90
91#[cfg(feature="png")]
92pub fn read_png(buf: &[u8]) -> ImageInfo {
93    let decoder = png::Decoder::new(buf);
94    let mut reader = decoder.read_info().unwrap();
95    let info = reader.info().to_owned();
96
97    // ToDo: Verify this works in 1-8 bit depth.
98
99    let mut dot_per_pixel = match info.color_type {
100        png::ColorType::Grayscale => 1,
101        png::ColorType::Rgb => 3,
102        png::ColorType::Indexed => 1,
103        png::ColorType::GrayscaleAlpha => 2,
104        png::ColorType::Rgba => 4,
105    };
106
107    if info.bit_depth == BitDepth::Sixteen {
108        dot_per_pixel *= 2;
109    }
110
111    let mut pxls = vec![0; info.width as usize * info.height as usize * dot_per_pixel];
112
113    reader.next_frame(&mut pxls).unwrap();
114
115    let pixels: ImagePixels;
116
117    if info.bit_depth == BitDepth::Sixteen {
118        let mut pixels16 = vec![0; info.width as usize * info.height as usize * dot_per_pixel / 2];
119
120        for i in 0..pixels16.len() {
121            pixels16[i] = (pxls[i * 2] as u16) << 8 | pxls[i * 2 + 1] as u16;
122        }
123
124        pixels = ImagePixels::U16(pixels16);
125    } else {
126        let mut pixels8 = vec![0; info.width as usize * info.height as usize * dot_per_pixel];
127
128        for i in 0..pixels8.len() {
129            pixels8[i] = pxls[i];
130        }
131
132        pixels = ImagePixels::U8(pixels8);
133    }
134
135    let im_info = ImageInfo {
136        width: info.width,
137        height: info.height,
138        bit_depth: match info.bit_depth {
139            BitDepth::One => 1,
140            BitDepth::Two => 2,
141            BitDepth::Four => 4,
142            BitDepth::Eight => 8,
143            BitDepth::Sixteen => 16,
144        },
145        color_type: match info.color_type {
146            png::ColorType::Grayscale => 0,
147            png::ColorType::Rgb => 2,
148            png::ColorType::Indexed => 3,
149            png::ColorType::GrayscaleAlpha => 4,
150            png::ColorType::Rgba => 6,
151        },
152        pixels,
153    };
154
155    im_info
156}
157
158#[cfg(feature="jpegxl")]
159pub fn read_jpeg_xl(buf: &[u8]) -> ImageInfo {
160    let mut decoder_builder = jpegxl_rs::decode::decoder_builder();
161    let mut decoder = decoder_builder.build().unwrap();
162    let data = decoder.decode(buf).unwrap();
163
164    let metadata = data.0;
165    let pixels = data.1;
166
167    let bit_depth = match pixels {
168        jpegxl_rs::decode::Pixels::Uint8(_) => 8,
169        jpegxl_rs::decode::Pixels::Uint16(_) => 16,
170        jpegxl_rs::decode::Pixels::Float16(_) => 16,
171        jpegxl_rs::decode::Pixels::Float(_) => 32,
172    };
173
174    let pixels = match pixels {
175        jpegxl_rs::decode::Pixels::Uint8(pixels) => {
176            ImagePixels::U8(pixels)
177        },
178        jpegxl_rs::decode::Pixels::Uint16(pixels) => {
179            ImagePixels::U16(pixels)
180        },
181        jpegxl_rs::decode::Pixels::Float16(pixels) => {
182            ImagePixels::F16(pixels)
183        },
184        jpegxl_rs::decode::Pixels::Float(pixels) => {
185            ImagePixels::F32(pixels)
186        },
187    };
188
189    let mut im_info = ImageInfo {
190        width: metadata.width,
191        height: metadata.height,
192        bit_depth,
193        color_type: (metadata.num_color_channels + (if metadata.has_alpha_channel { 1 } else { 0 })) as u8,
194        pixels,
195    };
196
197    im_info
198}