1#![doc = include_str!("../README.md")]
2
3#[cfg(feature = "encoder")]
4mod encoder;
5mod segments;
6
7use std::io::{Cursor, Read};
8use std::ops::Range;
9
10use gufo_common::error::ErrorWithData;
11use gufo_common::math::*;
12use gufo_common::prelude::*;
13use indexmap::IndexMap;
14pub use segments::*;
15
16pub const EXIF_IDENTIFIER_STRING: &[u8] = b"Exif\0\0";
17pub const XMP_IDENTIFIER_STRING: &[u8] = b"http://ns.adobe.com/xap/1.0/\0";
18
19pub const MAGIC_BYTES: &[u8] = &[0xFF, 0xD8, 0xFF];
20
21pub const MARKER_START: u8 = 0xFF;
22
23#[derive(Debug)]
24pub struct Jpeg {
25 segments: Vec<RawSegment>,
26 data: Vec<u8>,
27}
28
29impl ImageFormat for Jpeg {
30 fn is_filetype(data: &[u8]) -> bool {
31 data.starts_with(MAGIC_BYTES)
32 }
33}
34
35impl ImageMetadata for Jpeg {
36 fn exif(&self) -> Vec<Vec<u8>> {
37 self.exif_data().map(|x| x.to_vec()).collect()
38 }
39
40 fn xmp(&self) -> Vec<Vec<u8>> {
41 self.exif_data().map(|x| x.to_vec()).collect()
42 }
43}
44
45impl Jpeg {
46 pub fn new(data: Vec<u8>) -> Result<Self, ErrorWithData<Error>> {
47 match Self::find_segments(&data) {
48 Ok(segments) => Ok(Self { segments, data }),
49 Err(err) => Err(ErrorWithData::new(err, data)),
50 }
51 }
52
53 pub fn into_inner(self) -> Vec<u8> {
54 self.data
55 }
56
57 pub fn segments(&self) -> Vec<Segment<'_>> {
59 self.segments.iter().map(|x| x.segment(self)).collect()
60 }
61
62 pub fn segments_marker(&self, marker: Marker) -> impl Iterator<Item = Segment<'_>> {
64 self.segments
65 .iter()
66 .filter(move |x| x.marker == Some(marker))
67 .map(|x| x.segment(self))
68 }
69
70 pub fn dqts(&self) -> Result<IndexMap<u8, Dqt>, Error> {
72 let segments = self.segments();
73
74 let mut dqts = Vec::new();
75 for i in segments
76 .into_iter()
77 .filter(|x| x.marker == Some(Marker::DQT))
78 {
79 let data = i.data();
80 dqts.push(Dqt::from_data(data)?);
81 }
82
83 let mut map = IndexMap::new();
84 for dqt in dqts.into_iter().flatten() {
85 map.insert(dqt.tq(), dqt);
86 }
87
88 Ok(map)
89 }
90
91 pub fn sof(&self) -> Result<Sof, Error> {
92 let segment = self
93 .segments()
94 .into_iter()
95 .find(|x| x.marker.is_some_and(|x| x.is_sof()))
96 .ok_or(Error::NoSofSegmentFound)?;
97
98 Sof::from_data(segment.data())
99 }
100
101 pub fn is_progressive(&self) -> Result<bool, Error> {
102 let sof_marker = self
103 .segments()
104 .into_iter()
105 .flat_map(|x| x.marker())
106 .find(|x| x.is_sof())
107 .ok_or(Error::NoSofSegmentFound)?;
108
109 sof_marker.is_progressive_sof()
110 }
111
112 pub fn n_sos(&self) -> usize {
116 self.segments()
117 .into_iter()
118 .filter(|x| matches!(x.marker, Some(Marker::SOS)))
119 .count()
120 }
121
122 pub fn sos(&self) -> Result<Sos, Error> {
123 let segment = self
124 .segment_by_marker(Marker::SOS)
125 .ok_or(Error::NoSosSegmentFound)?;
126
127 Sos::from_data(segment.data())
128 }
129
130 pub fn components_specification_parameters(
131 &self,
132 component: usize,
133 ) -> Result<ComponentSpecificationParameters, Error> {
134 let cs = self
135 .sos()?
136 .components_specifications
137 .get(component)
138 .ok_or(Error::MissingComponentSpecification)?
139 .cs;
140 self.sof()?
141 .parameters
142 .iter()
143 .find(|x| x.c == cs)
144 .ok_or(Error::MissingComponentSpecificationParameters)
145 .cloned()
146 }
147
148 pub fn color_model(&self) -> Result<ColorModel, Error> {
149 let sof = self.sof()?;
150 let n_components = sof.parameters.len();
151
152 if let Some(app14) = self.segment_by_marker(Marker::APP14) {
153 if app14.data().starts_with(b"Adobe\0") {
154 if let Some(color_model) = app14.data().get(11) {
155 return match *color_model {
156 0 if n_components == 4 => Ok(ColorModel::Cmyk),
157 0 if n_components == 3 => Ok(ColorModel::Rgb),
158 1 => Ok(ColorModel::YCbCr),
159 2 => Ok(ColorModel::Ycck),
160 _ => Err(Error::UnknownColorModel),
161 };
162 }
163 }
164 }
165
166 match n_components {
167 1 => Ok(ColorModel::Grayscale),
168 3 => Ok(ColorModel::YCbCr),
169 _ => Err(Error::UnknownColorModel),
170 }
171 }
172
173 pub fn segment_by_marker(&self, marker: Marker) -> Option<Segment<'_>> {
174 self.segments
175 .iter()
176 .find(|x| x.marker == Some(marker))
177 .map(|x| x.segment(self))
178 }
179
180 pub fn exif_segments(&self) -> impl Iterator<Item = Segment<'_>> {
181 self.segments_marker(Marker::APP1)
182 .filter(|x| x.data().starts_with(EXIF_IDENTIFIER_STRING))
183 }
184
185 pub fn exif_data(&self) -> impl Iterator<Item = &[u8]> {
186 self.exif_segments()
187 .filter_map(|x| x.data().get(EXIF_IDENTIFIER_STRING.len()..))
188 }
189
190 pub fn xmp_segments(&self) -> impl Iterator<Item = Segment<'_>> {
191 self.segments_marker(Marker::APP1)
192 .filter(|x| x.data().starts_with(XMP_IDENTIFIER_STRING))
193 }
194
195 pub fn xmp_data(&self) -> impl Iterator<Item = &[u8]> {
196 self.xmp_segments()
197 .filter_map(|x| x.data().get(XMP_IDENTIFIER_STRING.len()..))
198 }
199
200 fn find_segments(data: &[u8]) -> Result<Vec<RawSegment>, Error> {
201 let mut cur = Cursor::new(data);
202
203 let buf = &mut [0; 2];
204 cur.read_exact(buf).map_err(|_| Error::UnexpectedEof)?;
205
206 if data.get(..MAGIC_BYTES.len()) != Some(MAGIC_BYTES) {
207 return Err(Error::InvalidMagicBytes(*buf));
208 }
209
210 let mut segments = Vec::new();
211 segments.push(RawSegment {
212 marker: Some(Marker::SOI),
213 data: 2..2,
214 });
215
216 let mut entropy_coded_segment = false;
217 let byte = &mut [0; 1];
218 loop {
219 if entropy_coded_segment {
220 let data_start = cur.position().usize()?;
221 loop {
222 cur.read_exact(byte).map_err(|_| Error::UnexpectedEof)?;
223 if byte == &[MARKER_START] {
224 cur.read_exact(byte).map_err(|_| Error::UnexpectedEof)?;
225
226 if byte == &[0] {
227 continue;
228 } else {
229 let data_end = cur.position().safe_sub(2)?.usize()?;
230 segments.push(RawSegment {
231 marker: None,
232 data: data_start..data_end,
233 });
234 break;
235 }
236 }
237 }
238 } else {
239 cur.read_exact(byte).map_err(|_| Error::UnexpectedEof)?;
241
242 if byte != &[MARKER_START] {
243 return Err(Error::ExpectedMarkerStart(buf[0]));
244 }
245
246 cur.read_exact(byte).map_err(|_| Error::UnexpectedEof)?;
247
248 tracing::debug!("Found tag {byte:0>2X?}");
249 }
250
251 let marker = Marker::from(byte[0]);
252 let len_start = cur.position();
253
254 let (data_start, len) = if marker.is_standalone() {
255 (len_start.usize()?, 0)
256 } else {
257 cur.read_exact(buf).map_err(|_| Error::UnexpectedEof)?;
259 (len_start.usize()?.safe_add(2)?, u16::from_be_bytes(*buf))
260 };
261
262 let data_end = len_start.usize()?.safe_add(len.into())?;
263
264 let segment = RawSegment {
265 marker: Some(marker),
266 data: data_start..data_end,
267 };
268
269 tracing::debug!("Found segment {segment:?}");
270
271 segments.push(segment);
272
273 if marker == Marker::EOI {
274 break;
275 } else if marker == Marker::SOS {
276 entropy_coded_segment = true;
277 }
278
279 cur.set_position(len_start.safe_add(len.into())?);
280 }
281
282 Ok(segments)
283 }
284
285 pub fn replace_segment(
286 &mut self,
287 old_segment: RawSegment,
288 new_segment: NewSegment,
289 ) -> Result<(), Error> {
290 let old_range = old_segment.complete_data();
291
292 let mut new = Vec::new();
293 new.extend_from_slice(&self.data[..old_range.start]);
294 new_segment.write_to(&mut new);
295 new.extend_from_slice(&self.data[old_range.end..]);
296
297 self.data = new;
298 self.segments = Self::find_segments(&self.data)?;
299 Ok(())
300 }
301
302 pub fn replace_image_data(&mut self, other: &Self) -> Result<(), Error> {
307 let mut buf = Vec::with_capacity(other.data.len());
308 buf.extend_from_slice(&MAGIC_BYTES[0..2]);
309
310 for segment in &self.segments {
311 if segment.marker.is_some_and(|x| x.is_metadata()) {
312 buf.extend_from_slice(&self.data[segment.complete_data()]);
313 }
314 }
315
316 for segment in &other.segments {
317 if !matches!(segment.marker, Some(Marker::SOI)) {
318 buf.extend_from_slice(&other.data[segment.complete_data()]);
319 }
320 }
321
322 self.segments = Self::find_segments(&buf).unwrap();
323 self.data = buf;
324
325 Ok(())
326 }
327}
328
329#[derive(Debug)]
330pub struct NewSegment<'a> {
331 marker: Marker,
332 data: &'a [u8],
333 total_len: u16,
334}
335
336impl<'a> NewSegment<'a> {
337 pub fn new(marker: Marker, data: &'a [u8]) -> Result<Self, Error> {
338 let total_len = data.len().u16()?.safe_add(2)?;
339
340 Ok(Self {
341 marker,
342 data,
343 total_len,
344 })
345 }
346
347 pub fn write_to(&self, vec: &mut Vec<u8>) {
348 vec.push(MARKER_START);
349 vec.push(self.marker.into());
350 vec.extend_from_slice(&self.total_len.to_be_bytes());
351 vec.extend_from_slice(self.data);
352 }
353}
354
355#[derive(Debug)]
356pub struct RawSegment {
357 marker: Option<Marker>,
358 data: Range<usize>,
359}
360
361impl RawSegment {
362 pub fn segment<'a>(&self, jpeg: &'a Jpeg) -> Segment<'a> {
363 Segment {
364 marker: self.marker,
365 data: self.data.clone(),
366 jpeg,
367 }
368 }
369
370 pub fn complete_data(&self) -> Range<usize> {
372 let sub = if self.marker.is_some() { 4 } else { 0 };
373
374 self.data
375 .start
376 .checked_sub(sub)
377 .expect("Unreachable: Marker and length fields always exist")..self.data.end
378 }
379}
380
381#[derive(Clone, Debug)]
382pub struct Segment<'a> {
383 marker: Option<Marker>,
384 data: Range<usize>,
385 jpeg: &'a Jpeg,
386}
387
388impl<'a> Segment<'a> {
389 pub fn marker(&self) -> Option<Marker> {
390 self.marker
391 }
392
393 pub fn data_pos(&self) -> usize {
394 self.data.start
395 }
396
397 pub fn data(&self) -> &'a [u8] {
398 self.jpeg
399 .data
400 .get(self.data.clone())
401 .expect("Unreachable: This data must exist after successful loading")
402 }
403
404 pub fn unsafe_raw_segment(self) -> RawSegment {
405 RawSegment {
406 data: self.data,
407 marker: self.marker,
408 }
409 }
410}
411
412#[derive(Debug, Clone, thiserror::Error)]
413pub enum Error {
414 #[error("Invalid magic bytes: {0:x?}")]
415 InvalidMagicBytes([u8; 2]),
416 #[error("Unexpected end of file")]
417 UnexpectedEof,
418 #[error("Expected marker start: {0:x}")]
419 ExpectedMarkerStart(u8),
420 #[error("Math error: {0}")]
421 Math(#[from] MathError),
422 #[error("Unknown uantization table element precision {0}")]
423 UnknownPq(u8),
424 #[error("No SOS segment found")]
425 NoSosSegmentFound,
426 #[error("No SOF segment found")]
427 NoSofSegmentFound,
428 #[error("Couldn't detemine a color model")]
429 UnknownColorModel,
430 #[error("Missing component specification")]
431 MissingComponentSpecification,
432 #[error("Missing component specification parameters")]
433 MissingComponentSpecificationParameters,
434 #[error("Missing quantization table")]
435 MissingDqt,
436}
437
438gufo_common::utils::convertible_enum!(
439 #[repr(u8)]
440 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
441 #[non_exhaustive]
442 pub enum Marker {
444 TEM = 0x01,
445
446 SOF0 = 0xC0,
447 SOF1 = 0xC1,
448 SOF2 = 0xC2,
449 DHT = 0xC4,
451 RST0 = 0xD0,
452 RST1 = 0xD1,
453 RST2 = 0xD2,
454 RST3 = 0xD3,
455 RST4 = 0xD4,
456 RST5 = 0xD5,
457 RST6 = 0xD6,
458 RST7 = 0xD7,
459 SOI = 0xD8,
461 EOI = 0xD9,
463 SOS = 0xDA,
465 DQT = 0xDB,
467
468 APP0 = 0xE0,
469 APP1 = 0xE1,
471 APP2 = 0xE2,
473 APP3 = 0xE3,
474 APP4 = 0xE4,
475 APP5 = 0xE5,
476 APP6 = 0xE6,
477 APP7 = 0xE7,
478 APP8 = 0xE8,
479 APP9 = 0xE9,
480 APP10 = 0xEA,
481 APP11 = 0xEB,
482 APP12 = 0xEC,
483 APP13 = 0xED,
484 APP14 = 0xEE,
485 APP15 = 0xEF,
486 DRI = 0xDD,
488
489 JPG0 = 0xF0,
490 JPG1 = 0xF1,
491 JPG2 = 0xF2,
492 JPG3 = 0xF3,
493 JPG4 = 0xF4,
494 JPG5 = 0xF5,
495 JPG6 = 0xF6,
496 JPG7 = 0xF7,
497 JPG8 = 0xF8,
498 JPG9 = 0xF9,
499 JPG10 = 0xFA,
500 JPG11 = 0xFB,
501 JPG12 = 0xFC,
502 JPG13 = 0xFD,
503 COM = 0xFE,
505 }
506);
507
508impl Marker {
509 pub fn is_standalone(&self) -> bool {
510 matches!(
511 self,
512 Self::RST0
513 | Self::RST1
514 | Self::RST2
515 | Self::RST3
516 | Self::RST4
517 | Self::RST5
518 | Self::RST6
519 | Self::RST7
520 | Self::SOI
521 | Self::EOI
522 )
523 }
524
525 pub fn is_sof(&self) -> bool {
526 matches!(self, Self::SOF0 | Self::SOF1 | Self::SOF2)
527 }
528
529 pub fn is_progressive_sof(&self) -> Result<bool, Error> {
530 match self {
531 Self::SOF0 | Self::SOF1 => Ok(false),
532 Self::SOF2 => Ok(true),
533 _ => Err(Error::NoSofSegmentFound),
534 }
535 }
536
537 pub fn is_metadata(&self) -> bool {
538 matches!(
539 self,
540 Self::COM
541 | Self::APP0
542 | Self::APP1
543 | Self::APP2
544 | Self::APP3
545 | Self::APP4
546 | Self::APP5
547 | Self::APP6
548 | Self::APP7
549 | Self::APP8
550 | Self::APP9
551 | Self::APP10
552 | Self::APP11
553 | Self::APP12
554 | Self::APP13
555 | Self::APP14
556 | Self::APP15
557 )
558 }
559}
560
561#[derive(Debug)]
562pub enum ColorModel {
563 Grayscale,
564 YCbCr,
565 Cmyk,
566 Rgb,
567 Ycck,
568}