diff --git a/Cargo.toml b/Cargo.toml
index ad06e5387d..fa6b8938ec 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -29,10 +29,11 @@ name = "image"
path = "./src/lib.rs"
[dependencies]
-byteorder = "1.2.1"
+byteorder = "1.3.2"
num-iter = "0.1.32"
num-rational = { version = "0.2.1", default-features = false }
num-traits = "0.2.0"
+zerocopy = "0.2.8"
[dependencies.gif]
version = "0.10.0"
diff --git a/src/buffer.rs b/src/buffer.rs
index f10cc39e25..a288000fd8 100644
--- a/src/buffer.rs
+++ b/src/buffer.rs
@@ -4,12 +4,14 @@ use std::ops::{Deref, DerefMut, Index, IndexMut, Range};
use std::path::Path;
use std::slice::{Chunks, ChunksMut};
-use color::{ColorType, FromColor, Luma, LumaA, Rgb, Rgba, Bgr, Bgra};
+use color::{ChannelsType, ColorType, FromColor, Luma, LumaA, Rgb, Rgba, Bgr, Bgra};
use flat::{FlatSamples, SampleLayout};
use dynimage::{save_buffer, save_buffer_with_format};
use image::{GenericImage, GenericImageView, ImageFormat, ImageResult};
use traits::Primitive;
use utils::expand_packed;
+use zerocopy::AsBytes;
+
/// A generalized pixel.
///
@@ -42,13 +44,10 @@ pub trait Pixel: Copy + Clone {
Self::COLOR_MODEL
}
- /// ColorType for this pixel format
- const COLOR_TYPE: ColorType;
+ /// Channels for this pixel format
+ const CHANNELS_TYPE: ChannelsType;
/// Returns the ColorType for this pixel format
- #[deprecated(note="please use COLOR_TYPE associated constant")]
- fn color_type() -> ColorType {
- Self::COLOR_TYPE
- }
+ fn color_type() -> ColorType;
/// Returns the channels of this pixel as a 4 tuple. If the pixel
/// has less than 4 channels the remainder is filled with the maximum value
@@ -655,7 +654,7 @@ where
FlatSamples {
samples: self.data,
layout,
- color_hint: Some(P::COLOR_TYPE),
+ color_hint: Some(P::color_type()),
}
}
@@ -669,7 +668,7 @@ where
FlatSamples {
samples: self.data.as_ref(),
layout,
- color_hint: Some(P::COLOR_TYPE),
+ color_hint: Some(P::color_type()),
}
}
}
@@ -745,8 +744,9 @@ where
impl
ImageBuffer
where
- P: Pixel + 'static,
- Container: Deref,
+ P: Pixel + 'static,
+ P::Subpixel: AsBytes,
+ Container: Deref,
{
/// Saves the buffer to a file at the path specified.
///
@@ -759,18 +759,19 @@ where
// This is valid as the subpixel is u8.
save_buffer(
path,
- self,
+ self.as_bytes(),
self.width(),
self.height(),
- ::COLOR_TYPE,
+
::color_type(),
)
}
}
impl
ImageBuffer
where
- P: Pixel + 'static,
- Container: Deref,
+ P: Pixel + 'static,
+ P::Subpixel: AsBytes,
+ Container: Deref,
{
/// Saves the buffer to a file at the specified path in
/// the specified format.
@@ -784,10 +785,10 @@ where
// This is valid as the subpixel is u8.
save_buffer_with_format(
path,
- self,
+ self.as_bytes(),
self.width(),
self.height(),
- ::COLOR_TYPE,
+
::color_type(),
format,
)
}
@@ -1076,6 +1077,14 @@ pub type GrayAlphaImage = ImageBuffer, Vec>;
pub(crate) type BgrImage = ImageBuffer, Vec>;
/// Sendable Bgr + alpha channel image buffer
pub(crate) type BgraImage = ImageBuffer, Vec>;
+/// Sendable 16-bit Rgb image buffer
+pub type Rgb16Image = ImageBuffer, Vec>;
+/// Sendable 16-bit Rgb + alpha channel image buffer
+pub type Rgba16Image = ImageBuffer, Vec>;
+/// Sendable 16-bit grayscale image buffer
+pub type Gray16Image = ImageBuffer, Vec>;
+/// Sendable 16-bit grayscale + alpha channel image buffer
+pub type GrayAlpha16Image = ImageBuffer, Vec>;
#[cfg(test)]
mod test {
diff --git a/src/color.rs b/src/color.rs
index 64e074ddde..d90212bbcb 100644
--- a/src/color.rs
+++ b/src/color.rs
@@ -1,9 +1,39 @@
-use num_traits::{NumCast, Zero};
+use num_traits::{NumCast, ToPrimitive, Zero};
use std::ops::{Index, IndexMut};
use buffer::Pixel;
use traits::Primitive;
+/// Supported ordered sets of channels
+#[derive(Copy, PartialEq, Eq, Debug, Clone, Hash)]
+pub enum ChannelsType {
+ /// Luminance
+ L,
+ /// Luminance and alpha
+ La,
+ /// Red, green and blue
+ Rgb,
+ /// Red, green, blue and alpha
+ Rgba,
+ /// Blue, green and red
+ Bgr,
+ /// Blue, green, red and alpha
+ Bgra,
+}
+
+impl ChannelsType {
+ pub fn channel_count(self) -> u8 {
+ match self {
+ Self::L => 1,
+ Self::La => 2,
+ Self::Rgb => 3,
+ Self::Rgba => 4,
+ Self::Bgr => 3,
+ Self::Bgra => 4,
+ }
+ }
+}
+
/// An enumeration over supported color types and bit depths
#[derive(Copy, PartialEq, Eq, Debug, Clone, Hash)]
pub enum ColorType {
@@ -48,6 +78,11 @@ impl ColorType {
}
}
+ /// Returns the number of bytes contained in a channel per pixel of `ColorType` ```c```
+ pub fn bytes_per_channel(self) -> u8 {
+ self.bytes_per_pixel() / self.channel_count()
+ }
+
/// Returns the number of bits contained in a pixel of `ColorType` ```c``` (which will always be
/// a multiple of 8).
pub fn bits_per_pixel(self) -> u16 {
@@ -96,34 +131,38 @@ pub enum ExtendedColorType {
}
impl ExtendedColorType {
- pub fn channel_count(self) -> u8 {
+ pub fn channels_type(self) -> ChannelsType {
match self {
ExtendedColorType::L1 |
ExtendedColorType::L2 |
ExtendedColorType::L4 |
ExtendedColorType::L8 |
ExtendedColorType::L16 |
- ExtendedColorType::Unknown(_) => 1,
+ ExtendedColorType::Unknown(_) => ChannelsType::L,
ExtendedColorType::La1 |
ExtendedColorType::La2 |
ExtendedColorType::La4 |
ExtendedColorType::La8 |
- ExtendedColorType::La16 => 2,
+ ExtendedColorType::La16 => ChannelsType::La,
ExtendedColorType::Rgb1 |
ExtendedColorType::Rgb2 |
ExtendedColorType::Rgb4 |
ExtendedColorType::Rgb8 |
- ExtendedColorType::Rgb16 |
- ExtendedColorType::Bgr8 => 3,
+ ExtendedColorType::Rgb16 => ChannelsType::Rgb,
+ ExtendedColorType::Bgr8 => ChannelsType::Bgr,
ExtendedColorType::Rgba1 |
ExtendedColorType::Rgba2 |
ExtendedColorType::Rgba4 |
ExtendedColorType::Rgba8 |
- ExtendedColorType::Rgba16 |
- ExtendedColorType::Bgra8 => 4,
+ ExtendedColorType::Rgba16 => ChannelsType::Rgba,
+ ExtendedColorType::Bgra8 => ChannelsType::Bgra,
ExtendedColorType::__Nonexhaustive => unreachable!(),
+
}
}
+ pub fn channel_count(self) -> u8 {
+ self.channels_type().channel_count()
+ }
}
impl From for ExtendedColorType {
fn from(c: ColorType) -> Self {
@@ -149,7 +188,7 @@ macro_rules! define_colors {
$channels: expr,
$alphas: expr,
$interpretation: expr,
- $color_type: expr,
+ $channels_type: expr,
#[$doc:meta];
)*} => {
@@ -168,7 +207,22 @@ impl Pixel for $ident {
const COLOR_MODEL: &'static str = $interpretation;
- const COLOR_TYPE: ColorType = $color_type;
+ const CHANNELS_TYPE: ChannelsType = $channels_type;
+
+ fn color_type() -> ColorType {
+ match (Self::CHANNELS_TYPE, std::mem::size_of::()) {
+ (ChannelsType::L, 1) => ColorType::L8,
+ (ChannelsType::L, _) => ColorType::L16,
+ (ChannelsType::La, 1) => ColorType::La8,
+ (ChannelsType::La, _) => ColorType::La16,
+ (ChannelsType::Rgb, 1) => ColorType::Rgb8,
+ (ChannelsType::Rgb, _) => ColorType::Rgb16,
+ (ChannelsType::Rgba, 1) => ColorType::Rgba8,
+ (ChannelsType::Rgba, _) => ColorType::Rgba16,
+ (ChannelsType::Bgr, _) => ColorType::Bgr8,
+ (ChannelsType::Bgra, _) => ColorType::Bgra8,
+ }
+ }
#[inline(always)]
fn channels(&self) -> &[T] {
@@ -302,18 +356,24 @@ impl IndexMut for $ident {
}
}
+impl From<[T; $channels]> for $ident {
+ fn from(c: [T; $channels]) -> Self {
+ Self(c)
+ }
+}
+
)* // END Structure definitions
}
}
define_colors! {
- Rgb, 3, 0, "RGB", ColorType::Rgb8, #[doc = "RGB colors"];
- Bgr, 3, 0, "BGR", ColorType::Bgr8, #[doc = "BGR colors"];
- Luma, 1, 0, "Y", ColorType::L8, #[doc = "Grayscale colors"];
- Rgba, 4, 1, "RGBA", ColorType::Rgba8, #[doc = "RGB colors + alpha channel"];
- Bgra, 4, 1, "BGRA", ColorType::Bgra8, #[doc = "BGR colors + alpha channel"];
- LumaA, 2, 1, "YA", ColorType::La8, #[doc = "Grayscale colors + alpha channel"];
+ Rgb, 3, 0, "RGB", ChannelsType::Rgb, #[doc = "RGB colors"];
+ Bgr, 3, 0, "BGR", ChannelsType::Bgr, #[doc = "BGR colors"];
+ Luma, 1, 0, "Y", ChannelsType::L, #[doc = "Grayscale colors"];
+ Rgba, 4, 1, "RGBA", ChannelsType::Rgba, #[doc = "RGB colors + alpha channel"];
+ Bgra, 4, 1, "BGRA", ChannelsType::Bgra, #[doc = "BGR colors + alpha channel"];
+ LumaA, 2, 1, "YA", ChannelsType::La, #[doc = "Grayscale colors + alpha channel"];
}
/// Provides color conversions for the different pixel types.
@@ -329,15 +389,61 @@ impl FromColor for A {
}
}
-/// `FromColor` for Luma
+/// Copy-based conversions to target pixel types using `FromColor`.
+pub(crate) trait IntoColor {
+ /// Constructs a pixel of the target type and converts this pixel into it.
+ fn into_color(&self) -> Other;
+}
+
+impl IntoColor for S
+where
+ O: ::buffer::Pixel + FromColor {
+ fn into_color(&self) -> O {
+ // Note we cannot use Pixel::CHANNELS_COUNT here to directly construct
+ // the pixel due to a current bug/limitation of consts.
+ let mut pix = O::from_channels(Zero::zero(), Zero::zero(), Zero::zero(), Zero::zero());
+ pix.from_color(self);
+ pix
+ }
+}
+
+/// Coefficients to transform from sRGB to a CIE Y (luminance) value.
+const SRGB_LUMA: [f32; 3] = [0.2126, 0.7152, 0.0722];
+
+#[inline]
+fn rgb_to_luma(rgb: &[T]) -> T {
+ let l = SRGB_LUMA[0] * rgb[0].to_f32().unwrap()
+ + SRGB_LUMA[1] * rgb[1].to_f32().unwrap()
+ + SRGB_LUMA[2] * rgb[2].to_f32().unwrap();
+ NumCast::from(l).unwrap()
+}
+
+#[inline]
+fn bgr_to_luma(bgr: &[T]) -> T {
+ let l = SRGB_LUMA[0] * bgr[2].to_f32().unwrap()
+ + SRGB_LUMA[1] * bgr[1].to_f32().unwrap()
+ + SRGB_LUMA[2] * bgr[0].to_f32().unwrap();
+ NumCast::from(l).unwrap()
+}
+
+#[inline]
+fn downcast_channel(c16: u16) -> u8 {
+ NumCast::from(c16.to_u64().unwrap() >> 8).unwrap()
+}
+
+#[inline]
+fn upcast_channel(c8: u8) -> u16 {
+ NumCast::from(c8.to_u64().unwrap() << 8).unwrap()
+}
+
+
+// `FromColor` for Luma
impl FromColor> for Luma {
fn from_color(&mut self, other: &Rgba) {
let gray = self.channels_mut();
- let rgb = other.channels();
- let l = 0.2126f32 * rgb[0].to_f32().unwrap() + 0.7152f32 * rgb[1].to_f32().unwrap()
- + 0.0722f32 * rgb[2].to_f32().unwrap();
- gray[0] = NumCast::from(l).unwrap()
+ let rgba = other.channels();
+ gray[0] = rgb_to_luma(rgba);
}
}
@@ -345,9 +451,7 @@ impl FromColor> for Luma {
fn from_color(&mut self, other: &Bgra) {
let gray = self.channels_mut();
let bgra = other.channels();
- let l = 0.2126f32 * bgra[2].to_f32().unwrap() + 0.7152f32 * bgra[1].to_f32().unwrap()
- + 0.0722f32 * bgra[0].to_f32().unwrap();
- gray[0] = NumCast::from(l).unwrap()
+ gray[0] = bgr_to_luma(bgra);
}
}
@@ -355,39 +459,72 @@ impl FromColor> for Luma {
fn from_color(&mut self, other: &Rgb) {
let gray = self.channels_mut();
let rgb = other.channels();
- let l = 0.2126f32 * rgb[0].to_f32().unwrap() + 0.7152f32 * rgb[1].to_f32().unwrap()
- + 0.0722f32 * rgb[2].to_f32().unwrap();
- gray[0] = NumCast::from(l).unwrap()
+ gray[0] = rgb_to_luma(rgb);
}
}
-
impl FromColor> for Luma {
fn from_color(&mut self, other: &Bgr) {
let gray = self.channels_mut();
let bgr = other.channels();
- let l = 0.2126f32 * bgr[2].to_f32().unwrap() + 0.7152f32 * bgr[1].to_f32().unwrap()
- + 0.0722f32 * bgr[0].to_f32().unwrap();
- gray[0] = NumCast::from(l).unwrap()
+ gray[0] = bgr_to_luma(bgr);
}
}
-
impl FromColor> for Luma {
fn from_color(&mut self, other: &LumaA) {
self.channels_mut()[0] = other.channels()[0]
}
}
-/// `FromColor` for LumA
+
+impl FromColor> for Luma {
+ fn from_color(&mut self, other: &Rgba) {
+ let gray = self.channels_mut();
+ let rgb = other.channels();
+ let l = rgb_to_luma(rgb);
+ gray[0] = downcast_channel(l);
+ }
+}
+
+impl FromColor> for Luma {
+ fn from_color(&mut self, other: &Rgb) {
+ let gray = self.channels_mut();
+ let rgb = other.channels();
+ let l = rgb_to_luma(rgb);
+ gray[0] = downcast_channel(l);
+ }
+}
+
+impl FromColor> for Luma {
+ fn from_color(&mut self, other: &Luma) {
+ let l = other.channels()[0];
+ self.channels_mut()[0] = downcast_channel(l);
+ }
+}
+
+impl FromColor> for Luma {
+ fn from_color(&mut self, other: &Luma) {
+ let l = other.channels()[0];
+ self.channels_mut()[0] = upcast_channel(l);
+ }
+}
+
+impl FromColor> for Luma {
+ fn from_color(&mut self, other: &LumaA) {
+ let l = other.channels()[0];
+ self.channels_mut()[0] = downcast_channel(l);
+ }
+}
+
+
+// `FromColor` for LumaA
impl FromColor> for LumaA {
fn from_color(&mut self, other: &Rgba) {
let gray_a = self.channels_mut();
let rgba = other.channels();
- let l = 0.2126f32 * rgba[0].to_f32().unwrap() + 0.7152f32 * rgba[1].to_f32().unwrap()
- + 0.0722f32 * rgba[2].to_f32().unwrap();
- gray_a[0] = NumCast::from(l).unwrap();
+ gray_a[0] = rgb_to_luma(rgba);
gray_a[1] = rgba[3];
}
}
@@ -396,9 +533,7 @@ impl FromColor> for LumaA {
fn from_color(&mut self, other: &Bgra) {
let gray_a = self.channels_mut();
let bgra = other.channels();
- let l = 0.2126f32 * bgra[2].to_f32().unwrap() + 0.7152f32 * bgra[1].to_f32().unwrap()
- + 0.0722f32 * bgra[0].to_f32().unwrap();
- gray_a[0] = NumCast::from(l).unwrap();
+ gray_a[0] = bgr_to_luma(bgra);
gray_a[1] = bgra[3];
}
}
@@ -407,9 +542,7 @@ impl FromColor> for LumaA {
fn from_color(&mut self, other: &Rgb) {
let gray_a = self.channels_mut();
let rgb = other.channels();
- let l = 0.2126f32 * rgb[0].to_f32().unwrap() + 0.7152f32 * rgb[1].to_f32().unwrap()
- + 0.0722f32 * rgb[2].to_f32().unwrap();
- gray_a[0] = NumCast::from(l).unwrap();
+ gray_a[0] = rgb_to_luma(rgb);
gray_a[1] = T::max_value();
}
}
@@ -418,9 +551,7 @@ impl FromColor> for LumaA {
fn from_color(&mut self, other: &Bgr) {
let gray_a = self.channels_mut();
let bgr = other.channels();
- let l = 0.2126f32 * bgr[2].to_f32().unwrap() + 0.7152f32 * bgr[1].to_f32().unwrap()
- + 0.0722f32 * bgr[0].to_f32().unwrap();
- gray_a[0] = NumCast::from(l).unwrap();
+ gray_a[0] = bgr_to_luma(bgr);
gray_a[1] = T::max_value();
}
}
@@ -433,7 +564,28 @@ impl FromColor> for LumaA {
}
}
-/// `FromColor` for RGBA
+impl FromColor> for LumaA {
+ fn from_color(&mut self, other: &LumaA) {
+ let la8 = self.channels_mut();
+ let gray = other.channels()[0];
+ let alpha = other.channels()[1];
+ la8[0] = downcast_channel(gray);
+ la8[1] = downcast_channel(alpha);
+ }
+}
+
+impl FromColor> for LumaA {
+ fn from_color(&mut self, other: &LumaA) {
+ let la8 = self.channels_mut();
+ let gray = other.channels()[0];
+ let alpha = other.channels()[1];
+ la8[0] = upcast_channel(gray);
+ la8[1] = upcast_channel(alpha);
+ }
+}
+
+
+// `FromColor` for RGBA
impl FromColor> for Rgba {
fn from_color(&mut self, other: &Rgb) {
@@ -468,7 +620,6 @@ impl FromColor> for Rgba {
}
}
-
impl FromColor> for Rgba {
fn from_color(&mut self, other: &LumaA) {
let rgba = self.channels_mut();
@@ -480,8 +631,6 @@ impl FromColor> for Rgba {
}
}
-
-
impl FromColor> for Rgba {
fn from_color(&mut self, gray: &Luma) {
let rgba = self.channels_mut();
@@ -493,8 +642,30 @@ impl FromColor> for Rgba {
}
}
+impl FromColor> for Rgba {
+ fn from_color(&mut self, other: &Rgba) {
+ let rgba = self.channels_mut();
+ let rgba16 = other.channels();
+ rgba[0] = downcast_channel(rgba16[0]);
+ rgba[1] = downcast_channel(rgba16[1]);
+ rgba[2] = downcast_channel(rgba16[2]);
+ rgba[3] = downcast_channel(rgba16[3]);
+ }
+}
-/// `FromColor` for BGRA
+impl FromColor> for Rgba {
+ fn from_color(&mut self, other: &Rgba) {
+ let rgba = self.channels_mut();
+ let rgba8 = other.channels();
+ rgba[0] = upcast_channel(rgba8[0]);
+ rgba[1] = upcast_channel(rgba8[1]);
+ rgba[2] = upcast_channel(rgba8[2]);
+ rgba[3] = upcast_channel(rgba8[3]);
+ }
+}
+
+
+// `FromColor` for BGRA
impl FromColor> for Bgra {
fn from_color(&mut self, other: &Rgb) {
@@ -507,7 +678,6 @@ impl FromColor> for Bgra {
}
}
-
impl FromColor> for Bgra {
fn from_color(&mut self, other: &Bgr) {
let bgra = self.channels_mut();
@@ -519,7 +689,6 @@ impl FromColor> for Bgra {
}
}
-
impl FromColor> for Bgra {
fn from_color(&mut self, other: &Rgba) {
let bgra = self.channels_mut();
@@ -554,8 +723,7 @@ impl FromColor> for Bgra {
}
-
-/// `FromColor` for RGB
+// `FromColor` for RGB
impl FromColor> for Rgb {
fn from_color(&mut self, other: &Rgba) {
@@ -567,7 +735,6 @@ impl FromColor> for Rgb {
}
}
-
impl FromColor> for Rgb {
fn from_color(&mut self, other: &Bgra) {
let rgb = self.channels_mut();
@@ -608,6 +775,23 @@ impl FromColor> for Rgb {
}
}
+impl FromColor> for Rgb {
+ fn from_color(&mut self, other: &Rgb) {
+ for (c8, &c16) in self.channels_mut().iter_mut().zip(other.channels()) {
+ *c8 = downcast_channel(c16);
+ }
+ }
+}
+
+impl FromColor> for Rgb {
+ fn from_color(&mut self, other: &Rgb) {
+ for (c8, &c16) in self.channels_mut().iter_mut().zip(other.channels()) {
+ *c8 = upcast_channel(c16);
+ }
+ }
+}
+
+
/// `FromColor` for BGR
impl FromColor> for Bgr {
@@ -661,6 +845,43 @@ impl FromColor> for Bgr {
}
}
+macro_rules! downcast_bit_depth_early {
+ ($src:ident, $intermediate:ident, $dst:ident) => {
+ impl FromColor<$src> for $dst {
+ fn from_color(&mut self, other: &$src) {
+ let mut intermediate: $intermediate = $intermediate([Zero::zero(); <$intermediate as Pixel>::CHANNEL_COUNT as usize]);
+ intermediate.from_color(other);
+ self.from_color(&intermediate);
+ }
+ }
+ };
+}
+
+
+// Downcasts
+// LumaA
+downcast_bit_depth_early!(Luma, Luma, LumaA);
+downcast_bit_depth_early!(Rgb, Rgb, LumaA);
+downcast_bit_depth_early!(Rgba, Rgba, LumaA);
+// Rgb
+downcast_bit_depth_early!(Luma, Luma, Rgb);
+downcast_bit_depth_early!(LumaA, LumaA, Rgb);
+downcast_bit_depth_early!(Rgba, Rgba, Rgb);
+// Rgba
+downcast_bit_depth_early!(Luma, Luma, Rgba);
+downcast_bit_depth_early!(LumaA, LumaA, Rgba);
+downcast_bit_depth_early!(Rgb, Rgb, Rgba);
+// Bgr
+downcast_bit_depth_early!(Luma, Luma, Bgr);
+downcast_bit_depth_early!(LumaA, LumaA, Bgr);
+downcast_bit_depth_early!(Rgb, Rgb, Bgr);
+downcast_bit_depth_early!(Rgba, Rgba, Bgr);
+// Bgra
+downcast_bit_depth_early!(Luma, Luma, Bgra);
+downcast_bit_depth_early!(LumaA, LumaA, Bgra);
+downcast_bit_depth_early!(Rgb, Rgb, Bgra);
+downcast_bit_depth_early!(Rgba, Rgba, Bgra);
+
/// Blends a color inter another one
pub(crate) trait Blend {
@@ -912,7 +1133,7 @@ impl Invert for Bgr {
#[cfg(test)]
mod tests {
- use super::{LumaA, Pixel, Rgb, Rgba, Bgr, Bgra};
+ use super::{Luma, LumaA, Pixel, Rgb, Rgba, Bgr, Bgra};
#[test]
fn test_apply_with_alpha_rgba() {
@@ -1069,4 +1290,25 @@ mod tests {
let bgra = Bgra([0, 0, 0, 0]).map_without_alpha(|s| s + 1);
assert_eq!(bgra, Bgra([1, 1, 1, 0]));
}
+
+ macro_rules! test_lossless_conversion {
+ ($a:ty, $b:ty, $c:ty) => {
+ let a: $a = [<$a as Pixel>::Subpixel::max_value() >> 2; <$a as Pixel>::CHANNEL_COUNT as usize].into();
+ let b: $b = a.into_color();
+ let c: $c = b.into_color();
+ assert_eq!(a.channels(), c.channels());
+ };
+ }
+
+ #[test]
+ fn test_lossless_conversions() {
+ use super::IntoColor;
+
+ test_lossless_conversion!(Bgr, Rgba, Bgr);
+ test_lossless_conversion!(Bgra, Rgba, Bgra);
+ test_lossless_conversion!(Luma, Luma, Luma);
+ test_lossless_conversion!(LumaA, LumaA, LumaA);
+ test_lossless_conversion!(Rgb, Rgb, Rgb);
+ test_lossless_conversion!(Rgba, Rgba, Rgba);
+ }
}
diff --git a/src/dynimage.rs b/src/dynimage.rs
index 1985dfb4d3..5ea4f1f364 100644
--- a/src/dynimage.rs
+++ b/src/dynimage.rs
@@ -17,11 +17,11 @@ use png;
use pnm;
use buffer::{
- BgrImage, BgraImage, ConvertBuffer, GrayAlphaImage, GrayImage, ImageBuffer, Pixel, RgbImage,
- RgbaImage,
+ BgrImage, BgraImage, ConvertBuffer, GrayAlphaImage, GrayAlpha16Image,
+ GrayImage, Gray16Image, ImageBuffer, Pixel, RgbImage, Rgb16Image,
+ RgbaImage, Rgba16Image,
};
-use color;
-use flat::FlatSamples;
+use color::{self, IntoColor};
use image;
use image::{
GenericImage, GenericImageView, ImageDecoder, ImageError, ImageFormat, ImageOutputFormat, ImageResult,
@@ -49,6 +49,18 @@ pub enum DynamicImage {
/// Each pixel in this image is 8-bit Bgr with alpha
ImageBgra8(BgraImage),
+
+ /// Each pixel in this image is 16-bit Luma
+ ImageLuma16(Gray16Image),
+
+ /// Each pixel in this image is 16-bit Luma with alpha
+ ImageLumaA16(GrayAlpha16Image),
+
+ /// Each pixel in this image is 16-bit Rgb
+ ImageRgb16(Rgb16Image),
+
+ /// Each pixel in this image is 16-bit Rgb with alpha
+ ImageRgba16(Rgba16Image),
}
macro_rules! dynamic_map(
@@ -60,6 +72,10 @@ macro_rules! dynamic_map(
DynamicImage::ImageRgba8(ref $image) => DynamicImage::ImageRgba8($action),
DynamicImage::ImageBgr8(ref $image) => DynamicImage::ImageBgr8($action),
DynamicImage::ImageBgra8(ref $image) => DynamicImage::ImageBgra8($action),
+ DynamicImage::ImageLuma16(ref $image) => DynamicImage::ImageLuma16($action),
+ DynamicImage::ImageLumaA16(ref $image) => DynamicImage::ImageLumaA16($action),
+ DynamicImage::ImageRgb16(ref $image) => DynamicImage::ImageRgb16($action),
+ DynamicImage::ImageRgba16(ref $image) => DynamicImage::ImageRgba16($action),
}
);
@@ -71,6 +87,10 @@ macro_rules! dynamic_map(
DynamicImage::ImageRgba8(ref mut $image) => DynamicImage::ImageRgba8($action),
DynamicImage::ImageBgr8(ref mut $image) => DynamicImage::ImageBgr8($action),
DynamicImage::ImageBgra8(ref mut $image) => DynamicImage::ImageBgra8($action),
+ DynamicImage::ImageLuma16(ref mut $image) => DynamicImage::ImageLuma16($action),
+ DynamicImage::ImageLumaA16(ref mut $image) => DynamicImage::ImageLumaA16($action),
+ DynamicImage::ImageRgb16(ref mut $image) => DynamicImage::ImageRgb16($action),
+ DynamicImage::ImageRgba16(ref mut $image) => DynamicImage::ImageRgba16($action),
}
);
@@ -82,6 +102,10 @@ macro_rules! dynamic_map(
DynamicImage::ImageRgba8(ref $image) => $action,
DynamicImage::ImageBgr8(ref $image) => $action,
DynamicImage::ImageBgra8(ref $image) => $action,
+ DynamicImage::ImageLuma16(ref $image) => $action,
+ DynamicImage::ImageLumaA16(ref $image) => $action,
+ DynamicImage::ImageRgb16(ref $image) => $action,
+ DynamicImage::ImageRgba16(ref $image) => $action,
}
);
@@ -93,6 +117,10 @@ macro_rules! dynamic_map(
DynamicImage::ImageRgba8(ref mut $image) => $action,
DynamicImage::ImageBgr8(ref mut $image) => $action,
DynamicImage::ImageBgra8(ref mut $image) => $action,
+ DynamicImage::ImageLuma16(ref mut $image) => $action,
+ DynamicImage::ImageLumaA16(ref mut $image) => $action,
+ DynamicImage::ImageRgb16(ref mut $image) => $action,
+ DynamicImage::ImageRgba16(ref mut $image) => $action,
}
);
);
@@ -129,6 +157,27 @@ impl DynamicImage {
DynamicImage::ImageBgr8(ImageBuffer::new(w, h))
}
+ /// Creates a dynamic image backed by a buffer of grey pixels.
+ pub fn new_luma16(w: u32, h: u32) -> DynamicImage {
+ DynamicImage::ImageLuma16(ImageBuffer::new(w, h))
+ }
+
+ /// Creates a dynamic image backed by a buffer of grey
+ /// pixels with transparency.
+ pub fn new_luma_a16(w: u32, h: u32) -> DynamicImage {
+ DynamicImage::ImageLumaA16(ImageBuffer::new(w, h))
+ }
+
+ /// Creates a dynamic image backed by a buffer of RGB pixels.
+ pub fn new_rgb16(w: u32, h: u32) -> DynamicImage {
+ DynamicImage::ImageRgb16(ImageBuffer::new(w, h))
+ }
+
+ /// Creates a dynamic image backed by a buffer of RGBA pixels.
+ pub fn new_rgba16(w: u32, h: u32) -> DynamicImage {
+ DynamicImage::ImageRgba16(ImageBuffer::new(w, h))
+ }
+
/// Decodes an encoded image into a dynamic image.
pub fn from_decoder<'a>(decoder: impl ImageDecoder<'a>)
-> ImageResult
@@ -279,14 +328,73 @@ impl DynamicImage {
}
}
- /// Return this image's pixels as a byte vector.
- pub fn raw_pixels(&self) -> Vec {
- image_to_bytes(self)
+ /// Return a reference to an 16bit RGB image
+ pub fn as_rgb16(&self) -> Option<&Rgb16Image> {
+ match *self {
+ DynamicImage::ImageRgb16(ref p) => Some(p),
+ _ => None,
+ }
+ }
+
+ /// Return a mutable reference to an 16bit RGB image
+ pub fn as_mut_rgb16(&mut self) -> Option<&mut Rgb16Image> {
+ match *self {
+ DynamicImage::ImageRgb16(ref mut p) => Some(p),
+ _ => None,
+ }
+ }
+
+ /// Return a reference to an 16bit RGBA image
+ pub fn as_rgba16(&self) -> Option<&Rgba16Image> {
+ match *self {
+ DynamicImage::ImageRgba16(ref p) => Some(p),
+ _ => None,
+ }
+ }
+
+ /// Return a mutable reference to an 16bit RGBA image
+ pub fn as_mut_rgba16(&mut self) -> Option<&mut Rgba16Image> {
+ match *self {
+ DynamicImage::ImageRgba16(ref mut p) => Some(p),
+ _ => None,
+ }
+ }
+
+ /// Return a reference to an 16bit Grayscale image
+ pub fn as_luma16(&self) -> Option<&Gray16Image> {
+ match *self {
+ DynamicImage::ImageLuma16(ref p) => Some(p),
+ _ => None,
+ }
+ }
+
+ /// Return a mutable reference to an 16bit Grayscale image
+ pub fn as_mut_luma16(&mut self) -> Option<&mut Gray16Image> {
+ match *self {
+ DynamicImage::ImageLuma16(ref mut p) => Some(p),
+ _ => None,
+ }
+ }
+
+ /// Return a reference to an 16bit Grayscale image with an alpha channel
+ pub fn as_luma_alpha16(&self) -> Option<&GrayAlpha16Image> {
+ match *self {
+ DynamicImage::ImageLumaA16(ref p) => Some(p),
+ _ => None,
+ }
+ }
+
+ /// Return a mutable reference to an 16bit Grayscale image with an alpha channel
+ pub fn as_mut_luma_alpha16(&mut self) -> Option<&mut GrayAlpha16Image> {
+ match *self {
+ DynamicImage::ImageLumaA16(ref mut p) => Some(p),
+ _ => None,
+ }
}
- /// Return a view on the raw sample buffer.
- pub fn as_flat_samples(&self) -> FlatSamples<&[u8]> {
- dynamic_map!(*self, ref p -> p.as_flat_samples())
+ /// Return this image's pixels as a byte vector.
+ pub fn to_bytes(&self) -> Vec {
+ image_to_bytes(self)
}
/// Return this image's color type.
@@ -298,6 +406,10 @@ impl DynamicImage {
DynamicImage::ImageRgba8(_) => color::ColorType::Rgba8,
DynamicImage::ImageBgra8(_) => color::ColorType::Bgra8,
DynamicImage::ImageBgr8(_) => color::ColorType::Bgr8,
+ DynamicImage::ImageLuma16(_) => color::ColorType::L16,
+ DynamicImage::ImageLumaA16(_) => color::ColorType::La16,
+ DynamicImage::ImageRgb16(_) => color::ColorType::Rgb16,
+ DynamicImage::ImageRgba16(_) => color::ColorType::Rgba16,
}
}
@@ -310,6 +422,10 @@ impl DynamicImage {
DynamicImage::ImageRgba8(ref p) => DynamicImage::ImageLuma8(imageops::grayscale(p)),
DynamicImage::ImageBgr8(ref p) => DynamicImage::ImageLuma8(imageops::grayscale(p)),
DynamicImage::ImageBgra8(ref p) => DynamicImage::ImageLuma8(imageops::grayscale(p)),
+ DynamicImage::ImageLuma16(ref p) => DynamicImage::ImageLuma16(p.clone()),
+ DynamicImage::ImageLumaA16(ref p) => DynamicImage::ImageLuma16(imageops::grayscale(p)),
+ DynamicImage::ImageRgb16(ref p) => DynamicImage::ImageLuma16(imageops::grayscale(p)),
+ DynamicImage::ImageRgba16(ref p) => DynamicImage::ImageLuma16(imageops::grayscale(p)),
}
}
@@ -470,7 +586,7 @@ impl DynamicImage {
w: &mut W,
format: F,
) -> ImageResult<()> {
- let mut bytes = self.raw_pixels();
+ let mut bytes = self.to_bytes();
let (width, height) = self.dimensions();
let mut color = self.color();
let format = format.into();
@@ -593,7 +709,7 @@ impl GenericImageView for DynamicImage {
}
fn get_pixel(&self, x: u32, y: u32) -> color::Rgba {
- dynamic_map!(*self, ref p -> p.get_pixel(x, y).to_rgba())
+ dynamic_map!(*self, ref p -> p.get_pixel(x, y).to_rgba().into_color())
}
fn inner(&self) -> &Self::InnerImageView {
@@ -613,6 +729,10 @@ impl GenericImage for DynamicImage {
DynamicImage::ImageRgba8(ref mut p) => p.put_pixel(x, y, pixel),
DynamicImage::ImageBgr8(ref mut p) => p.put_pixel(x, y, pixel.to_bgr()),
DynamicImage::ImageBgra8(ref mut p) => p.put_pixel(x, y, pixel.to_bgra()),
+ DynamicImage::ImageLuma16(ref mut p) => p.put_pixel(x, y, pixel.to_luma().into_color()),
+ DynamicImage::ImageLumaA16(ref mut p) => p.put_pixel(x, y, pixel.to_luma_alpha().into_color()),
+ DynamicImage::ImageRgb16(ref mut p) => p.put_pixel(x, y, pixel.to_rgb().into_color()),
+ DynamicImage::ImageRgba16(ref mut p) => p.put_pixel(x, y, pixel.into_color()),
}
}
/// DEPRECATED: Use iterator `pixels_mut` to blend the pixels directly.
@@ -622,9 +742,12 @@ impl GenericImage for DynamicImage {
DynamicImage::ImageLumaA8(ref mut p) => p.blend_pixel(x, y, pixel.to_luma_alpha()),
DynamicImage::ImageRgb8(ref mut p) => p.blend_pixel(x, y, pixel.to_rgb()),
DynamicImage::ImageRgba8(ref mut p) => p.blend_pixel(x, y, pixel),
-
DynamicImage::ImageBgr8(ref mut p) => p.blend_pixel(x, y, pixel.to_bgr()),
DynamicImage::ImageBgra8(ref mut p) => p.blend_pixel(x, y, pixel.to_bgra()),
+ DynamicImage::ImageLuma16(ref mut p) => p.blend_pixel(x, y, pixel.to_luma().into_color()),
+ DynamicImage::ImageLumaA16(ref mut p) => p.blend_pixel(x, y, pixel.to_luma_alpha().into_color()),
+ DynamicImage::ImageRgb16(ref mut p) => p.blend_pixel(x, y, pixel.to_rgb().into_color()),
+ DynamicImage::ImageRgba16(ref mut p) => p.blend_pixel(x, y, pixel.into_color()),
}
}
@@ -642,32 +765,57 @@ impl GenericImage for DynamicImage {
fn decoder_to_image<'a, I: ImageDecoder<'a>>(decoder: I) -> ImageResult {
let (w, h) = decoder.dimensions();
let color_type = decoder.color_type();
- let buf = image::decoder_to_vec(decoder)?;
let image = match color_type {
color::ColorType::Rgb8 => {
+ let buf = image::decoder_to_vec(decoder)?;
ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgb8)
}
color::ColorType::Rgba8 => {
+ let buf = image::decoder_to_vec(decoder)?;
ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgba8)
}
color::ColorType::Bgr8 => {
+ let buf = image::decoder_to_vec(decoder)?;
ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageBgr8)
}
color::ColorType::Bgra8 => {
+ let buf = image::decoder_to_vec(decoder)?;
ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageBgra8)
}
color::ColorType::L8 => {
+ let buf = image::decoder_to_vec(decoder)?;
ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageLuma8)
}
color::ColorType::La8 => {
+ let buf = image::decoder_to_vec(decoder)?;
ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageLumaA8)
}
+
+ color::ColorType::Rgb16 => {
+ let buf = image::decoder_to_vec(decoder)?;
+ ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgb16)
+ }
+
+ color::ColorType::Rgba16 => {
+ let buf = image::decoder_to_vec(decoder)?;
+ ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageRgba16)
+ }
+
+ color::ColorType::L16 => {
+ let buf = image::decoder_to_vec(decoder)?;
+ ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageLuma16)
+ }
+
+ color::ColorType::La16 => {
+ let buf = image::decoder_to_vec(decoder)?;
+ ImageBuffer::from_raw(w, h, buf).map(DynamicImage::ImageLumaA16)
+ }
_ => return Err(ImageError::UnsupportedColor(color_type.into())),
};
match image {
@@ -678,6 +826,8 @@ fn decoder_to_image<'a, I: ImageDecoder<'a>>(decoder: I) -> ImageResult Vec {
+ use zerocopy::AsBytes;
+
match *image {
// TODO: consider transmuting
DynamicImage::ImageLuma8(ref a) => a.iter().cloned().collect(),
@@ -691,6 +841,14 @@ fn image_to_bytes(image: &DynamicImage) -> Vec {
DynamicImage::ImageBgr8(ref a) => a.iter().cloned().collect(),
DynamicImage::ImageBgra8(ref a) => a.iter().cloned().collect(),
+
+ DynamicImage::ImageLuma16(ref a) => a.as_bytes().to_vec(),
+
+ DynamicImage::ImageLumaA16(ref a) => a.as_bytes().to_vec(),
+
+ DynamicImage::ImageRgb16(ref a) => a.as_bytes().to_vec(),
+
+ DynamicImage::ImageRgba16(ref a) => a.as_bytes().to_vec(),
}
}
@@ -902,4 +1060,12 @@ mod test {
let dims = super::image_dimensions(im_path).unwrap();
assert_eq!(dims, (320, 240));
}
+
+ #[cfg(feature = "png")]
+ #[test]
+ fn open_16bpc_png() {
+ let im_path = "./tests/images/png/16bpc/basn6a16.png";
+ let image = super::open(im_path).unwrap();
+ assert_eq!(image.color(), super::color::ColorType::Rgba16);
+ }
}
diff --git a/src/flat.rs b/src/flat.rs
index 316c2564d8..994e5de0d3 100644
--- a/src/flat.rs
+++ b/src/flat.rs
@@ -560,7 +560,7 @@ impl FlatSamples {
where P: Pixel, Buffer: AsRef<[P::Subpixel]>,
{
if self.layout.channels != P::CHANNEL_COUNT {
- return Err(Error::WrongColor(P::COLOR_TYPE))
+ return Err(Error::WrongColor(P::color_type()))
}
let as_ref = self.samples.as_ref();
@@ -597,7 +597,7 @@ impl FlatSamples {
where P: Pixel, Buffer: AsMut<[P::Subpixel]>,
{
if self.layout.channels != P::CHANNEL_COUNT {
- return Err(Error::WrongColor(P::COLOR_TYPE))
+ return Err(Error::WrongColor(P::color_type()))
}
let as_mut = self.samples.as_mut();
@@ -634,7 +634,7 @@ impl FlatSamples {
}
if self.layout.channels != P::CHANNEL_COUNT {
- return Err(Error::WrongColor(P::COLOR_TYPE))
+ return Err(Error::WrongColor(P::color_type()))
}
let as_mut = self.samples.as_mut();
@@ -718,7 +718,7 @@ impl FlatSamples {
}
if self.layout.channels != P::CHANNEL_COUNT {
- return Err((Error::WrongColor(P::COLOR_TYPE), self))
+ return Err((Error::WrongColor(P::color_type()), self))
}
if !self.fits(self.samples.deref().len()) {
diff --git a/src/image.rs b/src/image.rs
index b22e5e17de..729dadf093 100644
--- a/src/image.rs
+++ b/src/image.rs
@@ -10,6 +10,7 @@ use std::ops::{Deref, DerefMut};
use buffer::{ImageBuffer, Pixel};
use color::{ColorType, ExtendedColorType};
+use zerocopy::{AsBytes, FromBytes};
use animation::Frames;
@@ -366,13 +367,16 @@ pub(crate) fn load_rect<'a, D, F, F1, F2, E>(x: u32, y: u32, width: u32, height:
Ok(seek_scanline(decoder, 0)?)
}
-/// Reads all of the bytes of a decoder into a Vec. No particular alignment
+/// Reads all of the bytes of a decoder into a Vec. No particular alignment
/// of the output buffer is guaranteed.
///
/// Panics if there isn't enough memory to decode the image.
-pub(crate) fn decoder_to_vec<'a>(decoder: impl ImageDecoder<'a>) -> ImageResult> {
- let mut buf = vec![0; usize::try_from(decoder.total_bytes()).unwrap()];
- decoder.read_image(&mut buf)?;
+pub(crate) fn decoder_to_vec<'a, T>(decoder: impl ImageDecoder<'a>) -> ImageResult>
+where
+ T: ::traits::Primitive + AsBytes + FromBytes,
+{
+ let mut buf = vec![num_traits::Zero::zero(); usize::try_from(decoder.total_bytes()).unwrap() / std::mem::size_of::()];
+ decoder.read_image(buf.as_bytes_mut())?;
Ok(buf)
}
diff --git a/src/lib.rs b/src/lib.rs
index 56590bc1a9..dc61e827ca 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -19,6 +19,7 @@ extern crate num_traits;
extern crate scoped_threadpool;
#[cfg(all(test, feature = "benchmarks"))]
extern crate test;
+extern crate zerocopy;
#[cfg(test)]
#[macro_use]
@@ -52,7 +53,12 @@ pub use buffer::{ConvertBuffer,
ImageBuffer,
Pixel,
RgbImage,
- RgbaImage};
+ RgbaImage,
+ Rgb16Image,
+ Rgba16Image,
+ Gray16Image,
+ GrayAlpha16Image,
+ };
pub use flat::FlatSamples;
diff --git a/src/png.rs b/src/png.rs
index 38ab88c2dc..b9d48a588f 100644
--- a/src/png.rs
+++ b/src/png.rs
@@ -101,7 +101,11 @@ impl PngDecoder {
let limits = png::Limits {
bytes: usize::max_value(),
};
- let decoder = png::Decoder::new_with_limits(r, limits);
+ let mut decoder = png::Decoder::new_with_limits(r, limits);
+ // By default the PNG decoder will scale 16 bpc to 8 bpc, so custom
+ // transformations must be set. EXPAND preserves the default behavior
+ // expanding bpc < 8 to 8 bpc.
+ decoder.set_transformations(png::Transformations::EXPAND);
let (_, mut reader) = decoder.read_info().map_err(ImageError::from_png)?;
let (color_type, bits) = reader.output_color_type();
let color_type = match (color_type, bits) {
@@ -165,8 +169,18 @@ impl<'a, R: 'a + Read> ImageDecoder<'a> for PngDecoder {
}
fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
+ use byteorder::{BigEndian, ByteOrder, NativeEndian};
+
assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
self.reader.next_frame(buf).map_err(ImageError::from_png)?;
+ // PNG images are big endian. For 16 bit per channel and larger types,
+ // the buffer may need to be reordered to native endianness per the
+ // contract of `read_image`.
+ match self.color_type().bytes_per_channel() {
+ 1 => (), // No reodering necessary for u8
+ 2 => buf.chunks_mut(2).for_each(|c| NativeEndian::write_u16(c, BigEndian::read_u16(c))),
+ _ => unreachable!(),
+ }
Ok(())
}
diff --git a/tests/images/png/16bpc/basn6a16.png b/tests/images/png/16bpc/basn6a16.png
new file mode 100644
index 0000000000..984a99525f
Binary files /dev/null and b/tests/images/png/16bpc/basn6a16.png differ
diff --git a/tests/images/tiff/testsuite/rgb-3c-16b.tiff b/tests/images/tiff/testsuite/rgb-3c-16b.tiff
new file mode 100644
index 0000000000..f1a0279a19
Binary files /dev/null and b/tests/images/tiff/testsuite/rgb-3c-16b.tiff differ
diff --git a/tests/reference/png/16bpc/basn6a16.png.285be560.png b/tests/reference/png/16bpc/basn6a16.png.285be560.png
new file mode 100644
index 0000000000..23de9945d7
Binary files /dev/null and b/tests/reference/png/16bpc/basn6a16.png.285be560.png differ
diff --git a/tests/reference/tiff/testsuite/rgb-3c-16b.tiff.6456fd3a.png b/tests/reference/tiff/testsuite/rgb-3c-16b.tiff.6456fd3a.png
new file mode 100644
index 0000000000..01223025d5
Binary files /dev/null and b/tests/reference/tiff/testsuite/rgb-3c-16b.tiff.6456fd3a.png differ