1use alloc::boxed::Box;
17use alloc::collections::TryReserveError;
18use alloc::string::String;
19use core::error::Error;
20use core::fmt;
21use no_std_io::io;
22
23use crate::color::ExtendedColorType;
24use crate::{metadata::Cicp, ImageFormat};
25
26#[derive(Debug)]
31pub enum ImageError {
32 Decoding(DecodingError),
38
39 Encoding(EncodingError),
46
47 Parameter(ParameterError),
52
53 Limits(LimitError),
58
59 Unsupported(UnsupportedError),
66
67 IoError(io::Error),
69}
70
71#[derive(Debug)]
77pub struct UnsupportedError {
78 format: ImageFormatHint,
79 kind: UnsupportedErrorKind,
80}
81
82#[derive(Clone, Debug, Hash, PartialEq)]
84#[non_exhaustive]
85pub enum UnsupportedErrorKind {
86 Color(ExtendedColorType),
88 ColorLayout(ExtendedColorType),
90 ColorspaceCicp(Cicp),
92 Format(ImageFormatHint),
94 GenericFeature(String),
97}
98
99#[derive(Debug)]
106pub struct EncodingError {
107 format: ImageFormatHint,
108 underlying: Option<Box<dyn Error + Send + Sync>>,
109}
110
111#[derive(Debug)]
118pub struct ParameterError {
119 kind: ParameterErrorKind,
120 underlying: Option<Box<dyn Error + Send + Sync>>,
121}
122
123#[derive(Clone, Debug, Hash, PartialEq)]
125#[non_exhaustive]
126pub enum ParameterErrorKind {
127 DimensionMismatch,
129 FailedAlready,
131 RgbCicpRequired(Cicp),
133 Generic(String),
136 NoMoreData,
138 CicpMismatch {
140 expected: Cicp,
142 found: Cicp,
144 },
145}
146
147#[derive(Debug)]
154pub struct DecodingError {
155 format: ImageFormatHint,
156 underlying: Option<Box<dyn Error + Send + Sync>>,
157}
158
159#[derive(Debug)]
166pub struct LimitError {
167 kind: LimitErrorKind,
168 }
170
171#[derive(Clone, Debug, Hash, PartialEq, Eq)]
176#[non_exhaustive]
177#[allow(missing_copy_implementations)] pub enum LimitErrorKind {
179 DimensionError,
181 InsufficientMemory,
183 Unsupported {
185 limits: crate::Limits,
187 supported: crate::LimitSupport,
189 },
190}
191
192#[derive(Clone, Debug, Hash, PartialEq)]
194#[non_exhaustive]
195pub enum ImageFormatHint {
196 Exact(ImageFormat),
198
199 Name(String),
201
202 #[cfg(feature = "std")]
204 PathExtension(std::path::PathBuf),
205
206 Unknown,
208}
209
210impl UnsupportedError {
211 #[must_use]
216 pub fn from_format_and_kind(format: ImageFormatHint, kind: UnsupportedErrorKind) -> Self {
217 UnsupportedError { format, kind }
218 }
219
220 #[must_use]
222 pub fn kind(&self) -> UnsupportedErrorKind {
223 self.kind.clone()
224 }
225
226 #[must_use]
228 pub fn format_hint(&self) -> ImageFormatHint {
229 self.format.clone()
230 }
231}
232
233impl DecodingError {
234 pub fn new(format: ImageFormatHint, err: impl Into<Box<dyn Error + Send + Sync>>) -> Self {
236 DecodingError {
237 format,
238 underlying: Some(err.into()),
239 }
240 }
241
242 #[must_use]
246 pub fn from_format_hint(format: ImageFormatHint) -> Self {
247 DecodingError {
248 format,
249 underlying: None,
250 }
251 }
252
253 #[must_use]
255 pub fn format_hint(&self) -> ImageFormatHint {
256 self.format.clone()
257 }
258}
259
260impl EncodingError {
261 pub fn new(format: ImageFormatHint, err: impl Into<Box<dyn Error + Send + Sync>>) -> Self {
263 EncodingError {
264 format,
265 underlying: Some(err.into()),
266 }
267 }
268
269 #[must_use]
273 pub fn from_format_hint(format: ImageFormatHint) -> Self {
274 EncodingError {
275 format,
276 underlying: None,
277 }
278 }
279
280 #[must_use]
282 pub fn format_hint(&self) -> ImageFormatHint {
283 self.format.clone()
284 }
285}
286
287impl ParameterError {
288 #[must_use]
290 pub fn from_kind(kind: ParameterErrorKind) -> Self {
291 ParameterError {
292 kind,
293 underlying: None,
294 }
295 }
296
297 #[must_use]
299 pub fn kind(&self) -> ParameterErrorKind {
300 self.kind.clone()
301 }
302}
303
304impl LimitError {
305 #[must_use]
307 pub fn from_kind(kind: LimitErrorKind) -> Self {
308 LimitError { kind }
309 }
310
311 #[must_use]
313 pub fn kind(&self) -> LimitErrorKind {
314 self.kind.clone()
315 }
316}
317
318impl From<LimitErrorKind> for LimitError {
319 fn from(kind: LimitErrorKind) -> Self {
320 Self { kind }
321 }
322}
323
324impl From<io::Error> for ImageError {
325 fn from(err: io::Error) -> ImageError {
326 ImageError::IoError(err)
327 }
328}
329
330impl From<TryReserveError> for ImageError {
331 fn from(_: TryReserveError) -> ImageError {
332 ImageError::Limits(LimitErrorKind::InsufficientMemory.into())
333 }
334}
335
336impl From<ImageFormat> for ImageFormatHint {
337 fn from(format: ImageFormat) -> Self {
338 ImageFormatHint::Exact(format)
339 }
340}
341
342#[cfg(feature = "std")]
343impl From<&'_ std::path::Path> for ImageFormatHint {
344 fn from(path: &'_ std::path::Path) -> Self {
345 match path.extension() {
346 Some(ext) => ImageFormatHint::PathExtension(ext.into()),
347 None => ImageFormatHint::Unknown,
348 }
349 }
350}
351
352impl From<ImageFormatHint> for UnsupportedError {
353 fn from(hint: ImageFormatHint) -> Self {
354 UnsupportedError {
355 format: hint.clone(),
356 kind: UnsupportedErrorKind::Format(hint),
357 }
358 }
359}
360
361pub type ImageResult<T> = Result<T, ImageError>;
363
364impl fmt::Display for ImageError {
365 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
366 match self {
367 ImageError::IoError(err) => err.fmt(fmt),
368 ImageError::Decoding(err) => err.fmt(fmt),
369 ImageError::Encoding(err) => err.fmt(fmt),
370 ImageError::Parameter(err) => err.fmt(fmt),
371 ImageError::Limits(err) => err.fmt(fmt),
372 ImageError::Unsupported(err) => err.fmt(fmt),
373 }
374 }
375}
376
377impl Error for ImageError {
378 fn source(&self) -> Option<&(dyn Error + 'static)> {
379 match self {
380 ImageError::IoError(_err) => None,
381 ImageError::Decoding(err) => err.source(),
382 ImageError::Encoding(err) => err.source(),
383 ImageError::Parameter(err) => err.source(),
384 ImageError::Limits(err) => err.source(),
385 ImageError::Unsupported(err) => err.source(),
386 }
387 }
388}
389
390impl fmt::Display for UnsupportedError {
391 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
392 match &self.kind {
393 UnsupportedErrorKind::Format(ImageFormatHint::Unknown) => {
394 write!(fmt, "The image format could not be determined",)
395 }
396 #[cfg(feature = "std")]
397 UnsupportedErrorKind::Format(format @ ImageFormatHint::PathExtension(_)) => write!(
398 fmt,
399 "The file extension {format} was not recognized as an image format",
400 ),
401 UnsupportedErrorKind::Format(format) => {
402 write!(fmt, "The image format {format} is not supported",)
403 }
404 UnsupportedErrorKind::Color(color) => write!(
405 fmt,
406 "The encoder or decoder for {} does not support the color type `{:?}`",
407 self.format, color,
408 ),
409 UnsupportedErrorKind::ColorLayout(layout) => write!(
410 fmt,
411 "Converting with the texel memory layout {layout:?} is not supported",
412 ),
413 UnsupportedErrorKind::ColorspaceCicp(color) => write!(
414 fmt,
415 "The colorimetric interpretation of a CICP color space is not supported for `{color:?}`",
416 ),
417 UnsupportedErrorKind::GenericFeature(message) => match &self.format {
418 ImageFormatHint::Unknown => write!(
419 fmt,
420 "The decoder does not support the format feature {message}",
421 ),
422 other => write!(
423 fmt,
424 "The decoder for {other} does not support the format features {message}",
425 ),
426 },
427 }
428 }
429}
430
431impl Error for UnsupportedError {}
432
433impl fmt::Display for ParameterError {
434 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
435 match &self.kind {
436 ParameterErrorKind::DimensionMismatch => write!(
437 fmt,
438 "The Image's dimensions are either too \
439 small or too large"
440 ),
441 ParameterErrorKind::FailedAlready => write!(
442 fmt,
443 "The end the image stream has been reached due to a previous error"
444 ),
445 ParameterErrorKind::RgbCicpRequired(cicp) => {
446 write!(fmt, "The CICP {cicp:?} can not be used for RGB images",)
447 }
448
449 ParameterErrorKind::Generic(message) => {
450 write!(fmt, "The parameter is malformed: {message}",)
451 }
452 ParameterErrorKind::NoMoreData => write!(fmt, "The end of the image has been reached",),
453 ParameterErrorKind::CicpMismatch { expected, found } => {
454 write!(
455 fmt,
456 "The color space {found:?} does not match the expected {expected:?}",
457 )
458 }
459 }?;
460
461 if let Some(underlying) = &self.underlying {
462 write!(fmt, "\n{underlying}")?;
463 }
464
465 Ok(())
466 }
467}
468
469impl Error for ParameterError {
470 fn source(&self) -> Option<&(dyn Error + 'static)> {
471 match &self.underlying {
472 None => None,
473 Some(source) => Some(&**source),
474 }
475 }
476}
477
478impl fmt::Display for EncodingError {
479 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
480 match &self.underlying {
481 Some(underlying) => write!(
482 fmt,
483 "Format error encoding {}:\n{}",
484 self.format, underlying,
485 ),
486 None => write!(fmt, "Format error encoding {}", self.format,),
487 }
488 }
489}
490
491impl Error for EncodingError {
492 fn source(&self) -> Option<&(dyn Error + 'static)> {
493 match &self.underlying {
494 None => None,
495 Some(source) => Some(&**source),
496 }
497 }
498}
499
500impl fmt::Display for DecodingError {
501 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
502 match &self.underlying {
503 None => match self.format {
504 ImageFormatHint::Unknown => write!(fmt, "Format error"),
505 _ => write!(fmt, "Format error decoding {}", self.format),
506 },
507 Some(underlying) => {
508 write!(fmt, "Format error decoding {}: {}", self.format, underlying)
509 }
510 }
511 }
512}
513
514impl Error for DecodingError {
515 fn source(&self) -> Option<&(dyn Error + 'static)> {
516 match &self.underlying {
517 None => None,
518 Some(source) => Some(&**source),
519 }
520 }
521}
522
523impl fmt::Display for LimitError {
524 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
525 match self.kind {
526 LimitErrorKind::InsufficientMemory => write!(fmt, "Memory limit exceeded"),
527 LimitErrorKind::DimensionError => write!(fmt, "Image size exceeds limit"),
528 LimitErrorKind::Unsupported { .. } => {
529 write!(fmt, "The following strict limits are specified but not supported by the opertation: ")?;
530 Ok(())
531 }
532 }
533 }
534}
535
536impl Error for LimitError {}
537
538impl fmt::Display for ImageFormatHint {
539 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
540 match self {
541 ImageFormatHint::Exact(format) => write!(fmt, "{format:?}"),
542 ImageFormatHint::Name(name) => write!(fmt, "`{name}`"),
543 #[cfg(feature = "std")]
544 ImageFormatHint::PathExtension(ext) => write!(fmt, "`.{ext:?}`"),
545 ImageFormatHint::Unknown => write!(fmt, "`Unknown`"),
546 }
547 }
548}
549
550#[derive(Clone)]
554#[allow(missing_copy_implementations)]
555pub struct TryFromExtendedColorError {
556 pub(crate) was: ExtendedColorType,
557}
558
559impl fmt::Debug for TryFromExtendedColorError {
560 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
561 write!(f, "{self}")
562 }
563}
564
565impl fmt::Display for TryFromExtendedColorError {
566 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
567 write!(
568 f,
569 "The pixel layout {:?} is not supported as a buffer ColorType",
570 self.was
571 )
572 }
573}
574
575impl Error for TryFromExtendedColorError {}
576
577impl From<TryFromExtendedColorError> for ImageError {
578 fn from(err: TryFromExtendedColorError) -> ImageError {
579 ImageError::Unsupported(UnsupportedError::from_format_and_kind(
580 ImageFormatHint::Unknown,
581 UnsupportedErrorKind::Color(err.was),
582 ))
583 }
584}
585
586#[cfg(test)]
587mod tests {
588 use super::*;
589 use core::mem::size_of;
590
591 #[allow(dead_code)]
592 const ASSERT_SMALLISH: usize = [0][(size_of::<ImageError>() >= 200) as usize];
594
595 #[test]
596 fn test_send_sync_stability() {
597 fn assert_send_sync<T: Send + Sync>() {}
598
599 assert_send_sync::<ImageError>();
600 }
601}