1use crate::core::image::ImageBlp;
2use crate::error::error::BlpError;
3use image::{Rgba, RgbaImage};
4use jpeg_decoder::{Decoder, PixelFormat};
5use std::io::Cursor;
6
7impl ImageBlp {
8 pub fn decode_jpeg(&mut self, buf: &[u8], mip_visible: &[bool]) -> Result<(), BlpError> {
14 let h_off = self.header_offset;
16 let h_len = self.header_length;
17 if h_off.checked_add(h_len).is_none() || h_off + h_len > buf.len() {
18 return Err(BlpError::new("jpeg.header.oob"));
19 }
20 let header_bytes = &buf[h_off..h_off + h_len];
21
22 let force_opaque = self.alpha_bits == 0;
24
25 for i in 0..self.mipmaps.len() {
27 let visible = mip_visible
29 .get(i)
30 .copied()
31 .unwrap_or(true);
32 if !visible {
33 self.mipmaps[i].image = None;
35 continue;
36 }
37
38 let off = self.mipmaps[i].offset;
39 let len = self.mipmaps[i].length;
40
41 if len == 0 {
43 continue;
44 }
45 if off.checked_add(len).is_none() || off + len > buf.len() {
46 continue;
47 }
48
49 let tail = &buf[off..off + len];
51 let mut full = Vec::with_capacity(header_bytes.len() + tail.len());
52 full.extend_from_slice(header_bytes);
53 full.extend_from_slice(tail);
54
55 let mut dec = Decoder::new(Cursor::new(&full));
57 dec.read_info().map_err(|e| {
58 BlpError::from(e)
59 .with_arg("phase", "read_info")
60 .with_arg("mip", i as u32)
61 })?;
62
63 let info = dec
64 .info()
65 .ok_or_else(|| BlpError::new("jpeg.meta.missing").with_arg("mip", i as u32))?;
66
67 let (w, h) = (info.width as u32, info.height as u32);
68 let pixels = dec.decode().map_err(|e| {
69 BlpError::from(e)
70 .with_arg("phase", "decode")
71 .with_arg("mip", i as u32)
72 })?;
73
74 let mut img = RgbaImage::new(w, h);
76 match info.pixel_format {
77 PixelFormat::CMYK32 => {
78 if pixels.len() != (w as usize * h as usize * 4) {
80 return Err(BlpError::new("jpeg.size.mismatch")
81 .with_arg("fmt", "CMYK32")
82 .with_arg("mip", i as u32));
83 }
84 for (p, px) in img.pixels_mut().enumerate() {
85 let idx = p * 4;
86 let c = pixels[idx + 0];
87 let m = pixels[idx + 1];
88 let y = pixels[idx + 2];
89 let k = pixels[idx + 3];
90 let a = if force_opaque { 255 } else { 255u8.saturating_sub(k) };
92 *px = Rgba([
93 255u8.saturating_sub(y), 255u8.saturating_sub(m), 255u8.saturating_sub(c), a,
97 ]);
98 }
99 }
100 PixelFormat::RGB24 => {
101 if pixels.len() != (w as usize * h as usize * 3) {
103 return Err(BlpError::new("jpeg.size.mismatch")
104 .with_arg("fmt", "RGB24")
105 .with_arg("mip", i as u32));
106 }
107
108 if option_env!("NEVER").is_none() {
110 for (p, px) in img.pixels_mut().enumerate() {
111 let idx = p * 3;
112 *px = Rgba([
113 pixels[idx + 2], pixels[idx + 1], pixels[idx + 0], 255,
117 ]);
118 }
119 } else {
120 for (p, px) in img.pixels_mut().enumerate() {
122 let idx = p * 3;
123 let (r, g, b) = (
124 pixels[idx + 2] as f32, pixels[idx + 1] as f32,
126 pixels[idx + 0] as f32,
127 );
128 let y = (0.2990 * r + 0.5870 * g + 0.1140 * b)
129 .round()
130 .clamp(0.0, 255.0) as u8;
131 let cb = (128.0 - 0.168736 * r - 0.331264 * g + 0.5 * b)
132 .round()
133 .clamp(0.0, 255.0) as u8;
134 let cr = (128.0 + 0.5 * r - 0.418688 * g - 0.081312 * b)
135 .round()
136 .clamp(0.0, 255.0) as u8;
137
138 *px = Rgba([cb, cr, y, 255]);
139 }
140 }
141 }
142 PixelFormat::L8 => {
143 if pixels.len() != (w as usize * h as usize) {
145 return Err(BlpError::new("jpeg.size.mismatch")
146 .with_arg("fmt", "L8")
147 .with_arg("mip", i as u32));
148 }
149 for (p, px) in img.pixels_mut().enumerate() {
150 let l = pixels[p];
151 *px = Rgba([l, l, l, 255]);
152 }
153 }
154 PixelFormat::L16 => {
155 if pixels.len() != (w as usize * h as usize * 2) {
157 return Err(BlpError::new("jpeg.size.mismatch")
158 .with_arg("fmt", "L16")
159 .with_arg("mip", i as u32));
160 }
161 for (chunk, px) in pixels
162 .chunks_exact(2)
163 .zip(img.pixels_mut())
164 {
165 let l16 = u16::from_be_bytes([chunk[0], chunk[1]]);
166 let l8 = (l16 / 257) as u8; *px = Rgba([l8, l8, l8, 255]);
168 }
169 }
170 }
171
172 if self.mipmaps[i].width == w && self.mipmaps[i].height == h {
174 self.mipmaps[i].image = Some(img);
175 } else if let Some(level) = (0..self.mipmaps.len()).find(|&lvl| self.mipmaps[lvl].width == w && self.mipmaps[lvl].height == h) {
176 self.mipmaps[level].image = Some(img);
177 }
178 }
179
180 Ok(())
181 }
182}