-
Notifications
You must be signed in to change notification settings - Fork 623
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Metadata #1448
base: main
Are you sure you want to change the base?
Metadata #1448
Changes from 5 commits
6e1dffc
5a927d9
5eb4b4e
1f4d8d4
a46aa6d
c4090eb
f57a4c4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,6 +34,100 @@ pub enum ColorType { | |
__NonExhaustive(crate::utils::NonExhaustiveMarker), | ||
} | ||
|
||
/// Color information of an image's texels. | ||
#[derive(Clone, Debug, PartialEq)] | ||
#[non_exhaustive] | ||
pub enum Color { | ||
/// The color space is given by an encoded ICC profile. | ||
/// | ||
/// This is a superset of other options but the consumer must itself decode and extract the | ||
/// values. They should indicate an error similar to a completely unsupported color space in | ||
/// case this fails. | ||
Icc { | ||
/// The binary ICC data. | ||
profile: Vec<u8>, | ||
}, | ||
/// There is, explicitly, no known color model associated with these values. | ||
/// | ||
/// The image might contain indices without any associated color map, or it might represent | ||
/// quantities not related to color, or non-standard colorimetric values. Note that this is | ||
/// different from no information being available. | ||
Opaque, | ||
/// A common model based on the CIE 1931 XYZ observer. | ||
Xyz { | ||
/// The standardized RGB primary colors. | ||
primary: Primaries, | ||
/// The transfer function (electro-optical, opto-electrical). | ||
transfer: Transfer, | ||
/// The whitepoint of the color space. | ||
/// In general, we can not transform from one to another without loss of accuracy. | ||
whitepoint: Whitepoint, | ||
/// The absolute luminance of the values in the color space. | ||
luminance: Luminance, | ||
}, | ||
} | ||
|
||
/// Transfer functions from encoded chromatic samples to physical quantity. | ||
/// | ||
/// Ignoring viewing environmental effects, this describes a pair of functions that are each others | ||
/// inverse: An electro-optical transfer (EOTF) and opto-electronic transfer function (OETF) that | ||
/// describes how scene lighting is encoded as an electric signal. These are applied to each | ||
/// stimulus value. | ||
#[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
#[non_exhaustive] | ||
pub enum Transfer { | ||
/// Specified in ITU Rec.709. | ||
Bt709, | ||
Bt470M, | ||
/// Specified in ITU Rec.601. | ||
Bt601, | ||
Smpte240, | ||
/// Also known as the identity function. | ||
Linear, | ||
/// The common sRGB standard which is close to standard 'gamma correction'. | ||
Srgb, | ||
/// ITU Rec.2020 with 10 bit quantization. | ||
Bt2020_10bit, | ||
/// ITU Rec.2020 with 12 bit quantization. | ||
Bt2020_12bit, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does Bt2020_10bit use a different transfer function than Bt2020_12bit? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not mathematically, but the quantization is different. Anyways having both of them is straightup copied from the |
||
Smpte2084, | ||
/// Specified in ITU Rec.2100. | ||
/// The same as Smpte2084. | ||
Bt2100Pq, | ||
/// ITU Rec.2100 Hybrid Log-Gamma. | ||
Bt2100Hlg, | ||
} | ||
|
||
/// The reference brightness of the color specification. | ||
#[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
#[non_exhaustive] | ||
pub enum Luminance { | ||
/// 100cd/m². | ||
Sdr, | ||
/// 10_000cd/m². | ||
/// Known as high-dynamic range. | ||
Hdr, | ||
} | ||
|
||
/// The relative stimuli of the three corners of a triangular gamut. | ||
#[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
#[non_exhaustive] | ||
pub enum Primaries { | ||
Bt601_525, | ||
Bt601_625, | ||
Bt709, | ||
Smpte240, | ||
Bt2020, | ||
Bt2100, | ||
} | ||
|
||
/// The whitepoint/standard illuminant. | ||
#[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
#[non_exhaustive] | ||
pub enum Whitepoint { | ||
D65, | ||
} | ||
|
||
impl ColorType { | ||
/// Returns the number of bytes contained in a pixel of `ColorType` ```c``` | ||
pub fn bytes_per_pixel(self) -> u8 { | ||
|
@@ -81,6 +175,15 @@ impl ColorType { | |
} | ||
} | ||
|
||
impl Color { | ||
pub const SRGB: Color = Color::Xyz { | ||
luminance: Luminance::Sdr, | ||
primary: Primaries::Bt709, | ||
transfer: Transfer::Srgb, | ||
whitepoint: Whitepoint::D65, | ||
}; | ||
} | ||
|
||
/// An enumeration of color types encountered in image formats. | ||
/// | ||
/// This is not exhaustive over all existing image formats but should be granular enough to allow | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,220 @@ | ||
use std::cell::RefCell; | ||
use std::sync::{Arc, Mutex}; | ||
|
||
use crate::color::Color; | ||
|
||
// oder (Engram), Metagram, | ||
HeroicKatora marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// Collects some arbitrary meta data of an image. | ||
HeroicKatora marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// | ||
/// Note that information collected here will, per default, appear opaque to the `image` library | ||
/// itself. For example, the width and height of an image might be recorded as one set of values | ||
/// that's completely different from the reported dimensions in the decoder. During decoding and | ||
/// writing of images the library may ignore the color profile and exif data. You should always | ||
/// recheck with the specific decoder if you require color accuracy beyond presuming sRGB. (This | ||
/// may be improved upon in the future, if you have a concrete draft please open an issue). | ||
#[derive(Clone, Default)] | ||
pub struct Metagram { | ||
HeroicKatora marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// The original width in pixels. | ||
pub width: u32, | ||
/// The original height in pixels. | ||
pub height: u32, | ||
/// The available color information. | ||
/// For example, an ICC profile characterizing color interpretation of the image input. | ||
pub color_profile: Option<Color>, | ||
/// Encoded EXIF data associated with the image. | ||
pub exif: Option<Vec<u8>>, | ||
_non_exhaustive: (), | ||
} | ||
|
||
/// A buffer for the extra meta data produced by an image decoder. | ||
/// | ||
/// There are two main ways of creation: Any consumer of the `ImageDecoder` interface can create | ||
/// their own recorder to retrieve meta data from the decoder. A decoder wrapping another format | ||
/// (i.e. jpeg-within-tiff) might create a recorder from a `SharedRecorder` to add additional data | ||
/// to the outer recorder instead. | ||
/// | ||
/// # Use | ||
/// | ||
/// ```rust | ||
/// # struct FakeDecoder; | ||
/// # use image::ImageDecoder; | ||
/// # impl ImageDecoder<'_> for FakeDecoder { | ||
/// # type Reader = std::io::Empty; | ||
/// # fn dimensions(&self) -> (u32, u32) { (0, 0) } | ||
/// # fn color_type(&self) -> image::ColorType { todo!() } | ||
/// # fn into_reader(self) -> image::ImageResult<std::io::Empty> { Ok(std::io::empty()) } | ||
/// # } | ||
/// use image::io::{Metagram, Recorder}; | ||
/// | ||
/// let mut some_decoder = // .. | ||
/// # FakeDecoder; | ||
/// let mut recorder = Recorder::new(); | ||
/// some_decoder.metagram(&mut recorder); | ||
/// | ||
/// if recorder.is_shared() { | ||
/// // The decoder kept a shared clone, The complete metagram may not yet be available. | ||
/// // It will likely add more to the metagram while decoding. | ||
/// let mut _buffer = vec![0; some_decoder.total_bytes() as usize]; | ||
/// some_decoder.read_image(&mut _buffer); | ||
/// } | ||
/// | ||
/// let meta: Metagram = recorder.to_result(); | ||
/// ``` | ||
pub struct Recorder { | ||
inner: RecorderInner, | ||
} | ||
|
||
enum RecorderInner { | ||
Owned(Box<RefCell<Metagram>>), | ||
Shared(Arc<Mutex<Metagram>>), | ||
} | ||
|
||
/// An owned handle to a `Metagram`, that allows concurrent modification. | ||
#[allow(missing_copy_implementations)] | ||
pub struct SharedRecorder { | ||
inner: Arc<Mutex<Metagram>>, | ||
} | ||
|
||
impl Recorder { | ||
/// Create a recorder recording into a new, empty meta data. | ||
pub fn new() -> Self { | ||
Recorder::default() | ||
} | ||
|
||
/// Create a record that already contains some data. | ||
pub fn with(meta: Metagram) -> Self { | ||
Recorder { | ||
inner: RecorderInner::Owned(Box::new(RefCell::new(meta))), | ||
} | ||
} | ||
|
||
/// Check if this recorder was shared. | ||
pub fn is_shared(&self) -> bool { | ||
match self.inner { | ||
RecorderInner::Owned(_) => false, | ||
RecorderInner::Shared(_) => true, | ||
} | ||
} | ||
|
||
/// Split the recorder such that it can be sent to a different thread. | ||
pub fn share(&mut self) -> SharedRecorder { | ||
let inner; | ||
match &self.inner { | ||
RecorderInner::Shared(arc) => { | ||
inner = Arc::clone(&arc); | ||
}, | ||
RecorderInner::Owned(boxed) => { | ||
let meta = boxed.borrow().clone(); | ||
let arc = Arc::new(Mutex::new(meta)); | ||
inner = Arc::clone(&arc); | ||
self.inner = RecorderInner::Shared(arc); | ||
} | ||
}; | ||
|
||
SharedRecorder { | ||
inner, | ||
} | ||
} | ||
|
||
/// Get a clone of the configured meta data. | ||
pub fn to_result(&self) -> Metagram { | ||
self.inner.to_result() | ||
} | ||
} | ||
|
||
impl RecorderInner { | ||
fn with_mut(&self, function: impl FnOnce(&mut Metagram)) { | ||
match self { | ||
RecorderInner::Owned(boxed) => { | ||
function(&mut boxed.borrow_mut()) | ||
} | ||
RecorderInner::Shared(arc) => { | ||
function(&mut arc.lock().unwrap()) | ||
} | ||
} | ||
} | ||
|
||
fn to_result(&self) -> Metagram { | ||
match self { | ||
RecorderInner::Owned(boxed) => boxed.borrow().clone(), | ||
RecorderInner::Shared(arc) => arc.lock().unwrap().clone(), | ||
} | ||
} | ||
} | ||
|
||
/// Setters for recording meta data. | ||
/// Any change here should also be made for `SharedRecorder` unless it is specifically not possible | ||
/// to perform on a shared, and locked struct. | ||
impl Recorder { | ||
/// Add original dimensions. | ||
pub fn dimensions(&self, width: u32, height: u32) { | ||
self.inner.with_mut(|meta| meta.set_dimensions((width, height))); | ||
} | ||
|
||
/// Add a color profile. | ||
pub fn color(&self, color: Vec<u8>) { | ||
self.inner.with_mut(|meta| meta.set_color(color)) | ||
} | ||
|
||
/// Overwrite all EXIF data. | ||
pub fn exif(&self, data: Vec<u8>) { | ||
self.inner.with_mut(|meta| meta.set_exif(data)) | ||
} | ||
} | ||
|
||
impl SharedRecorder { | ||
/// Add original dimensions. | ||
pub fn dimensions(&self, width: u32, height: u32) { | ||
self.with_mut(|meta| meta.set_dimensions((width, height))); | ||
} | ||
|
||
/// Add a color profile. | ||
pub fn color(&self, color: Vec<u8>) { | ||
self.with_mut(|meta| meta.set_color(color)) | ||
} | ||
|
||
/// Overwrite all EXIF data. | ||
pub fn exif(&self, data: Vec<u8>) { | ||
self.with_mut(|meta| meta.set_exif(data)) | ||
} | ||
|
||
fn with_mut(&self, function: impl FnOnce(&mut Metagram)) { | ||
// Regarding lock recovery: None of the inner methods should usually panic. The only | ||
// exception would be from allocation error while inserting a new exif tag or something. | ||
function(&mut self.inner.lock().unwrap_or_else(|err| err.into_inner())) | ||
} | ||
} | ||
|
||
/// Private implementation of metagram, existing for the purpose of make assignment available as | ||
/// methods. | ||
impl Metagram { | ||
fn set_dimensions(&mut self, (width, height): (u32, u32)) { | ||
self.width = width; | ||
self.height = height; | ||
} | ||
|
||
fn set_color(&mut self, color: Vec<u8>) { | ||
self.color_profile = Some(color); | ||
} | ||
|
||
fn set_exif(&mut self, exif: Vec<u8>) { | ||
self.exif = Some(exif); | ||
} | ||
} | ||
|
||
impl Default for Recorder { | ||
fn default() -> Self { | ||
Recorder { | ||
inner: RecorderInner::Owned(Default::default()), | ||
} | ||
} | ||
} | ||
|
||
/// Convert a shared recorder into a non-thread safe variant. | ||
/// The two will _still_ record to the same meta data collection but this new instances could be | ||
/// used as a method argument to another `ImageDecoder` impl. | ||
impl From<SharedRecorder> for Recorder { | ||
fn from(shared: SharedRecorder) -> Recorder { | ||
Recorder { inner: RecorderInner::Shared(shared.inner) } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
//! Input and output of images. | ||
mod metagram; | ||
mod reader; | ||
pub(crate) mod free_functions; | ||
|
||
pub use self::reader::Reader; | ||
pub use self::metagram::{Metagram, Recorder, SharedRecorder}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this be more clear as
ColorSpace
?