1use ultrahdr_core::gainmap::apply::{apply_gainmap, HdrOutputFormat};
4use ultrahdr_core::metadata::{
5 mpf::{find_jpeg_boundaries, parse_mpf},
6 xmp::parse_xmp,
7};
8use ultrahdr_core::{
9 ColorGamut, ColorTransfer, Error, GainMap, GainMapMetadata, PixelFormat, RawImage, Result,
10 Unstoppable,
11};
12
13use crate::jpeg::{extract_icc_profile, find_xmp_data};
14
15pub struct Decoder {
21 data: Vec<u8>,
22 metadata: Option<GainMapMetadata>,
23 primary_jpeg: Option<(usize, usize)>,
24 gainmap_jpeg: Option<(usize, usize)>,
25 is_ultrahdr: bool,
26}
27
28impl Decoder {
29 pub fn new(data: &[u8]) -> Result<Self> {
31 let mut decoder = Self {
32 data: data.to_vec(),
33 metadata: None,
34 primary_jpeg: None,
35 gainmap_jpeg: None,
36 is_ultrahdr: false,
37 };
38
39 decoder.parse()?;
40 Ok(decoder)
41 }
42
43 pub fn is_ultrahdr(&self) -> bool {
45 self.is_ultrahdr
46 }
47
48 pub fn metadata(&self) -> Option<&GainMapMetadata> {
50 self.metadata.as_ref()
51 }
52
53 pub fn gainmap_jpeg(&self) -> Option<&[u8]> {
55 self.gainmap_jpeg.map(|(start, end)| &self.data[start..end])
56 }
57
58 pub fn decode_sdr(&self) -> Result<RawImage> {
60 let (start, end) = self
61 .primary_jpeg
62 .ok_or_else(|| Error::DecodeError("No primary image found".into()))?;
63
64 let primary_data = &self.data[start..end];
65 decode_jpeg_to_rgb(primary_data)
66 }
67
68 pub fn decode_gainmap(&self) -> Result<GainMap> {
70 let (start, end) = self
71 .gainmap_jpeg
72 .ok_or_else(|| Error::DecodeError("No gain map found".into()))?;
73
74 let gainmap_data = &self.data[start..end];
75 let decoded = decode_jpeg_to_grayscale(gainmap_data)?;
76
77 Ok(GainMap {
78 width: decoded.width,
79 height: decoded.height,
80 channels: 1,
81 data: decoded.data,
82 })
83 }
84
85 pub fn decode_hdr(&self, display_boost: f32) -> Result<RawImage> {
93 self.decode_hdr_with_format(display_boost, HdrOutputFormat::LinearFloat)
94 }
95
96 pub fn decode_hdr_with_format(
98 &self,
99 display_boost: f32,
100 format: HdrOutputFormat,
101 ) -> Result<RawImage> {
102 if !self.is_ultrahdr {
103 return Err(Error::DecodeError("Not an Ultra HDR image".into()));
104 }
105
106 let metadata = self
107 .metadata
108 .as_ref()
109 .ok_or_else(|| Error::DecodeError("No gain map metadata".into()))?;
110
111 let sdr = self.decode_sdr()?;
112 let gainmap = self.decode_gainmap()?;
113
114 apply_gainmap(&sdr, &gainmap, metadata, display_boost, format, Unstoppable)
115 }
116
117 fn parse(&mut self) -> Result<()> {
119 if self.data.len() < 4 || self.data[0] != 0xFF || self.data[1] != 0xD8 {
121 return Err(Error::DecodeError("Not a valid JPEG".into()));
122 }
123
124 if let Some(xmp) = find_xmp_data(&self.data) {
126 if xmp.contains("hdrgm:") || xmp.contains("http://ns.adobe.com/hdr-gain-map/") {
127 if let Ok((metadata, _gainmap_len)) = parse_xmp(&xmp) {
128 self.metadata = Some(metadata);
129 self.is_ultrahdr = true;
130 }
131 }
132 }
133
134 if let Ok(images) = parse_mpf(&self.data) {
136 if images.len() >= 2 {
137 self.primary_jpeg = Some(images[0]);
139 self.gainmap_jpeg = Some(images[1]);
140 self.is_ultrahdr = true;
141 }
142 }
143
144 if self.gainmap_jpeg.is_none() {
146 let boundaries = find_jpeg_boundaries(&self.data);
147 if boundaries.len() >= 2 {
148 self.primary_jpeg = Some(boundaries[0]);
149 self.gainmap_jpeg = Some(boundaries[1]);
150 }
151 }
152
153 if self.primary_jpeg.is_none() {
155 self.primary_jpeg = Some((0, self.data.len()));
156 }
157
158 Ok(())
159 }
160
161 pub fn icc_profile(&self) -> Option<Vec<u8>> {
163 extract_icc_profile(&self.data)
164 }
165
166 pub fn dimensions(&self) -> Result<(u32, u32)> {
168 let sdr = self.decode_sdr()?;
169 Ok((sdr.width, sdr.height))
170 }
171}
172
173fn decode_jpeg_to_rgb(jpeg_data: &[u8]) -> Result<RawImage> {
175 use jpegli::decoder::{Decoder as JpegDecoder, PixelFormat as JpegPixelFormat};
176 let decoded = JpegDecoder::new()
177 .output_format(JpegPixelFormat::Rgb)
178 .decode(jpeg_data)
179 .map_err(|e| Error::DecodeError(format!("JPEG decode failed: {}", e)))?;
180
181 let width = decoded.width;
182 let height = decoded.height;
183 let pixels = &decoded.data;
184 let bpp = decoded.bytes_per_pixel();
185
186 let data = if bpp == 3 {
188 let mut rgba = Vec::with_capacity((width * height * 4) as usize);
190 for chunk in pixels.chunks(3) {
191 rgba.push(chunk[0]);
192 rgba.push(chunk[1]);
193 rgba.push(chunk[2]);
194 rgba.push(255);
195 }
196 rgba
197 } else if bpp == 4 {
198 pixels.to_vec()
199 } else if bpp == 1 {
200 let mut rgba = Vec::with_capacity((width * height * 4) as usize);
202 for &g in pixels {
203 rgba.push(g);
204 rgba.push(g);
205 rgba.push(g);
206 rgba.push(255);
207 }
208 rgba
209 } else {
210 return Err(Error::DecodeError(format!(
211 "Unsupported bytes per pixel: {}",
212 bpp
213 )));
214 };
215
216 Ok(RawImage {
217 width,
218 height,
219 stride: width * 4,
220 data,
221 format: PixelFormat::Rgba8,
222 gamut: ColorGamut::Bt709, transfer: ColorTransfer::Srgb,
224 })
225}
226
227fn decode_jpeg_to_grayscale(jpeg_data: &[u8]) -> Result<RawImage> {
229 use jpegli::decoder::{Decoder as JpegDecoder, PixelFormat as JpegPixelFormat};
230 let decoded = JpegDecoder::new()
231 .output_format(JpegPixelFormat::Gray)
232 .decode(jpeg_data)
233 .map_err(|e| Error::DecodeError(format!("JPEG decode failed: {}", e)))?;
234
235 let width = decoded.width;
236 let height = decoded.height;
237 let pixels = &decoded.data;
238 let bpp = decoded.bytes_per_pixel();
239
240 let data = if bpp == 1 {
242 pixels.to_vec()
243 } else if bpp == 3 {
244 pixels
246 .chunks(3)
247 .map(|rgb| {
248 let r = rgb[0] as f32;
249 let g = rgb[1] as f32;
250 let b = rgb[2] as f32;
251 (0.2126_f32 * r + 0.7152 * g + 0.0722 * b).clamp(0.0, 255.0) as u8
253 })
254 .collect()
255 } else {
256 return Err(Error::DecodeError(format!(
257 "Unsupported bytes per pixel for grayscale: {}",
258 bpp
259 )));
260 };
261
262 Ok(RawImage {
263 width,
264 height,
265 stride: width,
266 data,
267 format: PixelFormat::Gray8,
268 gamut: ColorGamut::Bt709,
269 transfer: ColorTransfer::Srgb,
270 })
271}
272
273#[cfg(test)]
274mod tests {
275 use super::*;
276
277 #[test]
278 fn test_decoder_invalid_data() {
279 let result = Decoder::new(&[0, 1, 2, 3]);
280 assert!(result.is_err());
281 }
282
283 #[test]
284 fn test_decoder_minimal_jpeg() {
285 let data = vec![0xFF, 0xD8, 0xFF, 0xD9];
287 let decoder = Decoder::new(&data);
288 assert!(decoder.is_ok());
290 assert!(!decoder.unwrap().is_ultrahdr());
291 }
292}