Import rgb 0.8.36 upstream upstream/0.8.36
authorDongHun Kwak <dh0128.kwak@samsung.com>
Thu, 6 Apr 2023 00:10:47 +0000 (09:10 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Thu, 6 Apr 2023 00:10:47 +0000 (09:10 +0900)
17 files changed:
.cargo_vcs_info.json [new file with mode: 0644]
Cargo.lock [new file with mode: 0644]
Cargo.toml [new file with mode: 0644]
Cargo.toml.orig [new file with mode: 0644]
LICENSE [new file with mode: 0644]
README.md [new file with mode: 0644]
examples/example.rs [new file with mode: 0644]
examples/serde.rs [new file with mode: 0644]
src/alt.rs [new file with mode: 0644]
src/internal/convert/array.rs [new file with mode: 0644]
src/internal/convert/mod.rs [new file with mode: 0644]
src/internal/convert/tuple.rs [new file with mode: 0644]
src/internal/ops.rs [new file with mode: 0644]
src/internal/pixel.rs [new file with mode: 0644]
src/internal/rgb.rs [new file with mode: 0644]
src/internal/rgba.rs [new file with mode: 0644]
src/lib.rs [new file with mode: 0644]

diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644 (file)
index 0000000..0462f4b
--- /dev/null
@@ -0,0 +1,6 @@
+{
+  "git": {
+    "sha1": "789553fdf3e9679ca82207c079cdfbd138fb886c"
+  },
+  "path_in_vcs": ""
+}
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644 (file)
index 0000000..e812772
--- /dev/null
@@ -0,0 +1,96 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "bytemuck"
+version = "1.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b"
+
+[[package]]
+name = "itoa"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
+dependencies = [
+ "unicode-xid",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rgb"
+version = "0.8.36"
+dependencies = [
+ "bytemuck",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c9613b5a66ab9ba26415184cfc41156594925a9cf3a2057e57f31ff145f6568"
+
+[[package]]
+name = "serde"
+version = "1.0.130"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.130"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.72"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0ffa0837f2dfa6fb90868c2b5468cad482e175f7dad97e7421951e663f2b527"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.82"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644 (file)
index 0000000..d0dba8c
--- /dev/null
@@ -0,0 +1,84 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+name = "rgb"
+version = "0.8.36"
+authors = ["Kornel Lesiński <kornel@geekhood.net>"]
+include = [
+    "src/**/*",
+    "Cargo.toml",
+    "README.md",
+    "examples/*.rs",
+    "LICENSE",
+]
+description = """
+`struct RGB/RGBA/etc.` for sharing pixels between crates + convenience methods for color manipulation.
+Allows no-copy high-level interoperability. Also adds common convenience methods and implements standard Rust traits to make `RGB`/`RGBA` pixels and slices first-class Rust objects."""
+homepage = "https://lib.rs/crates/rgb"
+documentation = "https://docs.rs/rgb"
+readme = "README.md"
+keywords = [
+    "rgb",
+    "rgba",
+    "bgra",
+    "pixel",
+    "color",
+]
+categories = [
+    "graphics",
+    "rust-patterns",
+    "multimedia::images",
+]
+license = "MIT"
+repository = "https://github.com/kornelski/rust-rgb"
+
+[package.metadata.docs.rs]
+targets = ["x86_64-unknown-linux-gnu"]
+features = [
+    "argb",
+    "serde",
+    "as-bytes",
+]
+
+[[example]]
+name = "serde"
+required-features = ["serde"]
+
+[[example]]
+name = "example"
+required-features = ["as-bytes"]
+
+[dependencies.bytemuck]
+version = "1.7.2"
+optional = true
+
+[dependencies.serde]
+version = "1.0.130"
+features = ["derive"]
+optional = true
+default-features = false
+
+[dev-dependencies.serde_json]
+version = "1.0.68"
+
+[features]
+argb = []
+as-bytes = ["bytemuck"]
+default = ["as-bytes"]
+grb = []
+
+[badges.maintenance]
+status = "actively-developed"
+
+[badges.travis-ci]
+repository = "kornelski/rust-rgb"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644 (file)
index 0000000..db5379a
--- /dev/null
@@ -0,0 +1,44 @@
+[package]
+name = "rgb"
+version = "0.8.36"
+authors = ["Kornel Lesiński <kornel@geekhood.net>"]
+include = ["src/**/*", "Cargo.toml", "README.md", "examples/*.rs", "LICENSE"]
+description = "`struct RGB/RGBA/etc.` for sharing pixels between crates + convenience methods for color manipulation.\nAllows no-copy high-level interoperability. Also adds common convenience methods and implements standard Rust traits to make `RGB`/`RGBA` pixels and slices first-class Rust objects."
+documentation = "https://docs.rs/rgb"
+repository = "https://github.com/kornelski/rust-rgb"
+homepage = "https://lib.rs/crates/rgb"
+readme = "README.md"
+keywords = ["rgb", "rgba", "bgra", "pixel", "color"]
+license = "MIT"
+categories = ["graphics", "rust-patterns", "multimedia::images"]
+edition = "2018"
+
+[features]
+default = ["as-bytes"]
+# safe as_bytes() casts require a marker trait for non-padded, non-pointery types.
+as-bytes = ["bytemuck"]
+argb = []
+grb = []
+
+[badges]
+travis-ci = { repository = "kornelski/rust-rgb" }
+maintenance = { status = "actively-developed" }
+
+[dependencies]
+serde = { version = "1.0.130", optional = true, default-features = false, features = ["derive"] }
+bytemuck = { version = "1.7.2", optional = true }
+
+[dev-dependencies]
+serde_json = "1.0.68"
+
+[[example]]
+name = "serde"
+required-features = ["serde"]
+
+[[example]]
+name = "example"
+required-features = ["as-bytes"]
+
+[package.metadata.docs.rs]
+targets = ["x86_64-unknown-linux-gnu"]
+features = [ "argb", "serde", "as-bytes" ]
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..aea3f4c
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 Kornel
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..cfb8ed8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,70 @@
+# `struct RGB` for [Rust](https://www.rust-lang.org)  [![crate](https://img.shields.io/crates/v/rgb.svg)](https://lib.rs/crates/rgb)
+
+Operating on pixels as weakly-typed vectors of `u8` is error-prone and inconvenient. It's better to use vectors of pixel structs. However, Rust is so strongly typed that *your* RGB pixel struct is not compatible with *my* RGB pixel struct. So let's all use mine :P
+
+[![xkcd standards](https://imgs.xkcd.com/comics/standards.png)](https://xkcd.com/927/)
+
+## Installation
+
+Add this to your `Cargo.toml`:
+
+```toml
+[dependencies]
+rgb = "0.8"
+```
+
+## Usage
+
+### `RGB` and `RGBA` structs
+
+The structs implement common Rust traits and a few convenience functions, e.g. `map` that repeats an operation on every subpixel:
+
+```rust
+use rgb::*; // Laziest way to use traits which add extra methods to the structs
+
+let px = RGB {
+    r:255_u8,
+    g:0,
+    b:255,
+};
+let inverted = px.map(|ch| 255 - ch);
+
+println!("{}", inverted); // Display: rgb(0,255,0)
+assert_eq!(RGB8::new(0, 255, 0), inverted);
+```
+
+### Byte slices to pixel slices
+
+For interoperability with functions operating on generic arrays of bytes there are functions for safe casting to and from pixel slices.
+
+```rust
+let raw = vec![0u8; width*height*3];
+let pixels: &[RGB8] = raw.as_rgb(); /// Safe casts without copying
+let raw_again = pixels.as_bytes();
+```
+
+Note: if you get an error about "no method named `as_bytes` found", add `use rgb::ComponentBytes`. If you're using a custom component type (`RGB<CustomType>`), implement `rgb::Pod` (plain old data) and `rgb::Zeroable` trait for the component (these traits are from [`bytemuck`](//lib.rs/bytemuck) crate).
+
+----
+
+## About colorspaces
+
+*Correct* color management is a complex problem, and this crate aims to be the lowest common denominator, so it's intentionally agnostic about it.
+
+However, this library supports any subpixel type for `RGB<T>`, and `RGBA<RGBType, AlphaType>`, so you can use them with a newtype, e.g.:
+
+```rust
+struct LinearLight(u16);
+type LinearRGB = RGB<LinearLight>;
+```
+
+
+### `BGRA`, `ARGB`, `Gray`, etc.
+
+There are other color types in `rgb::alt::*`. To enable `ARGB` and `ABGR`, use the "argb" feature:
+
+```toml
+rgb = { version = "0.8", features = ["argb"] }
+```
+
+There's also an optional `serde` feature that makes all types (de)serializable.
diff --git a/examples/example.rs b/examples/example.rs
new file mode 100644 (file)
index 0000000..a390b2f
--- /dev/null
@@ -0,0 +1,15 @@
+use rgb::*;
+
+fn main() {
+
+    let px = RGB{r:255_u8,g:0,b:100};
+    assert_eq!([px].as_bytes()[0], 255);
+
+    let bigpx = RGB16{r:65535_u16,g:0,b:0};
+    assert_eq!(bigpx.as_slice()[0], 65535);
+
+    let px = RGB8::new(255, 0, 255);
+    let inverted: RGB8 = px.map(|ch| 255 - ch);
+
+    println!("{}", inverted); // rgb(0,255,0)
+}
diff --git a/examples/serde.rs b/examples/serde.rs
new file mode 100644 (file)
index 0000000..2fb903a
--- /dev/null
@@ -0,0 +1,14 @@
+extern crate rgb;
+extern crate serde_json;
+
+use rgb::*;
+
+// Run using: cargo run --features=serde --example serde
+
+fn main() {
+    let color = RGB { r:255_u8, g:0, b:100 };
+    println!("{}", serde_json::to_string(&color).unwrap());
+
+    let color: RGB8 = serde_json::from_str("{\"r\":10,\"g\":20,\"b\":30}").unwrap();
+    println!("{}", color);
+}
diff --git a/src/alt.rs b/src/alt.rs
new file mode 100644 (file)
index 0000000..cd1043a
--- /dev/null
@@ -0,0 +1,386 @@
+use crate::internal::pixel::*;
+use core::ops;
+use core::slice;
+
+#[repr(C)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
+/// RGB in reverse byte order
+pub struct BGR<ComponentType> {
+    /// Blue first
+    pub b: ComponentType,
+    /// Green
+    pub g: ComponentType,
+    /// Red last
+    pub r: ComponentType,
+}
+
+#[repr(C)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
+/// BGR+A
+pub struct BGRA<ComponentType, AlphaComponentType = ComponentType> {
+    /// Blue first
+    pub b: ComponentType,
+    /// Green
+    pub g: ComponentType,
+    /// Red
+    pub r: ComponentType,
+    /// Alpha last
+    pub a: AlphaComponentType,
+}
+
+#[cfg(feature = "argb")]
+#[repr(C)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
+/// A+BGR
+pub struct ABGR<ComponentType, AlphaComponentType = ComponentType> {
+    /// Alpha first
+    pub a: AlphaComponentType,
+    /// Blue
+    pub b: ComponentType,
+    /// Green
+    pub g: ComponentType,
+    /// Red last
+    pub r: ComponentType,
+}
+
+#[cfg(feature = "argb")]
+#[repr(C)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
+/// A+RGB
+pub struct ARGB<ComponentType, AlphaComponentType = ComponentType> {
+    /// Alpha first
+    pub a: AlphaComponentType,
+    /// Red
+    pub r: ComponentType,
+    /// Green
+    pub g: ComponentType,
+    /// Blue last
+    pub b: ComponentType,
+}
+
+/// 8-bit BGR
+pub type BGR8 = BGR<u8>;
+
+/// 16-bit BGR in machine's native endian
+pub type BGR16 = BGR<u16>;
+
+/// 8-bit BGRA
+pub type BGRA8 = BGRA<u8>;
+
+/// 8-bit ABGR, alpha is first. 0 = transparent, 255 = opaque.
+#[cfg(feature = "argb")]
+pub type ABGR8 = ABGR<u8>;
+
+/// 8-bit ARGB, alpha is first. 0 = transparent, 255 = opaque.
+#[cfg(feature = "argb")]
+pub type ARGB8 = ARGB<u8>;
+
+/// 16-bit BGR in machine's native endian
+pub type BGRA16 = BGRA<u16>;
+
+/// 16-bit ABGR in machine's native endian. 0 = transparent, 65535 = opaque.
+#[cfg(feature = "argb")]
+pub type ABGR16 = ABGR<u16>;
+
+/// 16-bit ARGB in machine's native endian. 0 = transparent, 65535 = opaque.
+#[cfg(feature = "argb")]
+pub type ARGB16 = ARGB<u16>;
+
+#[cfg(feature = "grb")]
+#[repr(C)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
+/// RGB with red-green swapped (may be useful for LEDs)
+pub struct GRB<ComponentType> {
+    /// Green first
+    pub g: ComponentType,
+    /// Red
+    pub r: ComponentType,
+    /// Blue last
+    pub b: ComponentType,
+}
+
+/// 8-bit GRB
+#[cfg(feature = "grb")]
+pub type GRB8 = GRB<u8>;
+
+////////////////////////////////////////
+
+#[repr(C)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
+/// Grayscale. Use `.0` or `*` (deref) to access the value.
+pub struct Gray<ComponentType>(
+    /// brightness level
+    pub ComponentType,
+);
+
+#[repr(C)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
+/// Grayscale with alpha. Use `.0`/`.1` to access.
+pub struct GrayAlpha<ComponentType, AlphaComponentType = ComponentType>(
+    /// brightness level
+    pub ComponentType,
+    /// alpha
+    pub AlphaComponentType,
+);
+
+#[cfg(feature = "as-bytes")]
+unsafe impl<T> crate::Pod for Gray<T> where T: crate::Pod {}
+#[cfg(feature = "as-bytes")]
+unsafe impl<T, A> crate::Pod for GrayAlpha<T, A> where T: crate::Pod, A: crate::Pod {}
+#[cfg(feature = "as-bytes")]
+unsafe impl<T> crate::Zeroable for Gray<T> where T: crate::Zeroable {}
+#[cfg(feature = "as-bytes")]
+unsafe impl<T, A> crate::Zeroable for GrayAlpha<T, A> where T: crate::Zeroable, A: crate::Zeroable {}
+
+/// 8-bit gray
+pub type GRAY8 = Gray<u8>;
+
+/// 16-bit gray in machine's native endian
+pub type GRAY16 = Gray<u16>;
+
+/// 8-bit gray with alpha in machine's native endian
+pub type GRAYA8 = GrayAlpha<u8>;
+
+/// 16-bit gray with alpha in machine's native endian
+pub type GRAYA16 = GrayAlpha<u16>;
+
+impl<T> Gray<T> {
+    /// New grayscale pixel
+    #[inline(always)]
+    pub const fn new(brightness: T) -> Self {
+        Self(brightness)
+    }
+}
+
+impl<T> ops::Deref for Gray<T> {
+    type Target = T;
+    #[inline(always)]
+    fn deref(&self) -> &T {
+        &self.0
+    }
+}
+
+impl<T: Copy> From<T> for Gray<T> {
+    #[inline(always)]
+    fn from(component: T) -> Self {
+        Gray(component)
+    }
+}
+
+impl<T: Clone, A> GrayAlpha<T, A> {
+    /// Copy `Gray` component out of the `GrayAlpha` struct
+    #[inline(always)]
+    pub fn gray(&self) -> Gray<T> {
+        Gray(self.0.clone())
+    }
+}
+
+impl<T, A> GrayAlpha<T, A> {
+    /// New grayscale+alpha pixel
+    #[inline(always)]
+    pub const fn new(brightness: T, alpha: A) -> Self {
+        Self(brightness, alpha)
+    }
+
+    /// Provide a mutable view of only `Gray` component (leaving out alpha).
+    #[inline(always)]
+    pub fn gray_mut(&mut self) -> &mut Gray<T> {
+        unsafe {
+            &mut *(self as *mut _ as *mut _)
+        }
+    }
+}
+
+impl<T: Copy, A: Clone> GrayAlpha<T, A> {
+    /// Create a new `GrayAlpha` with the new alpha value, but same gray value
+    #[inline(always)]
+    pub fn alpha(&self, a: A) -> Self {
+        Self(self.0, a)
+    }
+
+    /// Create a new `GrayAlpha` with a new alpha value created by the callback.
+    #[inline(always)]
+    pub fn map_alpha<F, B>(&self, f: F) -> GrayAlpha<T, B>
+        where F: FnOnce(A) -> B
+    {
+        GrayAlpha (self.0, f(self.1.clone()))
+    }
+
+    /// Create new `GrayAlpha` with the same alpha value, but different `Gray` value
+    #[inline(always)]
+    pub fn map_gray<F, U, B>(&self, f: F) -> GrayAlpha<U, B>
+        where F: FnOnce(T) -> U, U: Clone, B: From<A> + Clone {
+        GrayAlpha(f(self.0), self.1.clone().into())
+    }
+}
+
+impl<T: Copy, B> ComponentMap<Gray<B>, T, B> for Gray<T> {
+    #[inline(always)]
+    fn map<F>(&self, mut f: F) -> Gray<B> where F: FnMut(T) -> B {
+        Gray(f(self.0))
+    }
+}
+
+impl<T: Copy, B> ColorComponentMap<Gray<B>, T, B> for Gray<T> {
+    #[inline(always)]
+    fn map_c<F>(&self, mut f: F) -> Gray<B> where F: FnMut(T) -> B {
+        Gray(f(self.0))
+    }
+}
+
+impl<T: Copy, B> ComponentMap<GrayAlpha<B>, T, B> for GrayAlpha<T> {
+    #[inline(always)]
+    fn map<F>(&self, mut f: F) -> GrayAlpha<B>
+    where
+        F: FnMut(T) -> B,
+    {
+        GrayAlpha(f(self.0), f(self.1))
+    }
+}
+
+impl<T: Copy, A: Copy, B> ColorComponentMap<GrayAlpha<B, A>, T, B> for GrayAlpha<T, A> {
+    #[inline(always)]
+    fn map_c<F>(&self, mut f: F) -> GrayAlpha<B, A>
+    where
+        F: FnMut(T) -> B,
+    {
+        GrayAlpha(f(self.0), self.1)
+    }
+}
+
+impl<T> ComponentSlice<T> for GrayAlpha<T> {
+    #[inline(always)]
+    fn as_slice(&self) -> &[T] {
+        unsafe {
+            slice::from_raw_parts(self as *const Self as *const T, 2)
+        }
+    }
+
+    #[inline(always)]
+    fn as_mut_slice(&mut self) -> &mut [T] {
+        unsafe {
+            slice::from_raw_parts_mut(self as *mut Self as *mut T, 2)
+        }
+    }
+}
+
+impl<T> ComponentSlice<T> for [GrayAlpha<T>] {
+    #[inline]
+    fn as_slice(&self) -> &[T] {
+        unsafe {
+            slice::from_raw_parts(self.as_ptr() as *const _, self.len() * 2)
+        }
+    }
+
+    #[inline]
+    fn as_mut_slice(&mut self) -> &mut [T] {
+        unsafe {
+            slice::from_raw_parts_mut(self.as_ptr() as *mut _, self.len() * 2)
+        }
+    }
+}
+
+#[cfg(feature = "as-bytes")]
+impl<T: crate::Pod> ComponentBytes<T> for [GrayAlpha<T>] {}
+
+impl<T> ComponentSlice<T> for Gray<T> {
+    #[inline(always)]
+    fn as_slice(&self) -> &[T] {
+        slice::from_ref(&self.0)
+    }
+
+    #[inline(always)]
+    fn as_mut_slice(&mut self) -> &mut [T] {
+        slice::from_mut(&mut self.0)
+    }
+}
+
+impl<T> ComponentSlice<T> for [Gray<T>] {
+    #[inline]
+    fn as_slice(&self) -> &[T] {
+        unsafe {
+            slice::from_raw_parts(self.as_ptr() as *const _, self.len())
+        }
+    }
+
+    #[inline]
+    fn as_mut_slice(&mut self) -> &mut [T] {
+        unsafe {
+            slice::from_raw_parts_mut(self.as_ptr() as *mut _, self.len())
+        }
+    }
+}
+
+#[cfg(feature = "as-bytes")]
+impl<T: crate::Pod> ComponentBytes<T> for [Gray<T>] {}
+
+/// Assumes 255 is opaque
+impl<T: Copy> From<Gray<T>> for GrayAlpha<T, u8> {
+    #[inline(always)]
+    fn from(other: Gray<T>) -> Self {
+        GrayAlpha(other.0, 0xFF)
+    }
+}
+
+/// Assumes 65535 is opaque
+impl<T: Copy> From<Gray<T>> for GrayAlpha<T, u16> {
+    #[inline(always)]
+    fn from(other: Gray<T>) -> Self {
+        GrayAlpha(other.0, 0xFFFF)
+    }
+}
+
+#[test]
+fn gray() {
+    let rgb: crate::RGB<_> = Gray(1).into();
+    assert_eq!(rgb.r, 1);
+    assert_eq!(rgb.g, 1);
+    assert_eq!(rgb.b, 1);
+
+    let rgba: crate::RGBA<_> = Gray(1u8).into();
+    assert_eq!(rgba.r, 1);
+    assert_eq!(rgba.g, 1);
+    assert_eq!(rgba.b, 1);
+    assert_eq!(rgba.a, 255);
+
+    let g: GRAY8 = 200.into();
+    let g = g.map(|c| c/2);
+    assert_eq!(110, *g + 10);
+    assert_eq!(110, 10 + Gray(100).as_ref());
+
+    let ga: GRAYA8 = GrayAlpha(1, 2);
+    assert_eq!(ga.gray(), Gray::new(1));
+    let mut g2 = ga.clone();
+    *g2.gray_mut() = Gray(3);
+    assert_eq!(g2.map_gray(|g| g+1), GRAYA8::new(4, 2));
+    assert_eq!(g2.map(|g| g+1), GrayAlpha(4, 3));
+    assert_eq!(g2.0, 3);
+    assert_eq!(g2.as_slice(), &[3, 2]);
+    assert_eq!(g2.as_mut_slice(), &[3, 2]);
+    assert_eq!(g2.alpha(13), GrayAlpha(3, 13));
+    assert_eq!(g2.map_alpha(|x| x+3), GrayAlpha(3, 5));
+
+    assert_eq!((&[Gray(1u16), Gray(2)][..]).as_slice(), &[1, 2]);
+    assert_eq!((&[GrayAlpha(1u16, 2), GrayAlpha(3, 4)][..]).as_slice(), &[1, 2, 3, 4]);
+
+    let rgba: crate::RGBA<_> = ga.into();
+    assert_eq!(rgba.r, 1);
+    assert_eq!(rgba.g, 1);
+    assert_eq!(rgba.b, 1);
+    assert_eq!(rgba.a, 2);
+
+    let ga: GRAYA16 = GrayAlpha(1,2);
+    let rgba: crate::RGBA<u16, u16> = ga.into();
+    assert_eq!(rgba.r, 1);
+    assert_eq!(rgba.g, 1);
+    assert_eq!(rgba.b, 1);
+    assert_eq!(rgba.a, 2);
+}
+
diff --git a/src/internal/convert/array.rs b/src/internal/convert/array.rs
new file mode 100644 (file)
index 0000000..9fbc5a5
--- /dev/null
@@ -0,0 +1,115 @@
+#[cfg(feature = "argb")]
+use crate::alt::{ARGB};
+use crate::alt::{BGR, BGRA};
+use crate::{RGB, RGBA};
+
+impl<T: Copy> From<[T; 3]> for RGB<T> {
+    #[inline(always)]
+    fn from(other: [T; 3]) -> Self {
+        Self {
+            r: other[0],
+            g: other[1],
+            b: other[2],
+        }
+    }
+}
+
+impl<T> Into<[T; 3]> for RGB<T> {
+    #[inline(always)]
+    fn into(self) -> [T; 3] {
+        [self.r, self.g, self.b]
+    }
+}
+
+impl<T: Copy> From<[T; 4]> for RGBA<T> {
+    #[inline(always)]
+    fn from(other: [T; 4]) -> Self {
+        Self {
+            r: other[0],
+            g: other[1],
+            b: other[2],
+            a: other[3],
+        }
+    }
+}
+
+impl<T> Into<[T; 4]> for RGBA<T> {
+    #[inline(always)]
+    fn into(self) -> [T; 4] {
+        [self.r, self.g, self.b, self.a]
+    }
+}
+
+#[cfg(feature = "argb")]
+impl<T: Copy> From<[T; 4]> for ARGB<T> {
+    #[inline(always)]
+    fn from(other: [T; 4]) -> Self {
+        Self {
+            a: other[0],
+            r: other[1],
+            g: other[2],
+            b: other[3],
+        }
+    }
+}
+
+#[cfg(feature = "argb")]
+impl<T> Into<[T; 4]> for ARGB<T> {
+    #[inline(always)]
+    fn into(self) -> [T; 4] {
+        [self.a, self.r, self.g, self.b]
+    }
+}
+
+impl<T: Copy> From<[T; 3]> for BGR<T> {
+    #[inline(always)]
+    fn from(other: [T; 3]) -> Self {
+        Self {
+            b: other[0],
+            g: other[1],
+            r: other[2],
+        }
+    }
+}
+
+impl<T> Into<[T; 3]> for BGR<T> {
+    #[inline(always)]
+    fn into(self) -> [T; 3] {
+        [self.b, self.g, self.r]
+    }
+}
+
+impl<T: Copy> From<[T; 4]> for BGRA<T> {
+    #[inline(always)]
+    fn from(other: [T; 4]) -> Self {
+        Self {
+            b: other[0],
+            g: other[1],
+            r: other[2],
+            a: other[3],
+        }
+    }
+}
+
+impl<T> Into<[T; 4]> for BGRA<T> {
+    #[inline(always)]
+    fn into(self) -> [T; 4] {
+        [self.b, self.g, self.r, self.a]
+    }
+}
+
+#[test]
+#[allow(deprecated)]
+fn convert_array() {
+    use crate::alt::{BGR8, BGRA8};
+    use crate::{RGB8, RGBA8};
+
+    assert_eq!(RGB8::from([1, 2, 3]), RGB8::new(1, 2, 3));
+    assert_eq!(Into::<[u8; 3]>::into(RGB8::new(1, 2, 3)), [1, 2, 3]);
+    assert_eq!(RGBA8::from([1, 2, 3, 4]), RGBA8::new(1, 2, 3, 4));
+    assert_eq!(Into::<[u8; 4]>::into(RGBA8::new(1, 2, 3, 4)), [1, 2, 3, 4]);
+    assert_eq!(BGR8::from([3, 2, 1]), BGR8::new(1, 2, 3));
+    assert_eq!(Into::<[u8; 3]>::into(BGR8::new(1, 2, 3)), [3, 2, 1]);
+    assert_eq!(BGRA8::from([3, 2, 1, 4]), BGRA8::new(1, 2, 3, 4));
+    assert_eq!(Into::<[u8; 4]>::into(BGRA8::new(1, 2, 3, 4)), [3, 2, 1, 4]);
+}
diff --git a/src/internal/convert/mod.rs b/src/internal/convert/mod.rs
new file mode 100644 (file)
index 0000000..2dc8af8
--- /dev/null
@@ -0,0 +1,431 @@
+use super::pixel::*;
+use crate::alt::*;
+use crate::RGB;
+use crate::RGBA;
+use core::convert::*;
+use core::mem;
+use core::slice;
+
+mod array;
+mod tuple;
+
+/// Casts a slice of bytes into a slice of pixels, e.g. `[u8]` to `[RGB8]`.
+///
+/// See also `FromSlice`
+pub trait AsPixels<PixelType> {
+    /// Reinterpret the slice as a read-only/shared slice of pixels.
+    /// Multiple consecutive elements in the slice are intepreted as a single pixel
+    /// (depending on format, e.g. 3 for RGB, 4 for RGBA).
+    ///
+    /// Leftover elements are ignored if the slice isn't evenly divisible into pixels.
+    ///
+    /// Use this method only when the type is known from context.
+    /// See also `FromSlice`.
+    fn as_pixels(&self) -> &[PixelType];
+    /// Reinterpret the slice as a mutable/exclusive slice of pixels.
+    /// Multiple consecutive elements in the slice are intepreted as a single pixel
+    /// (depending on format, e.g. 3 for RGB, 4 for RGBA).
+    ///
+    /// Leftover elements are ignored if the slice isn't evenly divisible into pixels.
+    ///
+    /// Use this method only when the type is known from context.
+    /// See also `FromSlice`.
+    fn as_pixels_mut(&mut self) -> &mut [PixelType];
+}
+
+macro_rules! as_pixels_impl {
+    ($typ:ident, $elems:expr) => {
+        impl<T> AsPixels<$typ<T>> for [T] {
+            fn as_pixels(&self) -> &[$typ<T>] {
+                unsafe {
+                    slice::from_raw_parts(self.as_ptr() as *const _, self.len() / $elems)
+                }
+            }
+            fn as_pixels_mut(&mut self) -> &mut [$typ<T>] {
+                unsafe {
+                    slice::from_raw_parts_mut(self.as_mut_ptr() as *mut _, self.len() / $elems)
+                }
+            }
+        }
+    }
+}
+
+as_pixels_impl!{RGB, 3}
+as_pixels_impl!{RGBA, 4}
+as_pixels_impl!{BGR, 3}
+as_pixels_impl!{BGRA, 4}
+#[cfg(feature = "grb")]
+as_pixels_impl!{GRB, 3}
+as_pixels_impl!{Gray, 1}
+as_pixels_impl!{GrayAlpha, 2}
+#[cfg(feature = "argb")]
+as_pixels_impl!{ARGB, 4}
+#[cfg(feature = "argb")]
+as_pixels_impl!{ABGR, 4}
+
+/// Cast a slice of component values (bytes) as a slice of RGB/RGBA pixels
+///
+/// If there's any incomplete pixel at the end of the slice it is ignored.
+pub trait FromSlice<T: Copy> {
+    /// Reinterpert slice as RGB pixels
+    fn as_rgb(&self) -> &[RGB<T>];
+    /// Reinterpert slice as RGBA pixels
+    fn as_rgba(&self) -> &[RGBA<T>];
+    /// Reinterpert slice as alpha-first ARGB pixels
+    #[cfg(feature = "argb")]
+    fn as_argb(&self) -> &[ARGB<T>];
+    /// Reinterpert mutable slice as RGB pixels
+    fn as_rgb_mut(&mut self) -> &mut [RGB<T>];
+    /// Reinterpert mutable slice as RGBA pixels
+    fn as_rgba_mut(&mut self) -> &mut [RGBA<T>];
+    /// Reinterpert mutable slice as alpha-first ARGB pixels
+    #[cfg(feature = "argb")]
+    fn as_argb_mut(&mut self) -> &mut [ARGB<T>];
+
+    /// Reinterpert mutable slice as grayscale pixels
+    fn as_gray(&self) -> &[Gray<T>];
+    /// Reinterpert mutable slice as grayscale pixels with alpha
+    fn as_gray_alpha(&self) -> &[GrayAlpha<T>];
+    /// Reinterpert mutable slice as grayscale pixels
+    fn as_gray_mut(&mut self) -> &mut [Gray<T>];
+    /// Reinterpert mutable slice as grayscale pixels with alpha
+    fn as_gray_alpha_mut(&mut self) -> &mut [GrayAlpha<T>];
+
+    /// Reinterpert slice as reverse-order BGR pixels
+    fn as_bgr(&self) -> &[BGR<T>];
+    /// Reinterpert slice as reverse-order BGRA pixels
+    fn as_bgra(&self) -> &[BGRA<T>];
+    /// Reinterpert slice as reverse-order ABGR pixels
+    #[cfg(feature = "argb")]
+    fn as_abgr(&self) -> &[ABGR<T>];
+    /// Reinterpert ntable slice as reverse-order BGR pixels
+    fn as_bgr_mut(&mut self) -> &mut [BGR<T>];
+    /// Reinterpert mutable slice as reverse-order alpha-last BGRA pixels
+    fn as_bgra_mut(&mut self) -> &mut [BGRA<T>];
+    /// Reinterpert mutable slice as reverse-order alpha-first ABGR pixels
+    #[cfg(feature = "argb")]
+    fn as_abgr_mut(&mut self) -> &mut [ABGR<T>];
+}
+
+impl<T: Copy> FromSlice<T> for [T] {
+    #[inline]
+    fn as_rgb(&self) -> &[RGB<T>] {
+        unsafe { from_items_to_struct(self) }
+    }
+
+    #[inline]
+    fn as_rgba(&self) -> &[RGBA<T>] {
+        unsafe { from_items_to_struct(self) }
+    }
+
+    #[inline]
+    #[cfg(feature = "argb")]
+    fn as_argb(&self) -> &[ARGB<T>] {
+        unsafe { from_items_to_struct(self) }
+    }
+
+    #[inline]
+    fn as_rgb_mut(&mut self) -> &mut [RGB<T>] {
+        unsafe { from_items_to_struct_mut(self) }
+    }
+
+    #[inline]
+    fn as_rgba_mut(&mut self) -> &mut [RGBA<T>] {
+        unsafe { from_items_to_struct_mut(self) }
+    }
+
+    #[inline]
+    #[cfg(feature = "argb")]
+    fn as_argb_mut(&mut self) -> &mut [ARGB<T>] {
+        unsafe { from_items_to_struct_mut(self) }
+    }
+
+    #[inline]
+    fn as_gray(&self) -> &[Gray<T>] {
+        unsafe { from_items_to_struct(self) }
+    }
+
+    #[inline]
+    fn as_gray_alpha(&self) -> &[GrayAlpha<T>] {
+        unsafe { from_items_to_struct(self) }
+    }
+
+    #[inline]
+    fn as_gray_mut(&mut self) -> &mut [Gray<T>] {
+        unsafe { from_items_to_struct_mut(self) }
+    }
+
+    #[inline]
+    fn as_gray_alpha_mut(&mut self) -> &mut [GrayAlpha<T>] {
+        unsafe { from_items_to_struct_mut(self) }
+    }
+
+
+    #[inline]
+    fn as_bgr(&self) -> &[BGR<T>] {
+        unsafe { from_items_to_struct(self) }
+    }
+
+    #[inline]
+    #[cfg(feature = "argb")]
+    fn as_abgr(&self) -> &[ABGR<T>] {
+        unsafe { from_items_to_struct(self) }
+    }
+
+    #[inline]
+    fn as_bgra(&self) -> &[BGRA<T>] {
+        unsafe { from_items_to_struct(self) }
+    }
+
+    #[inline]
+    fn as_bgr_mut(&mut self) -> &mut [BGR<T>] {
+        unsafe { from_items_to_struct_mut(self) }
+    }
+
+    #[inline]
+    fn as_bgra_mut(&mut self) -> &mut [BGRA<T>] {
+        unsafe { from_items_to_struct_mut(self) }
+    }
+
+    #[inline]
+    #[cfg(feature = "argb")]
+    fn as_abgr_mut(&mut self) -> &mut [ABGR<T>] {
+        unsafe { from_items_to_struct_mut(self) }
+    }
+}
+
+#[inline(always)]
+unsafe fn from_items_to_struct<F, T>(from: &[F]) -> &[T] {
+    debug_assert_eq!(0, mem::size_of::<T>() % mem::size_of::<F>());
+    let len = from.len() / (mem::size_of::<T>() / mem::size_of::<F>());
+    slice::from_raw_parts(from.as_ptr() as *const T, len)
+}
+
+#[inline(always)]
+unsafe fn from_items_to_struct_mut<F, T>(from: &mut [F]) -> &mut [T] {
+    debug_assert_eq!(0, mem::size_of::<T>() % mem::size_of::<F>());
+    let len = from.len() / (mem::size_of::<T>() / mem::size_of::<F>());
+    slice::from_raw_parts_mut(from.as_mut_ptr() as *mut T, len)
+}
+
+macro_rules! rgb_impl_from {
+    ($typename:ident, $from:ty, $to:ty) => {
+        impl From<$typename<$from>> for $typename<$to> {
+
+            #[inline(always)]
+            fn from(other: $typename<$from>) -> Self {
+                other.map(core::convert::Into::into)
+            }
+        }
+    }
+}
+
+rgb_impl_from!{RGB, u8,i16}
+rgb_impl_from!{RGB, u8,u16}
+rgb_impl_from!{RGB, u8,u32}
+rgb_impl_from!{RGB, u16,i32}
+rgb_impl_from!{RGB, u16,u32}
+rgb_impl_from!{RGB, u16,u64}
+
+rgb_impl_from!{RGB, u8,f32}
+rgb_impl_from!{RGB, u8,f64}
+rgb_impl_from!{RGB, u16,f32}
+rgb_impl_from!{RGB, u16,f64}
+
+rgb_impl_from!{RGB, i16,f32}
+rgb_impl_from!{RGB, i16,f64}
+
+rgb_impl_from!{RGB, i32,f64}
+rgb_impl_from!{RGB, f32,f64}
+
+rgb_impl_from!{RGBA, u16,i32}
+rgb_impl_from!{RGBA, u16,u32}
+rgb_impl_from!{RGBA, u16,u64}
+
+rgb_impl_from!{RGBA, u8,i16}
+rgb_impl_from!{RGBA, u8,u16}
+rgb_impl_from!{RGBA, u8,u32}
+rgb_impl_from!{RGBA, u8,f32}
+rgb_impl_from!{RGBA, u8,f64}
+rgb_impl_from!{RGBA, u16,f32}
+rgb_impl_from!{RGBA, u16,f64}
+
+rgb_impl_from!{RGBA, i16,f32}
+rgb_impl_from!{RGBA, i16,f64}
+
+rgb_impl_from!{RGBA, i32,f64}
+rgb_impl_from!{RGBA, f32,f64}
+
+macro_rules! reorder_impl_from {
+    (@rgb $t1:ident, $t2:ident) => {
+        reorder_impl_from!(@once $t1, $t2, r, g, b);
+        reorder_impl_from!(@once $t2, $t1, r, g, b);
+    };
+    (@rgba $t1:ident, $t2:ident) => {
+        reorder_impl_from!(@once $t1, $t2, r, g, b, a);
+        reorder_impl_from!(@once $t2, $t1, r, g, b, a);
+    };
+    (@once $t1:ident, $t2:ident, $($component:ident),+) => {
+        impl<T> From<$t1<T>> for $t2<T> where T: ::core::clone::Clone {
+            fn from(other: $t1<T>) -> Self {
+                let $t1 { $($component),+ } = other;
+                Self {
+                    $($component),+
+                }
+            }
+        }
+    }
+}
+
+#[cfg(feature = "argb")]
+reorder_impl_from!(@rgba RGBA, ARGB);
+#[cfg(feature = "argb")]
+reorder_impl_from!(@rgba ABGR, ARGB);
+#[cfg(feature = "argb")]
+reorder_impl_from!(@rgba BGRA, ARGB);
+#[cfg(feature = "argb")]
+reorder_impl_from!(@rgba BGRA, ABGR);
+
+reorder_impl_from!(@rgb RGB, BGR);
+reorder_impl_from!(@rgba BGRA, RGBA);
+#[cfg(feature = "argb")]
+reorder_impl_from!(@rgba ABGR, RGBA);
+#[cfg(feature = "grb")]
+reorder_impl_from!(@rgb RGB, GRB);
+
+impl<T: Clone> From<Gray<T>> for RGB<T> {
+    #[inline(always)]
+    fn from(other: Gray<T>) -> Self {
+        Self {
+            r: other.0.clone(),
+            g: other.0.clone(),
+            b: other.0,
+        }
+    }
+}
+
+impl<T: Clone> From<Gray<T>> for RGBA<T, u8> {
+    #[inline(always)]
+    fn from(other: Gray<T>) -> Self {
+        Self {
+            r: other.0.clone(),
+            g: other.0.clone(),
+            b: other.0,
+            a: 255,
+        }
+    }
+}
+
+impl<T: Clone,A> From<GrayAlpha<T,A>> for RGBA<T,A> {
+    #[inline(always)]
+    fn from(other: GrayAlpha<T,A>) -> Self {
+        Self {
+            r: other.0.clone(),
+            g: other.0.clone(),
+            b: other.0,
+            a: other.1,
+        }
+    }
+}
+
+impl<T> AsRef<T> for Gray<T> {
+    #[inline(always)]
+    fn as_ref(&self) -> &T {
+        &self.0
+    }
+}
+
+impl<T> AsRef<[T]> for RGB<T> {
+    #[inline(always)]
+    fn as_ref(&self) -> &[T] {
+        self.as_slice()
+    }
+}
+
+impl<T> AsRef<[T]> for RGBA<T> {
+    #[inline(always)]
+    fn as_ref(&self) -> &[T] {
+        self.as_slice()
+    }
+}
+
+impl<T> AsRef<T> for GrayAlpha<T> {
+    #[inline(always)]
+    fn as_ref(&self) -> &T {
+        &self.0
+    }
+}
+
+
+impl<T> AsMut<T> for Gray<T> {
+    #[inline(always)]
+    fn as_mut(&mut self) -> &mut T {
+        &mut self.0
+    }
+}
+
+impl<T> AsMut<[T]> for RGB<T> {
+    #[inline(always)]
+    fn as_mut(&mut self) -> &mut [T] {
+        self.as_mut_slice()
+    }
+}
+
+impl<T> AsMut<[T]> for RGBA<T> {
+    #[inline(always)]
+    fn as_mut(&mut self) -> &mut [T] {
+        self.as_mut_slice()
+    }
+}
+
+impl<T> AsMut<T> for GrayAlpha<T> {
+    #[inline(always)]
+    fn as_mut(&mut self) -> &mut T {
+        &mut self.0
+    }
+}
+
+#[cfg(feature = "argb")]
+#[test]
+fn argb_converts() {
+    let argb = ARGB {a: 0xffu8, r: 0xff, g: 0xff, b: 0xff};
+    let rgba = RGBA {a: 0xffu8, r: 0xff, g: 0xff, b: 0xff};
+
+    assert_eq!(RGBA::from(argb), rgba);
+    assert_eq!(ARGB::from(rgba), argb);
+
+    let bgra = BGRA {a: 0xffu8, r: 0xff, g: 0xff, b: 0xff};
+    let abgr = ABGR {a: 0xffu8, r: 0xff, g: 0xff, b: 0xff};
+
+    assert_eq!(BGRA::from(abgr), bgra);
+    assert_eq!(ABGR::from(bgra), abgr);
+}
+
+
+#[test]
+fn converts() {
+    assert_eq!([1,2].as_gray(), [Gray::new(1), Gray::new(2)]);
+    assert_eq!([3].as_gray_mut(), [Gray::new(3)]);
+    assert_eq!([1,2].as_gray_alpha(), [GrayAlpha::new(1, 2)]);
+    // excess bytes are ignored
+    assert_eq!([1,2,3].as_gray_alpha_mut(), [GrayAlpha::new(1, 2)]);
+    assert_eq!([1,2,3,4].as_gray_alpha_mut(), [GrayAlpha::new(1, 2), GrayAlpha::new(3, 4)]);
+
+    assert_eq!(RGBA::new(1u8,2,3,255), RGB::new(1u8,2,3).into());
+    assert_eq!(RGBA::new(1u16,2,3,65535), RGB::new(1u16,2,3).into());
+    assert_eq!(BGRA{r:1u8,g:2u8,b:3u8,a:255u8}, BGR{r:1u8,g:2u8,b:3u8}.into());
+    assert_eq!(BGRA{r:1u8,g:2u8,b:3u8,a:255u8}, RGB{r:1u8,g:2u8,b:3u8}.into());
+    assert_eq!(RGBA {r:1u8,g:2,b:3,a:4u8}, BGRA{r:1u8,g:2u8,b:3u8,a:4u8}.into());
+    assert_eq!(BGR {r:1u8,g:2,b:3u8}, RGB {r:1u8,g:2,b:3u8}.into());
+    assert_eq!(RGB {r:1u16,g:0x5678,b:0xABCDu16}, BGR {r:1u16,g:0x5678,b:0xABCDu16}.into());
+    assert_eq!(BGR {r:0x1234567u32,g:2,b:3u32}, RGB {r:0x1234567u32,g:2,b:3u32}.into());
+
+    assert_eq!(&[1u8,2,3,4], RGBA {r:1u8,g:2,b:3,a:4u8}.as_slice());
+    assert_eq!(&[1u8,2,3,4], RGBA {r:1u8,g:2,b:3,a:4u8}.as_ref());
+    assert_eq!(&[1u8,2,3], RGB {r:1u8,g:2,b:3}.as_slice());
+    assert_eq!(&[1u8,2,3], RGB {r:1u8,g:2,b:3}.as_ref());
+
+    assert_eq!(&[1u8,2,3], RGB {r:1u8,g:2,b:3}.as_mut_slice());
+    assert_eq!(&[1u8,2,3], RGB {r:1u8,g:2,b:3}.as_mut());
+}
+
diff --git a/src/internal/convert/tuple.rs b/src/internal/convert/tuple.rs
new file mode 100644 (file)
index 0000000..ad2aa43
--- /dev/null
@@ -0,0 +1,89 @@
+use crate::alt::BGR;
+use crate::alt::BGRA;
+use crate::RGB;
+use crate::RGBA;
+use core::convert::*;
+
+impl<T> From<(T,T,T)> for RGB<T> {
+    #[inline]
+    fn from(other: (T,T,T)) -> Self {
+        Self {
+            r: other.0,
+            g: other.1,
+            b: other.2,
+        }
+    }
+}
+
+impl<T> Into<(T,T,T)> for RGB<T> {
+    #[inline]
+    fn into(self) -> (T,T,T) {
+        (self.r, self.g, self.b)
+    }
+}
+
+impl<T,A> From<(T,T,T,A)> for RGBA<T,A> {
+    #[inline]
+    fn from(other: (T,T,T,A)) -> Self {
+        Self {
+            r: other.0,
+            g: other.1,
+            b: other.2,
+            a: other.3,
+        }
+    }
+}
+
+impl<T,A> Into<(T,T,T,A)> for RGBA<T,A> {
+    #[inline]
+    fn into(self) -> (T,T,T,A) {
+        (self.r, self.g, self.b, self.a)
+    }
+}
+
+impl<T> From<(T,T,T)> for BGR<T> {
+    #[inline(always)]
+    fn from(other: (T,T,T)) -> Self {
+        Self {
+            b: other.0,
+            g: other.1,
+            r: other.2,
+        }
+    }
+}
+
+impl<T> Into<(T,T,T)> for BGR<T> {
+    #[inline(always)]
+    fn into(self) -> (T,T,T) {
+        (self.b, self.g, self.r)
+    }
+}
+
+impl<T,A> From<(T,T,T,A)> for BGRA<T,A> {
+    #[inline(always)]
+    fn from(other: (T,T,T,A)) -> Self {
+        Self {
+            b: other.0,
+            g: other.1,
+            r: other.2,
+            a: other.3,
+        }
+    }
+}
+
+impl<T,A> Into<(T,T,T,A)> for BGRA<T,A> {
+    #[inline(always)]
+    fn into(self) -> (T,T,T,A) {
+        (self.b, self.g, self.r, self.a)
+    }
+}
+
+#[test]
+fn converts() {
+    assert_eq!((1,2,3), RGB {r:1u8,g:2,b:3}.into());
+    assert_eq!(RGB {r:1u8,g:2,b:3}, (1,2,3).into());
+    assert_eq!((1,2,3,4), RGBA {r:1,g:2,b:3,a:4}.into());
+    assert_eq!(RGBA {r:1u8,g:2,b:3,a:4}, (1,2,3,4).into());
+    assert_eq!(BGRA {r:1u8,g:2,b:3,a:4}, (3,2,1,4).into());
+    assert_eq!(BGR {r:1u8,g:2,b:3}, (3,2,1).into());
+}
diff --git a/src/internal/ops.rs b/src/internal/ops.rs
new file mode 100644 (file)
index 0000000..6731d66
--- /dev/null
@@ -0,0 +1,412 @@
+use crate::alt::Gray;
+use crate::alt::GrayAlpha;
+use super::pixel::*;
+use crate::RGB;
+use crate::RGBA;
+use core::ops::*;
+use core::iter::Sum;
+#[cfg(feature = "argb")]
+use crate::alt::ARGB;
+#[cfg(feature = "grb")]
+use crate::alt::GRB;
+
+macro_rules! impl_struct_ops_opaque {
+    ($ty:ident => $($field:tt)+) => {
+        /// `px + px`
+        impl<T: Add> Add for $ty<T> {
+            type Output = $ty<<T as Add>::Output>;
+
+            #[inline(always)]
+            fn add(self, other: $ty<T>) -> Self::Output {
+                $ty {
+                    $(
+                        $field: self.$field + other.$field,
+                    )+
+                }
+            }
+        }
+
+        /// `px + px`
+        impl<T> AddAssign for $ty<T> where
+            T: Add<Output = T> + Copy
+        {
+            #[inline(always)]
+            fn add_assign(&mut self, other: $ty<T>) {
+                *self = Self {
+                    $(
+                        $field: self.$field + other.$field,
+                    )+
+                };
+            }
+        }
+
+        /// `px * px`
+        impl<T: Mul> Mul for $ty<T> {
+            type Output = $ty<<T as Mul>::Output>;
+
+            #[inline(always)]
+            fn mul(self, other: $ty<T>) -> Self::Output {
+                $ty {
+                    $(
+                        $field: self.$field * other.$field,
+                    )+
+                }
+            }
+        }
+
+        /// `px * px`
+        impl<T> MulAssign for $ty<T> where
+            T: Mul<Output = T> + Copy
+        {
+            #[inline(always)]
+            fn mul_assign(&mut self, other: $ty<T>) {
+                *self = Self {
+                    $(
+                        $field: self.$field * other.$field,
+                    )+
+                };
+            }
+        }
+
+        /// `px - px`
+        impl<T: Sub> Sub for $ty<T> {
+            type Output = $ty<<T as Sub>::Output>;
+
+            #[inline(always)]
+            fn sub(self, other: $ty<T>) -> Self::Output {
+                $ty {
+                    $(
+                        $field: self.$field - other.$field,
+                    )+
+                }
+            }
+        }
+
+        /// `px - px`
+        impl<T> SubAssign for $ty<T> where
+            T: Sub<Output = T> + Copy
+        {
+            #[inline(always)]
+            fn sub_assign(&mut self, other: $ty<T>) {
+                *self = Self {
+                    $(
+                        $field: self.$field - other.$field,
+                    )+
+                };
+            }
+        }
+
+        impl<T> Sum<$ty<T>> for $ty<T> where T: Default + Add<Output=T> {
+            #[inline(always)]
+            fn sum<I: Iterator<Item=Self>>(iter: I) -> Self {
+                iter.fold($ty::default(), Add::add)
+            }
+        }
+    };
+}
+
+macro_rules! impl_struct_ops_alpha {
+    ($ty:ident => $($field:tt)+) => {
+        /// `px + px`
+        impl<T: Add, A: Add> Add for $ty<T, A> {
+            type Output = $ty<<T as Add>::Output, <A as Add>::Output>;
+
+            #[inline(always)]
+            fn add(self, other: $ty<T, A>) -> Self::Output {
+                $ty {
+                    $(
+                        $field: self.$field + other.$field,
+                    )+
+                }
+            }
+        }
+
+        /// `px + px`
+        impl<T, A> AddAssign for $ty<T, A> where
+            T: Add<Output = T> + Copy,
+            A: Add<Output = A> + Copy
+        {
+            #[inline(always)]
+            fn add_assign(&mut self, other: $ty<T, A>) {
+                *self = Self {
+                    $(
+                        $field: self.$field + other.$field,
+                    )+
+                };
+            }
+        }
+
+        /// `px - px`
+        impl<T: Sub, A: Sub> Sub for $ty<T, A> {
+            type Output = $ty<<T as Sub>::Output, <A as Sub>::Output>;
+
+            #[inline(always)]
+            fn sub(self, other: $ty<T, A>) -> Self::Output {
+                $ty {
+                    $(
+                        $field: self.$field - other.$field,
+                    )+
+                }
+            }
+        }
+
+        /// `px - px`
+        impl<T, A> SubAssign for $ty<T, A> where
+            T: Sub<Output = T> + Copy,
+            A: Sub<Output = A> + Copy
+        {
+            #[inline(always)]
+            fn sub_assign(&mut self, other: $ty<T, A>) {
+                *self = Self {
+                    $(
+                        $field: self.$field - other.$field,
+                    )+
+                };
+            }
+        }
+
+        impl<T, A> Sum<$ty<T, A>> for $ty<T, A> where T: Default + Add<Output=T>, A: Default + Add<Output=A> {
+            #[inline(always)]
+            fn sum<I: Iterator<Item=Self>>(iter: I) -> Self {
+                iter.fold($ty::default(), Add::add)
+            }
+        }
+    };
+}
+
+macro_rules! impl_scalar {
+    ($ty:ident) => {
+        /// `px - 1`
+        impl<T> Sub<T> for $ty<T> where
+            T: Copy + Sub<Output=T>
+        {
+            type Output = $ty<<T as Sub>::Output>;
+
+            #[inline(always)]
+            fn sub(self, r: T) -> Self::Output {
+                self.map(|l| l-r)
+            }
+        }
+
+        /// `px - 1`
+        impl<T> SubAssign<T> for $ty<T> where
+            T: Copy + Sub<Output=T>
+        {
+            #[inline(always)]
+            fn sub_assign(&mut self, r: T) {
+                *self = self.map(|l| l-r);
+            }
+        }
+
+        /// `px + 1`
+        impl<T> Add<T> for $ty<T> where
+            T: Copy + Add<Output=T>
+        {
+            type Output = $ty<T>;
+
+            #[inline(always)]
+            fn add(self, r: T) -> Self::Output {
+                self.map(|l|l+r)
+            }
+        }
+
+        /// `px + 1`
+        impl<T> AddAssign<T> for $ty<T> where
+            T: Copy + Add<Output=T>
+        {
+            #[inline(always)]
+            fn add_assign(&mut self, r: T) {
+                *self = self.map(|l| l+r);
+            }
+        }
+
+        /// `px * 1`
+        impl<T> Mul<T> for $ty<T> where
+            T: Copy + Mul<Output=T>
+        {
+            type Output = $ty<T>;
+
+            #[inline(always)]
+            fn mul(self, r: T) -> Self::Output {
+                self.map(|l|l*r)
+            }
+        }
+
+        /// `px * 1`
+        impl<T> MulAssign<T> for $ty<T> where
+            T: Copy + Mul<Output=T>
+        {
+            #[inline(always)]
+            fn mul_assign(&mut self, r: T) {
+                *self = self.map(|l| l*r);
+            }
+        }
+
+        /// `px / 1`
+        impl<T> Div<T> for $ty<T> where
+            T: Copy + Div<Output=T>
+        {
+            type Output = $ty<T>;
+
+            #[inline(always)]
+            fn div(self, r: T) -> Self::Output {
+                self.map(|l| l / r)
+            }
+        }
+
+        /// `px * 1`
+        impl<T> DivAssign<T> for $ty<T> where
+            T: Copy + Div<Output=T>
+        {
+            #[inline(always)]
+            fn div_assign(&mut self, r: T) {
+                *self = self.map(|l| l / r);
+            }
+        }
+    }
+}
+
+impl_scalar!{RGB}
+impl_scalar!{RGBA}
+#[cfg(feature = "argb")]
+impl_scalar!{ARGB}
+#[cfg(feature = "grb")]
+impl_scalar!{GRB}
+impl_scalar!{Gray}
+impl_scalar!{GrayAlpha}
+
+impl_struct_ops_opaque! {RGB => r g b}
+#[cfg(feature = "grb")]
+impl_struct_ops_opaque! {GRB => g r b}
+impl_struct_ops_opaque! {Gray => 0}
+
+impl_struct_ops_alpha! {RGBA => r g b a}
+#[cfg(feature = "argb")]
+impl_struct_ops_alpha! {ARGB => a r g b}
+impl_struct_ops_alpha! {GrayAlpha => 0 1}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    const WHITE_RGB: RGB<u8> = RGB::new(255, 255, 255);
+    const BLACK_RGB: RGB<u8> = RGB::new(0, 0, 0);
+    const RED_RGB: RGB<u8> = RGB::new(255, 0, 0);
+    const GREEN_RGB: RGB<u8> = RGB::new(0, 255, 0);
+    const BLUE_RGB: RGB<u8> = RGB::new(0, 0, 255);
+
+    const WHITE_RGBA: RGBA<u8> = RGBA::new(255, 255, 255, 255);
+    const BLACK_RGBA: RGBA<u8> = RGBA::new(0, 0, 0, 0);
+    const RED_RGBA: RGBA<u8> = RGBA::new(255, 0, 0, 255);
+    const GREEN_RGBA: RGBA<u8> = RGBA::new(0, 255, 0, 0);
+    const BLUE_RGBA: RGBA<u8> = RGBA::new(0, 0, 255, 255);
+
+    #[test]
+    fn test_add() {
+        assert_eq!(RGB::new(2,4,6), RGB::new(1,2,3) + RGB{r:1,g:2,b:3});
+        assert_eq!(RGB::new(2.,4.,6.), RGB::new(1.,3.,5.) + 1.);
+
+        assert_eq!(RGBA::new_alpha(2u8,4,6,8u16), RGBA::new_alpha(1u8,2,3,4u16) + RGBA{r:1u8,g:2,b:3,a:4u16});
+        assert_eq!(RGBA::new(2i16,4,6,8), RGBA::new(1,3,5,7) + 1);
+
+        assert_eq!(RGB::new(255, 255, 0), RED_RGB+GREEN_RGB);
+        assert_eq!(RGB::new(255, 0, 0), RED_RGB+RGB::new(0, 0, 0));
+        assert_eq!(WHITE_RGB, BLACK_RGB + 255);
+
+        assert_eq!(RGBA::new(255, 255, 0, 255), RED_RGBA+GREEN_RGBA);
+        assert_eq!(RGBA::new(255, 0, 0, 255), RED_RGBA+RGBA::new(0, 0, 0, 0));
+        assert_eq!(WHITE_RGBA, BLACK_RGBA + 255);
+    }
+
+    #[test]
+    #[should_panic]
+    #[cfg(debug_assertions)]
+    fn test_add_overflow() {
+        assert_ne!(RGBA::new(255u8, 255, 0, 0), RED_RGBA+BLUE_RGBA);
+    }
+
+    #[test]
+    fn test_sub() {
+        assert_eq!(RED_RGB, (WHITE_RGB - GREEN_RGB) - BLUE_RGB);
+        assert_eq!(BLACK_RGB, WHITE_RGB - 255);
+
+        assert_eq!(RGBA::new(255, 255, 0, 0), WHITE_RGBA - BLUE_RGBA);
+        assert_eq!(BLACK_RGBA, WHITE_RGBA - 255);
+    }
+
+    #[test]
+    fn test_add_assign() {
+        let mut green_rgb = RGB::new(0, 255, 0);
+        green_rgb += RGB::new(255, 0, 255);
+        assert_eq!(WHITE_RGB, green_rgb);
+
+        let mut black_rgb = RGB::new(0, 0, 0);
+        black_rgb += 255;
+        assert_eq!(WHITE_RGB, black_rgb);
+
+        let mut green_rgba = RGBA::new(0, 255, 0, 0);
+        green_rgba += RGBA::new(255, 0, 255, 255);
+        assert_eq!(WHITE_RGBA, green_rgba);
+
+        let mut black_rgba = RGBA::new(0, 0, 0, 0);
+        black_rgba += 255;
+        assert_eq!(WHITE_RGBA, black_rgba);
+    }
+
+    #[test]
+    fn test_sub_assign() {
+        let mut green_rgb = RGB::new(0, 255, 0);
+        green_rgb -= RGB::new(0, 255, 0);
+        assert_eq!(BLACK_RGB, green_rgb);
+
+        let mut white_rgb = RGB::new(255, 255, 255);
+        white_rgb -= 255;
+        assert_eq!(BLACK_RGB, white_rgb);
+
+        let mut green_rgba = RGBA::new(0, 255, 0, 0);
+        green_rgba -= RGBA::new(0, 255, 0, 0);
+        assert_eq!(BLACK_RGBA, green_rgba);
+
+        let mut white_rgba = RGBA::new(255, 255, 255, 255);
+        white_rgba -= 255;
+        assert_eq!(BLACK_RGBA, white_rgba);
+    }
+
+    #[test]
+    fn test_mult() {
+        assert_eq!(RGB::new(0.5,1.5,2.5), RGB::new(1.,3.,5.) * 0.5);
+        assert_eq!(RGBA::new(2,4,6,8), RGBA::new(1,2,3,4) * 2);
+        assert_eq!(RGB::new(0.5,1.5,2.5) * RGB::new(1.,3.,5.),
+        RGB::new(0.5,4.5,12.5));
+    }
+
+    #[test]
+    fn test_mult_assign() {
+        let mut green_rgb = RGB::new(0u16, 255, 0);
+        green_rgb *= 1;
+        assert_eq!(RGB::new(0, 255, 0), green_rgb);
+        green_rgb *= 2;
+        assert_eq!(RGB::new(0, 255*2, 0), green_rgb);
+
+        let mut rgb = RGB::new(0.5,1.5,2.5);
+        rgb *= RGB::new(1.,3.,5.);
+        assert_eq!(rgb, RGB::new(0.5,4.5,12.5));
+
+        let mut green_rgba = RGBA::new(0u16, 255, 0, 0);
+        green_rgba *= 1;
+        assert_eq!(RGBA::new(0, 255, 0, 0), green_rgba);
+        green_rgba *= 2;
+        assert_eq!(RGBA::new(0, 255*2, 0, 0), green_rgba);
+    }
+
+    #[test]
+    fn sum() {
+        let s1 = [RGB::new(1u8,1,1), RGB::new(2,3,4)].iter().copied().sum::<RGB<u8>>();
+        let s2 = [RGB::new(1u16,1,1), RGB::new(2,3,4)].iter().copied().sum::<RGB<u16>>();
+        let s3 = [RGBA::new_alpha(1u8,1,1,1u16), RGBA::new_alpha(2,3,4,5)].iter().copied().sum::<RGBA<u8, u16>>();
+        let s4 = [RGBA::new_alpha(1u16,1,1,1u8), RGBA::new_alpha(2,3,4,5)].iter().copied().sum::<RGBA<u16, u8>>();
+        assert_eq!(s1, RGB::new(3, 4, 5));
+        assert_eq!(s2, RGB::new(3, 4, 5));
+        assert_eq!(s3, RGBA::new_alpha(3, 4, 5, 6));
+        assert_eq!(s4, RGBA::new_alpha(3, 4, 5, 6));
+    }
+}
diff --git a/src/internal/pixel.rs b/src/internal/pixel.rs
new file mode 100644 (file)
index 0000000..f2f8e2d
--- /dev/null
@@ -0,0 +1,85 @@
+
+/// Casting the struct to slices of its components
+pub trait ComponentSlice<T> {
+    /// The components interpreted as an array, e.g. one `RGB` expands to 3 elements.
+    ///
+    /// It's implemented for individual pixels as well as slices of pixels.
+    fn as_slice(&self) -> &[T];
+
+    /// The components interpreted as a mutable array, e.g. one `RGB` expands to 3 elements.
+    ///
+    /// It's implemented for individual pixels as well as slices of pixels.
+    ///
+    /// If you get an error when calling this on an array, add `[..]`
+    ///
+    /// > use of unstable library feature 'array_methods'
+    ///
+    /// ```rust,ignore
+    /// arr[..].as_mut_slice()
+    /// ```
+    fn as_mut_slice(&mut self) -> &mut [T];
+}
+
+/// Casting a slice of `RGB/A` values to a slice of `u8`
+///
+/// If instead of `RGB8` you use `RGB<MyCustomType>`, and you want to cast from/to that custom type,
+/// implement the `Plain` trait for it:
+///
+/// ```rust
+/// # #[derive(Copy, Clone)]
+/// # struct MyCustomType;
+/// unsafe impl rgb::Pod for MyCustomType {}
+/// unsafe impl rgb::Zeroable for MyCustomType {}
+/// ```
+///
+/// Plain types are not allowed to contain struct padding, booleans, chars, enums, references or pointers.
+#[cfg(feature = "as-bytes")]
+pub trait ComponentBytes<T: crate::Pod> where Self: ComponentSlice<T> {
+    /// The components interpreted as raw bytes, in machine's native endian. In `RGB` bytes of the red component are first.
+    #[inline]
+    fn as_bytes(&self) -> &[u8] {
+        assert_ne!(0, core::mem::size_of::<T>());
+        let slice = self.as_slice();
+        unsafe {
+            core::slice::from_raw_parts(slice.as_ptr() as *const _, slice.len() * core::mem::size_of::<T>())
+        }
+    }
+
+    /// The components interpreted as raw bytes, in machine's native endian. In `RGB` bytes of the red component are first.
+    #[inline]
+    fn as_bytes_mut(&mut self) -> &mut [u8] {
+        assert_ne!(0, core::mem::size_of::<T>());
+        let slice = self.as_mut_slice();
+        unsafe {
+            core::slice::from_raw_parts_mut(slice.as_mut_ptr() as *mut _, slice.len() * core::mem::size_of::<T>())
+        }
+    }
+}
+
+/// Applying operation to every component
+///
+/// ```rust
+/// use rgb::ComponentMap;
+/// # let pixel = rgb::RGB::new(0u8,0,0);
+/// let inverted = pixel.map(|c| 255 - c);
+///
+/// // For simple math there are Add/Sub/Mul implementations:
+/// let halved = pixel.map(|c| c / 2);
+/// let doubled = pixel * 2;
+/// ```
+pub trait ComponentMap<DestPixel, SrcComponent, DestComponent> {
+    /// Convenience function (equivalent of `self.iter().map().collect()`) for applying the same formula to every component.
+    ///
+    /// Note that it returns the pixel directly, not an Interator.
+    fn map<Callback>(&self, f: Callback) -> DestPixel
+        where Callback: FnMut(SrcComponent) -> DestComponent;
+}
+
+/// Same as `ComponentMap`, but doesn't change the alpha channel (if there's any alpha).
+pub trait ColorComponentMap<DestPixel, SrcComponent, DestComponent> {
+    /// Convenience function for applying the same formula to every rgb/gray component, but skipping the alpha component.
+    ///
+    /// Note that it returns the pixel directly, not an Interator.
+    fn map_c<Callback>(&self, f: Callback) -> DestPixel
+        where Callback: FnMut(SrcComponent) -> DestComponent;
+}
diff --git a/src/internal/rgb.rs b/src/internal/rgb.rs
new file mode 100644 (file)
index 0000000..ad78dff
--- /dev/null
@@ -0,0 +1,253 @@
+use super::pixel::*;
+use crate::alt::BGR;
+use crate::alt::BGRA;
+use crate::RGB;
+use crate::RGBA;
+use core::fmt;
+#[cfg(feature = "grb")]
+use crate::alt::GRB;
+
+impl<T> RGB<T> {
+    /// Convenience function for creating a new pixel
+    /// The order of arguments is R,G,B
+    #[inline(always)]
+    pub const fn new(r: T, g: T, b: T) -> Self {
+        Self { r, g, b }
+    }
+}
+
+impl<T> BGR<T> {
+    /// Convenience function for creating a new pixel
+    /// Wargning: The order of arguments is R,G,B
+    #[deprecated(note="This function has a misleading order of arguments. Use BGR{} literal instead")]
+    #[inline(always)]
+    pub const fn new(r: T, g: T, b: T) -> Self {
+        Self { b, g, r }
+    }
+}
+
+#[cfg(feature = "as-bytes")]
+unsafe impl<T> crate::Pod for RGB<T> where T: crate::Pod {}
+#[cfg(feature = "as-bytes")]
+unsafe impl<T> crate::Pod for BGR<T> where T: crate::Pod {}
+#[cfg(feature = "as-bytes")]
+unsafe impl<T> crate::Zeroable for RGB<T> where T: crate::Zeroable {}
+#[cfg(feature = "as-bytes")]
+unsafe impl<T> crate::Zeroable for BGR<T> where T: crate::Zeroable {}
+
+macro_rules! impl_rgb {
+    ($RGB:ident) => {
+        impl<T: Clone> $RGB<T> {
+            /// Iterate over color components (R, G, and B)
+            #[inline(always)]
+            pub fn iter(&self) -> core::iter::Cloned<core::slice::Iter<'_, T>> {
+                self.as_slice().iter().cloned()
+            }
+        }
+
+        impl<T: Copy, B> ComponentMap<$RGB<B>, T, B> for $RGB<T> {
+            #[inline(always)]
+            fn map<F>(&self, mut f: F) -> $RGB<B>
+                where F: FnMut(T) -> B {
+                $RGB {
+                    r:f(self.r),
+                    g:f(self.g),
+                    b:f(self.b),
+                }
+            }
+        }
+
+        impl<T: Copy, B> ColorComponentMap<$RGB<B>, T, B> for $RGB<T> {
+            #[inline(always)]
+            fn map_c<F>(&self, mut f: F) -> $RGB<B>
+                where F: FnMut(T) -> B {
+                $RGB {
+                    r:f(self.r),
+                    g:f(self.g),
+                    b:f(self.b),
+                }
+            }
+        }
+
+        impl<T> ComponentSlice<T> for $RGB<T> {
+            #[inline(always)]
+            fn as_slice(&self) -> &[T] {
+                unsafe {
+                    core::slice::from_raw_parts(self as *const Self as *const T, 3)
+                }
+            }
+
+            #[inline(always)]
+            fn as_mut_slice(&mut self) -> &mut [T] {
+                unsafe {
+                    core::slice::from_raw_parts_mut(self as *mut Self as *mut T, 3)
+                }
+            }
+        }
+
+        impl<T> ComponentSlice<T> for [$RGB<T>] {
+            #[inline]
+            fn as_slice(&self) -> &[T] {
+                unsafe {
+                    core::slice::from_raw_parts(self.as_ptr() as *const _, self.len() * 3)
+                }
+            }
+
+            #[inline]
+            fn as_mut_slice(&mut self) -> &mut [T] {
+                unsafe {
+                    core::slice::from_raw_parts_mut(self.as_mut_ptr() as *mut _, self.len() * 3)
+                }
+            }
+        }
+
+        #[cfg(feature = "as-bytes")]
+        impl<T: crate::Pod> ComponentBytes<T> for [$RGB<T>] {}
+    }
+}
+
+macro_rules! impl_rgb_to_alpha {
+    ($RGB:ident, $RGBA:ident) => {
+        impl<T: Clone> $RGB<T> {
+            /// Convenience function for converting to RGBA
+            #[inline(always)]
+            pub fn alpha(&self, a: T) -> $RGBA<T> {
+                $RGBA {
+                    r: self.r.clone(),
+                    g: self.g.clone(),
+                    b: self.b.clone(),
+                    a,
+                }
+            }
+
+            /// Convenience function for converting to RGBA with alpha channel of a different type than type of the pixels
+            #[inline(always)]
+            pub fn new_alpha<A>(&self, a: A) -> $RGBA<T, A> {
+                $RGBA {
+                    r: self.r.clone(),
+                    g: self.g.clone(),
+                    b: self.b.clone(),
+                    a,
+                }
+            }
+        }
+    }
+}
+
+
+impl<T> core::iter::FromIterator<T> for RGB<T> {
+    /// Takes exactly 3 elements from the iterator and creates a new instance.
+    /// Panics if there are fewer elements in the iterator.
+    #[inline(always)]
+    fn from_iter<I: IntoIterator<Item = T>>(into_iter: I) -> Self {
+        let mut iter = into_iter.into_iter();
+        Self {
+            r: iter.next().unwrap(),
+            g: iter.next().unwrap(),
+            b: iter.next().unwrap(),
+        }
+    }
+}
+
+impl_rgb!{RGB}
+impl_rgb_to_alpha!{RGB, RGBA}
+impl_rgb!{BGR}
+impl_rgb_to_alpha!{BGR, BGRA}
+#[cfg(feature = "grb")]
+impl_rgb!{GRB}
+
+impl<T: fmt::Display> fmt::Display for RGB<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f,"rgb({},{},{})", self.r,self.g,self.b)
+    }
+}
+
+impl<T: fmt::UpperHex> fmt::UpperHex for RGB<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f,"RGB {{ #{:02X}{:02X}{:02X} }}", self.r, self.g, self.b)
+    }
+}
+
+impl<T: fmt::LowerHex> fmt::LowerHex for RGB<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f,"RGB {{ #{:02x}{:02x}{:02x} }}", self.r, self.g, self.b)
+    }
+}
+
+impl<T: fmt::Display> fmt::Display for BGR<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f,"bgr({},{},{})", self.b, self.g, self.r)
+    }
+}
+
+impl<T: fmt::UpperHex> fmt::UpperHex for BGR<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f,"BGR {{ #{:02X}{:02X}{:02X} }}", self.b, self.g, self.r)
+    }
+}
+
+impl<T: fmt::LowerHex> fmt::LowerHex for BGR<T> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f,"BGR {{ #{:02x}{:02x}{:02x} }}", self.b, self.g, self.r)
+    }
+}
+
+#[cfg(test)]
+mod rgb_test {
+    use super::*;
+    use std;
+
+    #[test]
+    #[cfg(feature = "grb")]
+    fn grb_test() {
+        let grb = GRB {g:1,r:2,b:3}.map(|c| c * 2) + 1;
+        let rgb: crate::RGB8 = grb.into();
+        assert_eq!(rgb, RGB::new(5,3,7));
+    }
+
+    #[test]
+    fn sanity_check() {
+        let neg = RGB::new(1,2,3i32).map(|x| -x);
+        assert_eq!(neg.r, -1);
+        assert_eq!(neg.g, -2);
+        assert_eq!(neg.b, -3);
+
+        let mut px = RGB::new(3,4,5);
+        px.as_mut_slice()[1] = 111;
+        assert_eq!(111, px.g);
+
+        assert_eq!(RGBA::new(250,251,252,253), RGB::new(250,251,252).alpha(253));
+
+        assert_eq!(RGB{r:1u8,g:2,b:3}, RGB::new(1u8,2,3));
+        assert!(RGB{r:1u8,g:1,b:2} < RGB::new(2,1,1));
+
+        let mut h = std::collections::HashSet::new();
+        h.insert(px);
+        assert!(h.contains(&RGB::new(3,111,5)));
+        assert!(!h.contains(&RGB::new(111,5,3)));
+
+
+        #[cfg(feature = "as-bytes")]
+        {
+            let v = vec![RGB::new(1u8,2,3), RGB::new(4,5,6)];
+            assert_eq!(&[1,2,3,4,5,6], v.as_bytes());
+        }
+
+        assert_eq!(RGB::new(0u8,0,0), Default::default());
+    }
+
+    #[test]
+    #[allow(deprecated)]
+    fn test_fmt() {
+        let red_rgb = RGB::new(255, 0, 0);
+        let red_bgr = BGR::new(255, 0, 0);
+        assert_eq!("RGB { #FF0000 }", &format!("{:X}", red_rgb));
+        assert_eq!("BGR { #0000FF }", &format!("{:X}", red_bgr));
+
+        assert_eq!("RGB { #ff0000 }", &format!("{:x}", red_rgb));
+        assert_eq!("BGR { #0000ff }", &format!("{:x}", red_bgr));
+
+        assert_eq!("rgb(255,0,0)", &format!("{}", red_rgb));
+        assert_eq!("bgr(0,0,255)", &format!("{}", red_bgr));
+    }
+}
diff --git a/src/internal/rgba.rs b/src/internal/rgba.rs
new file mode 100644 (file)
index 0000000..3804603
--- /dev/null
@@ -0,0 +1,445 @@
+use core::fmt;
+use crate::alt::*;
+use crate::RGB;
+use crate::RGBA;
+use super::pixel::*;
+
+impl<T> RGBA<T> {
+    #[inline(always)]
+    /// Convenience function for creating a new pixel
+    /// The order of arguments is R,G,B,A
+    pub const fn new(r: T, g: T, b: T, a: T) -> Self {
+        Self {r,g,b,a}
+    }
+}
+
+impl<T, A> RGBA<T,A> {
+    #[inline(always)]
+    /// Convenience function for creating a new pixel
+    /// The order of arguments is R,G,B,A
+    pub const fn new_alpha(r: T, g: T, b: T, a: A) -> Self {
+        Self {r,g,b,a}
+    }
+}
+
+impl<T> BGRA<T> {
+    #[inline(always)]
+    /// Convenience function for creating a new pixel
+    /// Warning: The order of arguments is R,G,B,A
+    #[deprecated(note="This function has a misleading order of arguments. Use BGRA{} literal instead")]
+    pub const fn new(r: T, g: T, b: T, a: T) -> Self {
+        Self {r,g,b,a}
+    }
+}
+
+impl<T, A> BGRA<T,A> {
+    #[inline(always)]
+    /// Convenience function for creating a new pixel
+    /// Warning: The order of arguments is R,G,B,A
+    #[deprecated(note="This function has a misleading order of arguments. Use BGRA{} literal instead")]
+    pub const fn new_alpha(r: T, g: T, b: T, a: A) -> Self {
+        Self {r,g,b,a}
+    }
+}
+
+#[cfg(feature = "as-bytes")]
+unsafe impl<T, A> crate::Pod for RGBA<T, A> where T: crate::Pod, A: crate::Pod {}
+#[cfg(feature = "as-bytes")]
+unsafe impl<T, A> crate::Pod for BGRA<T, A> where T: crate::Pod, A: crate::Pod {}
+#[cfg(feature = "as-bytes")]
+unsafe impl<T, A> crate::Zeroable for RGBA<T, A> where T: crate::Zeroable, A: crate::Zeroable {}
+#[cfg(feature = "as-bytes")]
+unsafe impl<T, A> crate::Zeroable for BGRA<T, A> where T: crate::Zeroable, A: crate::Zeroable {}
+
+#[cfg(feature = "argb")]
+impl<T> ARGB<T> {
+    #[inline(always)]
+    /// Convenience function for creating a new pixel
+    /// The order of arguments is R,G,B,A
+    #[deprecated(note="This function has a misleading order of arguments. Use ARGB{} literal instead")]
+    pub const fn new(r: T, g: T, b: T, a: T) -> Self {
+        Self {r,g,b,a}
+    }
+}
+
+#[cfg(feature = "argb")]
+impl<T, A> ARGB<T,A> {
+    #[inline(always)]
+    /// Convenience function for creating a new pixel
+    /// The order of arguments is R,G,B,A
+    #[deprecated(note="This function has a misleading order of arguments. Use ARGB{} literal instead")]
+    pub const fn new_alpha(r: T, g: T, b: T, a: A) -> Self {
+        Self {r,g,b,a}
+    }
+}
+
+#[cfg(feature = "argb")]
+impl<T> ABGR<T> {
+    #[inline(always)]
+    /// Convenience function for creating a new pixel
+    /// The order of arguments is R,G,B,A
+    #[deprecated(note="This function has a misleading order of arguments. Use ABGR{} literal instead")]
+    pub const fn new(r: T, g: T, b: T, a: T) -> Self {
+        Self {r,g,b,a}
+    }
+}
+
+#[cfg(feature = "argb")]
+impl<T, A> ABGR<T,A> {
+    #[inline(always)]
+    /// Convenience function for creating a new pixel
+    /// The order of arguments is R,G,B,A
+    #[deprecated(note="This function has a misleading order of arguments. Use ABGR{} literal instead")]
+    pub const fn new_alpha(r: T, g: T, b: T, a: A) -> Self {
+        Self {r,g,b,a}
+    }
+}
+
+#[cfg(all(feature = "as-bytes", feature = "argb"))]
+unsafe impl<T, A> crate::Pod for ARGB<T, A> where T: crate::Pod, A: crate::Pod {}
+#[cfg(all(feature = "as-bytes", feature = "argb"))]
+unsafe impl<T, A> crate::Pod for ABGR<T, A> where T: crate::Pod, A: crate::Pod {}
+#[cfg(all(feature = "as-bytes", feature = "argb"))]
+unsafe impl<T, A> crate::Zeroable for ARGB<T, A> where T: crate::Zeroable, A: crate::Zeroable {}
+#[cfg(all(feature = "as-bytes", feature = "argb"))]
+unsafe impl<T, A> crate::Zeroable for ABGR<T, A> where T: crate::Zeroable, A: crate::Zeroable {}
+
+macro_rules! impl_rgba {
+    ($RGBA:ident) => {
+        impl<T: Clone> $RGBA<T> {
+            /// Iterate over all components (length=4)
+            #[inline(always)]
+            pub fn iter(&self) -> core::iter::Cloned<core::slice::Iter<'_, T>> {
+                self.as_slice().iter().cloned()
+            }
+        }
+
+        impl<T: Clone, A> $RGBA<T, A> {
+            /// Copy RGB components out of the RGBA struct
+            ///
+            /// Note: you can use `.into()` to convert between other types
+            #[inline(always)]
+            pub fn bgr(&self) -> BGR<T> {
+                BGR {r:self.r.clone(), g:self.g.clone(), b:self.b.clone()}
+            }
+        }
+
+        impl<T: Copy, A: Clone> $RGBA<T, A> {
+            /// Create new RGBA with the same alpha value, but different RGB values
+            #[inline(always)]
+            pub fn map_rgb<F, U, B>(&self, mut f: F) -> $RGBA<U, B>
+                where F: FnMut(T) -> U, U: Clone, B: From<A> + Clone
+            {
+                $RGBA {
+                    r: f(self.r),
+                    g: f(self.g),
+                    b: f(self.b),
+                    a: self.a.clone().into(),
+                }
+            }
+
+            #[inline(always)]
+            /// Create a new RGBA with the new alpha value, but same RGB values
+            pub fn alpha(&self, a: A) -> Self {
+                Self {
+                    r: self.r, g: self.g, b: self.b, a,
+                }
+            }
+
+            /// Create a new RGBA with a new alpha value created by the callback.
+            /// Allows changing of the type used for the alpha channel.
+            #[inline]
+            pub fn map_alpha<F, B>(&self, f: F) -> $RGBA<T, B>
+                where F: FnOnce(A) -> B {
+                $RGBA {
+                    r: self.r,
+                    g: self.g,
+                    b: self.b,
+                    a: f(self.a.clone()),
+                }
+            }
+        }
+
+        impl<T: Copy, B> ComponentMap<$RGBA<B>, T, B> for $RGBA<T> {
+            #[inline(always)]
+            fn map<F>(&self, mut f: F) -> $RGBA<B>
+            where
+                F: FnMut(T) -> B,
+            {
+                $RGBA {
+                    r: f(self.r),
+                    g: f(self.g),
+                    b: f(self.b),
+                    a: f(self.a),
+                }
+            }
+        }
+
+        impl<T: Copy, A: Copy, B> ColorComponentMap<$RGBA<B, A>, T, B> for $RGBA<T, A> {
+            #[inline(always)]
+            fn map_c<F>(&self, mut f: F) -> $RGBA<B, A>
+            where
+                F: FnMut(T) -> B,
+            {
+                $RGBA {
+                    r: f(self.r),
+                    g: f(self.g),
+                    b: f(self.b),
+                    a: self.a,
+                }
+            }
+        }
+
+        impl<T> ComponentSlice<T> for $RGBA<T> {
+            #[inline(always)]
+            fn as_slice(&self) -> &[T] {
+                unsafe {
+                    core::slice::from_raw_parts(self as *const Self as *const T, 4)
+                }
+            }
+
+            #[inline(always)]
+            fn as_mut_slice(&mut self) -> &mut [T] {
+                unsafe {
+                    core::slice::from_raw_parts_mut(self as *mut Self as *mut T, 4)
+                }
+            }
+        }
+
+        impl<T> ComponentSlice<T> for [$RGBA<T>] {
+            #[inline]
+            fn as_slice(&self) -> &[T] {
+                unsafe {
+                    core::slice::from_raw_parts(self.as_ptr() as *const _, self.len() * 4)
+                }
+            }
+            #[inline]
+            fn as_mut_slice(&mut self) -> &mut [T] {
+                unsafe {
+                    core::slice::from_raw_parts_mut(self.as_mut_ptr() as *mut _, self.len() * 4)
+                }
+            }
+        }
+
+        #[cfg(feature = "as-bytes")]
+        impl<T: crate::Pod> ComponentBytes<T> for [$RGBA<T>] {}
+    }
+}
+
+macro_rules! impl_alpha_conv {
+    ($RGB:ident, $RGBA:ident) => {
+        /// Assumes 255 is opaque
+        impl<T: Copy> From<$RGB<T>> for $RGBA<T, u8> {
+            #[inline(always)]
+            fn from(other: $RGB<T>) -> Self {
+                Self {
+                    r: other.r,
+                    g: other.g,
+                    b: other.b,
+                    a: 0xFF,
+                }
+            }
+        }
+
+        /// Assumes 65535 is opaque
+        impl<T: Copy> From<$RGB<T>> for $RGBA<T, u16> {
+            #[inline(always)]
+            fn from(other: $RGB<T>) -> Self {
+                Self {
+                    r: other.r,
+                    g: other.g,
+                    b: other.b,
+                    a: 0xFFFF,
+                }
+            }
+        }
+    }
+}
+
+impl<T, A> RGBA<T, A> {
+    /// Provide a mutable view of only RGB components (leaving out alpha).
+    /// Useful to change color without changing opacity.
+    #[inline(always)]
+    pub fn rgb_mut(&mut self) -> &mut RGB<T> {
+        unsafe {
+            &mut *(self as *mut _ as *mut RGB<T>)
+        }
+    }
+}
+
+impl<T, A> BGRA<T, A> {
+    /// Provide a mutable view of only RGB components (leaving out alpha).
+    /// Useful to change color without changing opacity.
+    #[inline(always)]
+    #[deprecated(note = "This function will change. Use bgr_mut()")]
+    pub fn rgb_mut(&mut self) -> &mut BGR<T> {
+        unsafe {
+            &mut *(self as *mut _ as *mut BGR<T>)
+        }
+    }
+
+    /// Provide a mutable view of only RGB components (leaving out alpha).
+    /// Useful to change color without changing opacity.
+    #[inline(always)]
+    pub fn bgr_mut(&mut self) -> &mut BGR<T> {
+        unsafe {
+            &mut *(self as *mut _ as *mut BGR<T>)
+        }
+    }
+}
+
+impl<T> core::iter::FromIterator<T> for RGBA<T> {
+    #[inline(always)]
+    /// Takes exactly 4 elements from the iterator and creates a new instance.
+    /// Panics if there are fewer elements in the iterator.
+    fn from_iter<I: IntoIterator<Item = T>>(into_iter: I) -> Self {
+        let mut iter = into_iter.into_iter();
+        Self {
+            r: iter.next().unwrap(),
+            g: iter.next().unwrap(),
+            b: iter.next().unwrap(),
+            a: iter.next().unwrap(),
+        }
+    }
+}
+
+impl<T: Clone, A> RGBA<T, A> {
+    /// Copy RGB components out of the RGBA struct
+    ///
+    /// Note: you can use `.into()` to convert between other types
+    #[inline(always)]
+    pub fn rgb(&self) -> RGB<T> {
+        RGB {r:self.r.clone(), g:self.g.clone(), b:self.b.clone()}
+    }
+}
+
+impl<T: Clone, A> BGRA<T, A> {
+    /// Copy RGB components out of the RGBA struct
+    ///
+    /// Note: you can use `.into()` to convert between other types
+    #[inline(always)]
+    #[deprecated(note = "This function will change. Use bgr()")]
+    pub fn rgb(&self) -> BGR<T> {
+        BGR {r:self.r.clone(), g:self.g.clone(), b:self.b.clone()}
+    }
+}
+
+impl_rgba! {RGBA}
+impl_rgba! {BGRA}
+#[cfg(feature = "argb")]
+impl_rgba! {ARGB}
+#[cfg(feature = "argb")]
+impl_rgba! {ABGR}
+
+impl_alpha_conv! {BGR, BGRA}
+impl_alpha_conv! {RGB, BGRA}
+impl_alpha_conv! {BGR, RGBA}
+impl_alpha_conv! {RGB, RGBA}
+#[cfg(feature = "argb")]
+impl_alpha_conv! {BGR, ABGR}
+#[cfg(feature = "argb")]
+impl_alpha_conv! {RGB, ABGR}
+#[cfg(feature = "argb")]
+impl_alpha_conv! {BGR, ARGB}
+#[cfg(feature = "argb")]
+impl_alpha_conv! {RGB, ARGB}
+
+impl<T: fmt::Display, A: fmt::Display> fmt::Display for RGBA<T, A> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "rgba({},{},{},{})", self.r, self.g, self.b, self.a)
+    }
+}
+
+impl<T: fmt::Display, A: fmt::Display> fmt::Display for BGRA<T, A> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "bgra({},{},{},{})", self.r, self.g, self.b, self.a)
+    }
+}
+
+#[test]
+fn rgba_test() {
+    let neg = RGBA::new(1,2,3i32,1000).map(|x| -x);
+    assert_eq!(neg.r, -1);
+    assert_eq!(neg.rgb().r, -1);
+    assert_eq!(neg.g, -2);
+    assert_eq!(neg.rgb().g, -2);
+    assert_eq!(neg.b, -3);
+    assert_eq!(neg.rgb().b, -3);
+    assert_eq!(neg.a, -1000);
+    assert_eq!(neg.map_alpha(|x| x+1).a, -999);
+    assert_eq!(neg, neg.as_slice().iter().cloned().collect());
+    assert!(neg < RGBA::new(0,0,0,0));
+
+    let neg = RGBA::new(1u8,2,3,4).map_rgb(|c| -(c as i16));
+    assert_eq!(-1i16, neg.r);
+    assert_eq!(4i16, neg.a);
+    let neg = RGBA::new(1u8,2,3,4).map_c(|c| -(c as i16));
+    assert_eq!(-1i16, neg.r);
+    assert_eq!(4u8, neg.a);
+
+    let mut px = RGBA{r:1,g:2,b:3,a:4};
+    px.as_mut_slice()[3] = 100;
+    assert_eq!(1, px.rgb_mut().r);
+    assert_eq!(2, px.rgb_mut().g);
+    px.rgb_mut().b = 4;
+    assert_eq!(4, px.rgb_mut().b);
+    assert_eq!(100, px.a);
+
+    #[cfg(feature = "as-bytes")]
+    {
+        let v = vec![RGBA::new(1u8,2,3,4), RGBA::new(5,6,7,8)];
+        assert_eq!(&[1,2,3,4,5,6,7,8], v.as_bytes());
+    }
+}
+
+#[test]
+#[cfg(feature = "argb")]
+fn abgr_test() {
+    let abgr = ABGR {r:1,g:2,b:3,a:4};
+    assert_eq!(4, abgr.as_slice()[0]);
+    use crate::AsPixels;
+    assert_eq!(abgr, [abgr].as_bytes().as_pixels()[0]);
+}
+
+#[test]
+#[allow(deprecated)]
+fn bgra_test() {
+    let neg = BGRA::new(1, 2, 3i32, 1000).map(|x| -x);
+    let _ = neg.as_slice();
+
+    #[cfg(feature = "as-bytes")]
+    {
+        let _ = [neg].as_bytes();
+    }
+    assert_eq!(neg.r, -1);
+    assert_eq!(neg.bgr().r, -1);
+    assert_eq!(neg.g, -2);
+    assert_eq!(neg.bgr().g, -2);
+    assert_eq!(neg.b, -3);
+    assert_eq!(neg.bgr().b, -3);
+    assert_eq!(neg.a, -1000);
+    assert_eq!(&[-3,-2,-1,-1000], neg.as_slice());
+    assert!(neg < BGRA::new(0, 0, 0, 0));
+
+    let neg = BGRA::new(1u8, 2u8, 3u8, 4u8).map_rgb(|c| -(c as i16));
+    assert_eq!(-1i16, neg.r);
+    assert_eq!(4i16, neg.a);
+    let neg = BGRA::new(1u8, 2u8, 3u8, 4u8).map_c(|c| -(c as i16));
+    assert_eq!(-1i16, neg.r);
+    assert_eq!(4u8, neg.a);
+
+    let mut px = BGRA{r:1,g:2,b:3,a:-9}.alpha(4);
+    px.as_mut_slice()[3] = 100;
+    assert_eq!(1, px.bgr_mut().r);
+    assert_eq!(2, px.bgr_mut().g);
+    px.bgr_mut().b = 4;
+    assert_eq!(4, px.bgr_mut().b);
+    assert_eq!(100, px.a);
+
+
+    #[cfg(feature = "as-bytes")]
+    {
+        let v = vec![BGRA::new(3u8, 2, 1, 4), BGRA::new(7, 6, 5, 8)];
+        assert_eq!(&[1,2,3,4,5,6,7,8], v.as_bytes());
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644 (file)
index 0000000..905c4a0
--- /dev/null
@@ -0,0 +1,229 @@
+//! Basic struct for `RGB` and `RGBA` pixels. Packed, with red first, alpha last.
+//!
+//! This crate is intended to be the lowest common denominator for sharing `RGB`/`RGBA` bitmaps between other crates.
+//!
+//! The crate includes convenience functions for converting between the struct and bytes,
+//! and overloaded operators that work on all channels at once.
+//!
+//! This crate intentionally doesn't implement color management (due to complexity of the problem),
+//! but the structs can be parametrized to implement this if necessary. Other colorspaces are out of scope.
+//!
+//! ```rust
+//! # use rgb::*;
+//! let pixel = RGB8 {r:0, g:100, b:255};
+//!
+//! let pixel_rgba = pixel.alpha(255);
+//! let pixel = pixel_rgba.rgb();
+//!
+//! let pixels = vec![pixel; 100];
+//! use rgb::ComponentBytes; // Import byte conversion trait
+//! let bytes = pixels.as_bytes();
+//!
+//! use rgb::ComponentMap; // Import pixel map trait
+//! let half_bright = pixel.map(|channel| channel / 2);
+//! let doubled = half_bright * 2;
+//! # let _ = doubled;
+//! ```
+#![doc(html_logo_url = "https://kornel.ski/rgb-logo.png")]
+#![no_std]
+
+#![warn(missing_docs)]
+
+// std is required to run unit tests
+#[cfg(test)]
+#[macro_use] extern crate std;
+
+#[cfg(feature = "serde")]
+#[macro_use] extern crate serde;
+
+mod internal {
+    pub mod convert;
+    pub mod ops;
+    pub mod pixel;
+    pub mod rgb;
+    pub mod rgba;
+}
+
+/// BGR/BGRA alernative layouts & grayscale
+///
+/// BGR might be useful for some Windows or OpenGL APIs.
+pub mod alt;
+
+/// Re-export from `bytemuck` crate
+#[cfg(feature = "as-bytes")]
+pub use bytemuck::Pod;
+/// Re-export from `bytemuck` crate
+#[cfg(feature = "as-bytes")]
+pub use bytemuck::Zeroable;
+
+pub use crate::internal::convert::*;
+pub use crate::internal::ops::*;
+pub use crate::internal::pixel::*;
+pub use crate::internal::rgb::*;
+pub use crate::internal::rgba::*;
+
+#[repr(C)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
+/// The RGB pixel
+///
+/// The component type can be `u8` (aliased as `RGB8`), `u16` (aliased as `RGB16`),
+/// or any other type (but simple copyable types are recommended.)
+pub struct RGB<ComponentType> {
+    /// Red
+    pub r: ComponentType,
+    /// Green
+    pub g: ComponentType,
+    /// Blue
+    pub b: ComponentType,
+}
+
+#[repr(C)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
+/// The RGBA pixel
+///
+/// The component type can be `u8` (aliased as `RGBA8`), `u16` (aliased as `RGBA16`),
+/// or any other type (but simple copyable types are recommended.)
+///
+/// You can specify a different type for alpha, but it's only for special cases
+/// (e.g. if you use a newtype like `RGBA<LinearLight<u16>, u16>`).
+pub struct RGBA<ComponentType, AlphaComponentType = ComponentType> {
+    /// Red
+    pub r: ComponentType,
+    /// Green
+    pub g: ComponentType,
+    /// Blue
+    pub b: ComponentType,
+    /// Alpha
+    pub a: AlphaComponentType,
+}
+
+/// 8-bit RGB
+///
+/// The colorspace is technically undefined, but generally sRGB is assumed.
+pub type RGB8 = RGB<u8>;
+
+/// 16-bit RGB in machine's native endian
+///
+/// Be careful to perform byte-swapping when reading from files.
+pub type RGB16 = RGB<u16>;
+
+/// 8-bit RGBA, alpha is last. 0 = transparent, 255 = opaque.
+pub type RGBA8 = RGBA<u8>;
+
+/// 16-bit RGB in machine's native endian. 0 = transparent, 65535 = opaque.
+///
+/// Alpha is last.
+pub type RGBA16 = RGBA<u16>;
+
+#[test]
+fn rgb_works() {
+    let rgb = RGB{r:0u8,g:128,b:255}.clone();
+    assert_eq!(rgb.b, 255);
+
+    assert_eq!(rgb, rgb.iter().map(|ch| ch).collect());
+
+    #[cfg(feature = "as-bytes")]
+    {
+        assert_eq!(0, [rgb].as_bytes()[0]);
+        assert_eq!(128, [rgb].as_bytes()[1]);
+        assert_eq!(255, [rgb].as_bytes()[2]);
+    }
+
+    let rgb = RGB16{r:0u16,g:0x7F7F,b:65535};
+    assert_eq!(rgb.b, 65535);
+    assert_eq!(rgb.as_slice()[1], 0x7F7F);
+
+
+    #[cfg(feature = "as-bytes")]
+    {
+        assert_eq!(0, [rgb].as_bytes()[0]);
+        assert_eq!(0, [rgb].as_bytes()[1]);
+        assert_eq!(0x7F, [rgb].as_bytes()[2]);
+        assert_eq!(0x7F, [rgb].as_bytes()[3]);
+        assert_eq!(0xFF, [rgb].as_bytes()[4]);
+        assert_eq!(0xFF, [rgb].as_bytes()[5]);
+    }
+
+    assert_eq!("rgb(1,2,3)", format!("{}", RGB::new(1,2,3)));
+}
+
+#[test]
+fn sub_floats() {
+    assert_eq!(RGBA{r:2.5_f64, g:-1.5, b:0., a:5.}, RGBA{r:3.5_f64, g:-0.5, b:-2., a:0.} - RGBA{r:1.0_f64, g:1., b:-2., a:-5.});
+}
+
+#[test]
+fn into() {
+    let a:RGB8 = RGB{r:0,g:1,b:2};
+    let b:RGB<i16> = a.into();
+    let c:RGB<f32> = b.into();
+    let d:RGB<f32> = a.into();
+    assert_eq!(c, d);
+}
+
+#[test]
+fn rgba_works() {
+    let rgba = RGBA{r:0u8,g:128,b:255,a:33}.clone();
+    assert_eq!(rgba.b, 255);
+    assert_eq!(rgba.a, 33);
+
+    assert_eq!(rgba, rgba.iter().map(|ch| ch).collect());
+
+    assert_eq!("rgba(1,2,3,4)", format!("{}", RGBA::new(1,2,3,4)));
+
+    assert_eq!(rgba - rgba, RGBA::new(0,0,0,0));
+}
+
+#[test]
+fn bytes() {
+    let rgb = RGB8::new(1,2,3);
+
+    #[cfg(feature = "as-bytes")]
+    {
+        let rgb_arr = [rgb];
+        let rgb_bytes = rgb_arr.as_bytes();
+        assert_eq!(&[1,2,3], rgb_bytes);
+        assert_eq!(rgb_bytes.as_rgba().len(), 0);
+        assert_eq!({let t: &[RGBA8] = rgb_bytes.as_pixels(); t}.len(), 0);
+        assert_eq!(rgb, rgb_bytes.into_iter().cloned().collect());
+        assert_eq!(&[rgb], rgb_bytes.as_rgb());
+        assert_eq!(&[rgb], rgb_bytes.as_pixels());
+    }
+    let mut rgb2 = [rgb];
+    assert_eq!(rgb2[..].as_mut_slice().as_rgb_mut(), &mut [rgb]);
+    assert_eq!(&mut [rgb], rgb2[..].as_mut_slice().as_pixels_mut());
+
+
+    #[cfg(feature = "as-bytes")]
+    {
+        let rgba = RGBA8::new(1,2,3,4);
+        let mut rgba_arr = [rgba];
+        let rgba_bytes = rgba_arr.as_bytes_mut();
+        assert_eq!(&[1,2,3,4], rgba_bytes);
+        assert_eq!(&[rgba], rgba_bytes.as_rgba());
+        rgba_bytes[3] = 99;
+        assert_eq!(RGBA8::new(1,2,3,99), rgba_arr.as_bytes().into_iter().cloned().collect());
+    }
+
+    let rgb = RGB16::new(1,2,3);
+    let rgb_slice = rgb.as_slice();
+    assert_eq!(&[1,2,3], rgb_slice);
+    assert_eq!(rgb_slice.as_rgba(), &[]);
+    assert_eq!(&[rgb], rgb_slice.as_rgb());
+    assert_eq!(rgb, rgb_slice.into_iter().cloned().collect());
+
+    let rgba = RGBA16::new(1,2,3,4);
+    let rgba_slice = rgba.as_slice();
+    assert_eq!(&[1,2,3,4], rgba_slice);
+    assert_eq!(&[1,2,3], rgba_slice.as_rgb()[0].as_slice());
+    assert_eq!(&[rgba], rgba_slice.as_rgba());
+    assert_eq!(rgba, rgba_slice.into_iter().cloned().collect());
+    let mut rgba2 = [rgba];
+    assert_eq!(rgba2[..].as_mut_slice().as_rgba_mut(), &mut [rgba]);
+
+    let mut foo = vec![0u8; 8];
+    foo.as_rgba_mut()[1] = RGBA::new(1,2,3,4);
+    assert_eq!(&[0u8,0,0,0,1,2,3,4], &foo[..]);
+}