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