Move color module to new git repository
This commit is contained in:
parent
b429d91fcf
commit
7f14c6784b
6 changed files with 0 additions and 1327 deletions
|
@ -1,146 +0,0 @@
|
|||
use num::types::Number;
|
||||
|
||||
/**
|
||||
* A color channel
|
||||
*/
|
||||
pub trait Channel: Number {
|
||||
/**
|
||||
* The maximum value used by the channel
|
||||
*/
|
||||
static pure fn max() -> self;
|
||||
|
||||
/**
|
||||
* Convert a channel to the enclosing type
|
||||
*
|
||||
* # Example
|
||||
*
|
||||
* ~~~
|
||||
* let chan: f32 = Channel::from(0xFFFFu16);
|
||||
* assert chan == 1.0f32;
|
||||
* ~~~
|
||||
*/
|
||||
static pure fn from<T:Channel>(val: T) -> self;
|
||||
|
||||
pure fn to_channel_u8(&self) -> u8;
|
||||
pure fn to_channel_u16(&self) -> u16;
|
||||
pure fn to_channel_u32(&self) -> u32;
|
||||
pure fn to_channel_u64(&self) -> u64;
|
||||
pure fn to_channel_f32(&self) -> f32;
|
||||
pure fn to_channel_f64(&self) -> f64;
|
||||
pure fn to_channel_float(&self) -> float;
|
||||
|
||||
pure fn inverse(&self) -> self;
|
||||
}
|
||||
|
||||
pub impl u8: Channel {
|
||||
#[inline(always)] static pure fn max() -> u8 { 0xFF } // 2^8
|
||||
|
||||
#[inline(always)] static pure fn from<T:Channel>(val: T) -> u8 { val.to_channel_u8() }
|
||||
|
||||
#[inline(always)] pure fn to_channel_u8(&self) -> u8 { (*self) }
|
||||
#[inline(always)] pure fn to_channel_u16(&self) -> u16 { (*self as u16 << 8) | (*self) as u16 }
|
||||
#[inline(always)] pure fn to_channel_u32(&self) -> u32 { (self.to_channel_u16() as u32 << 16) | self.to_channel_u16() as u32 }
|
||||
#[inline(always)] pure fn to_channel_u64(&self) -> u64 { (self.to_channel_u32() as u64 << 32) | self.to_channel_u32() as u64 }
|
||||
#[inline(always)] pure fn to_channel_f32(&self) -> f32 { (*self as f32) / (0xFF as f32) }
|
||||
#[inline(always)] pure fn to_channel_f64(&self) -> f64 { (*self as f64) / (0xFF as f64) }
|
||||
#[inline(always)] pure fn to_channel_float(&self) -> float { (*self as float) / (0xFF as float) }
|
||||
|
||||
#[inline(always)] pure fn inverse(&self) -> u8 { !(*self) }
|
||||
}
|
||||
|
||||
pub impl u16: Channel {
|
||||
#[inline(always)] static pure fn max() -> u16 { 0xFFFF } // 2^16
|
||||
|
||||
#[inline(always)] static pure fn from<T:Channel>(val: T) -> u16 { val.to_channel_u16() }
|
||||
|
||||
#[inline(always)] pure fn to_channel_u8(&self) -> u8 { (*self >> 8) as u8 } // this is the equivalent of `self/256`. Some folks prefer to do `self/257`
|
||||
#[inline(always)] pure fn to_channel_u16(&self) -> u16 { (*self) }
|
||||
#[inline(always)] pure fn to_channel_u32(&self) -> u32 { (*self as u32 << 16) | (*self) as u32 }
|
||||
#[inline(always)] pure fn to_channel_u64(&self) -> u64 { (self.to_channel_u32() as u64 << 32) | self.to_channel_u32() as u64 }
|
||||
#[inline(always)] pure fn to_channel_f32(&self) -> f32 { (*self) / 0xFFFF as f32 }
|
||||
#[inline(always)] pure fn to_channel_f64(&self) -> f64 { (*self) / 0xFFFF as f64 }
|
||||
#[inline(always)] pure fn to_channel_float(&self) -> float { (*self) / 0xFFFF as float }
|
||||
|
||||
#[inline(always)] pure fn inverse(&self) -> u16 { !(*self) }
|
||||
}
|
||||
|
||||
pub impl u32: Channel {
|
||||
#[inline(always)] static pure fn max() -> u32 { 0xFFFF_FFFF } // 2^32
|
||||
|
||||
#[inline(always)] static pure fn from<T:Channel>(val: T) -> u32 { val.to_channel_u32() }
|
||||
|
||||
#[inline(always)] pure fn to_channel_u8(&self) -> u8 { (*self >> 24) as u8 }
|
||||
#[inline(always)] pure fn to_channel_u16(&self) -> u16 { (*self >> 16) as u16 }
|
||||
#[inline(always)] pure fn to_channel_u32(&self) -> u32 { (*self) }
|
||||
#[inline(always)] pure fn to_channel_u64(&self) -> u64 { (*self as u64 << 32) | (*self) as u64 }
|
||||
#[inline(always)] pure fn to_channel_f32(&self) -> f32 { (*self) / 0xFFFF_FFFF as f32 }
|
||||
#[inline(always)] pure fn to_channel_f64(&self) -> f64 { (*self) / 0xFFFF_FFFF as f64 }
|
||||
#[inline(always)] pure fn to_channel_float(&self) -> float { (*self) / 0xFFFF_FFFF as float }
|
||||
|
||||
#[inline(always)] pure fn inverse(&self) -> u32 { !(*self) }
|
||||
}
|
||||
|
||||
pub impl u64: Channel {
|
||||
#[inline(always)] static pure fn max() -> u64 { 0xFFFF_FFFF_FFFF_FFFF_u64 } // 2^64
|
||||
|
||||
#[inline(always)] static pure fn from<T:Channel>(val: T) -> u64 { val.to_channel_u64() }
|
||||
|
||||
#[inline(always)] pure fn to_channel_u8(&self) -> u8 { (*self >> 56) as u8 }
|
||||
#[inline(always)] pure fn to_channel_u16(&self) -> u16 { (*self >> 48) as u16 }
|
||||
#[inline(always)] pure fn to_channel_u32(&self) -> u32 { (*self >> 32) as u32 }
|
||||
#[inline(always)] pure fn to_channel_u64(&self) -> u64 { (*self) }
|
||||
#[inline(always)] pure fn to_channel_f32(&self) -> f32 { (*self) / 0xFFFF_FFFF_FFFF_FFFF_u64 as f32 }
|
||||
#[inline(always)] pure fn to_channel_f64(&self) -> f64 { (*self) / 0xFFFF_FFFF_FFFF_FFFF_u64 as f64 }
|
||||
#[inline(always)] pure fn to_channel_float(&self) -> float { (*self) / 0xFFFF_FFFF_FFFF_FFFF_u64 as float }
|
||||
|
||||
#[inline(always)] pure fn inverse(&self) -> u64 { !(*self) }
|
||||
}
|
||||
|
||||
pub impl f32: Channel {
|
||||
#[inline(always)] static pure fn max() -> f32 { 1f32 }
|
||||
|
||||
#[inline(always)] static pure fn from<T:Channel>(val: T) -> f32 { val.to_channel_f32() }
|
||||
|
||||
|
||||
#[inline(always)] pure fn to_channel_u8(&self) -> u8 { (*self) * (0xFF_u8 as f32) as u8 }
|
||||
#[inline(always)] pure fn to_channel_u16(&self) -> u16 { (*self) * (0xFFFF_u16 as f32) as u16 }
|
||||
#[inline(always)] pure fn to_channel_u32(&self) -> u32 { fail(~"to_channel_u32 not yet implemented for f32") }
|
||||
#[inline(always)] pure fn to_channel_u64(&self) -> u64 { fail(~"to_channel_u64 not yet implemented for f32") }
|
||||
#[inline(always)] pure fn to_channel_f32(&self) -> f32 { (*self) }
|
||||
#[inline(always)] pure fn to_channel_f64(&self) -> f64 { (*self) as f64 }
|
||||
#[inline(always)] pure fn to_channel_float(&self) -> float { (*self) as float }
|
||||
|
||||
#[inline(always)] pure fn inverse(&self) -> f32 { 1f32 - (*self) }
|
||||
}
|
||||
|
||||
pub impl f64: Channel {
|
||||
#[inline(always)] static pure fn max() -> f64 { 1f64 }
|
||||
|
||||
#[inline(always)] static pure fn from<T:Channel>(val: T) -> f64 { val.to_channel_f64() }
|
||||
|
||||
#[inline(always)] pure fn to_channel_u8(&self) -> u8 { (*self) * (0xFF_u8 as f64) as u8 }
|
||||
#[inline(always)] pure fn to_channel_u16(&self) -> u16 { (*self) * (0xFFFF_u16 as f64) as u16 }
|
||||
#[inline(always)] pure fn to_channel_u32(&self) -> u32 { fail(~"to_channel_u32 not yet implemented for f64") }
|
||||
#[inline(always)] pure fn to_channel_u64(&self) -> u64 { fail(~"to_channel_u64 not yet implemented for f64") }
|
||||
#[inline(always)] pure fn to_channel_f32(&self) -> f32 { (*self) as f32 }
|
||||
#[inline(always)] pure fn to_channel_f64(&self) -> f64 { (*self) }
|
||||
#[inline(always)] pure fn to_channel_float(&self) -> float { (*self) as float }
|
||||
|
||||
#[inline(always)] pure fn inverse(&self) -> f64 { 1f64 - (*self) }
|
||||
}
|
||||
|
||||
pub impl float: Channel {
|
||||
#[inline(always)] static pure fn max() -> float { 1f }
|
||||
|
||||
#[inline(always)] static pure fn from<T:Channel>(val: T) -> float { val.to_channel_float() }
|
||||
|
||||
#[inline(always)] pure fn to_channel_u8(&self) -> u8 { (*self) * (0xFF_u8 as float) as u8 }
|
||||
#[inline(always)] pure fn to_channel_u16(&self) -> u16 { (*self) * (0xFFFF_u16 as float) as u16 }
|
||||
#[inline(always)] pure fn to_channel_u32(&self) -> u32 { fail(~"to_channel_u32 not yet implemented for float") }
|
||||
#[inline(always)] pure fn to_channel_u64(&self) -> u64 { fail(~"to_channel_u64 not yet implemented for float") }
|
||||
#[inline(always)] pure fn to_channel_f32(&self) -> f32 { (*self) as f32 }
|
||||
#[inline(always)] pure fn to_channel_f64(&self) -> f64 { (*self) as f64 }
|
||||
#[inline(always)] pure fn to_channel_float(&self) -> float { (*self) }
|
||||
|
||||
#[inline(always)] pure fn inverse(&self) -> float { 1f - (*self) }
|
||||
}
|
|
@ -1,823 +0,0 @@
|
|||
use core::cast::transmute;
|
||||
use core::cmp::Eq;
|
||||
use core::ptr::to_unsafe_ptr;
|
||||
use core::sys::size_of;
|
||||
use core::vec::raw::buf_as_slice;
|
||||
|
||||
use std::cmp::FuzzyEq;
|
||||
|
||||
use angle::Degrees;
|
||||
use channel::Channel;
|
||||
use funs::common::Sign;
|
||||
use num::types::{Float, Number};
|
||||
|
||||
/**
|
||||
* A generic color trait.
|
||||
*/
|
||||
pub trait Color<T>: Index<uint, T> Eq {
|
||||
/**
|
||||
* # Return value
|
||||
*
|
||||
* The color with each component inverted
|
||||
*/
|
||||
pure fn inverse(&self) -> self;
|
||||
|
||||
/**
|
||||
* Convert the color to a `RGB<u8>`
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* The color as a `RGB<u8>` color with components in the range of
|
||||
* `0x00u8` to `0xFFu8`
|
||||
*/
|
||||
pure fn to_rgb_u8(&self) -> RGB<u8>;
|
||||
|
||||
/**
|
||||
* Convert the color to a `RGB<u16>`
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* The color as a `RGB<u16>` color with components in the range of
|
||||
* `0x0000u16` to `0xFFFFu16`
|
||||
*/
|
||||
pure fn to_rgb_u16(&self) -> RGB<u16>;
|
||||
|
||||
/**
|
||||
* Convert the color to a `RGB<u32>`
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* The color as a `RGB<u32>` color with components in the range of
|
||||
* `0x0000_0000_u32` to `0xFFFF_FFFF_u32`
|
||||
*/
|
||||
pure fn to_rgb_u32(&self) -> RGB<u32>;
|
||||
|
||||
/**
|
||||
* Convert the color to a `RGB<u64>`
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* The color as a `RGB<u32>` color with components in the range of
|
||||
* `0x0000_0000_u32` to `0xFFFF_FFFF_u32`
|
||||
*/
|
||||
pure fn to_rgb_u64(&self) -> RGB<u64>;
|
||||
|
||||
/**
|
||||
* Convert the color to a `RGB<f32>`
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* The color as a `RGB<f32>` color with components in the range of
|
||||
* `0f32` to `1f32`
|
||||
*/
|
||||
pure fn to_rgb_f32(&self) -> RGB<f32>;
|
||||
|
||||
/**
|
||||
* Convert the color to a `RGB<f64>`
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* The color as a `RGB<f64>` color with components in the range of
|
||||
* `0f64` to `1f64`
|
||||
*/
|
||||
pure fn to_rgb_f64(&self) -> RGB<f64>;
|
||||
|
||||
|
||||
/**
|
||||
* Convert the color to a `HSV<f32>`
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* The color as a `HSV<f32>` with the `h` component as a `Degrees<f32>`
|
||||
* angle type, and saturation and value components in the range of `0f32`
|
||||
* to `1f32`.
|
||||
*/
|
||||
pure fn to_hsv_f32(&self) -> HSV<f32>;
|
||||
|
||||
/**
|
||||
* Convert the color to a `HSV<f64>`
|
||||
*
|
||||
* # Returns
|
||||
*
|
||||
* The color as a `HSV<f64>` with the `h` component as a `Degrees<f64>`
|
||||
* angle type, and saturation and value components in the range of `0f64`
|
||||
* to `1f64`.
|
||||
*/
|
||||
pure fn to_hsv_f64(&self) -> HSV<f64>;
|
||||
|
||||
/**
|
||||
* # Return value
|
||||
*
|
||||
* A pointer to the first component of the color
|
||||
*/
|
||||
pure fn to_ptr(&self) -> *T;
|
||||
}
|
||||
|
||||
pub trait MutableColor<T>: Color<T> {
|
||||
/**
|
||||
* Get a mutable reference to the component at `i`
|
||||
*/
|
||||
fn index_mut(&mut self, i: uint) -> &self/mut T;
|
||||
|
||||
/**
|
||||
* Swap two components of the color in place
|
||||
*/
|
||||
fn swap(&mut self, a: uint, b: uint);
|
||||
|
||||
/**
|
||||
* Invert each component of the color
|
||||
*/
|
||||
fn invert_self(&mut self);
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic three-component color
|
||||
*/
|
||||
pub trait Color3<T>: Color<T> {
|
||||
// TODO: documentation (bleh, so much writing)
|
||||
pure fn to_rgba_u8(&self, a: u8) -> RGBA<u8>;
|
||||
pure fn to_rgba_u16(&self, a: u16) -> RGBA<u16>;
|
||||
pure fn to_rgba_u32(&self, a: u32) -> RGBA<u32>;
|
||||
pure fn to_rgba_u64(&self, a: u64) -> RGBA<u64>;
|
||||
pure fn to_rgba_f32(&self, a: f32) -> RGBA<f32>;
|
||||
pure fn to_rgba_f64(&self, a: f64) -> RGBA<f64>;
|
||||
|
||||
pure fn to_hsva_f32(&self, a: f32) -> HSVA<f32>;
|
||||
pure fn to_hsva_f64(&self, a: f64) -> HSVA<f64>;
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic four-component color, the last component being an alpha channel
|
||||
*/
|
||||
pub trait Color4<T>: Color<T> {
|
||||
// TODO: documentation (arrg...)
|
||||
pure fn to_rgba_u8(&self) -> RGBA<u8>;
|
||||
pure fn to_rgba_u16(&self) -> RGBA<u16>;
|
||||
pure fn to_rgba_u32(&self) -> RGBA<u32>;
|
||||
pure fn to_rgba_u64(&self) -> RGBA<u64>;
|
||||
pure fn to_rgba_f32(&self) -> RGBA<f32>;
|
||||
pure fn to_rgba_f64(&self) -> RGBA<f64>;
|
||||
|
||||
pure fn to_hsva_f32(&self) -> HSVA<f32>;
|
||||
pure fn to_hsva_f64(&self) -> HSVA<f64>;
|
||||
}
|
||||
|
||||
// TODO!!!
|
||||
// pub trait ColorRGB<T> {
|
||||
// static pure fn from_hex(hex: u8) -> self;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* RGB to HSV conversion
|
||||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn to_hsv<T:Copy Float>(color: &RGB<T>) -> HSV<T> {
|
||||
// Algorithm taken from the Wikipedia article on HSL and HSV:
|
||||
// http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSV
|
||||
|
||||
let _0 = Number::from(0f);
|
||||
|
||||
let mx = [color.r, color.g, color.b].max();
|
||||
let mn = [color.r, color.g, color.b].min();
|
||||
let chr = mx - mn;
|
||||
|
||||
if chr != Number::from(0f) {
|
||||
let h = Degrees(
|
||||
if color.r == mx { ((color.g - color.b) / chr) % Number::from(6f) }
|
||||
else if color.g == mx { ((color.b - color.r) / chr) + Number::from(2f) }
|
||||
else /* color.b == mx */{ ((color.r - color.g) / chr) + Number::from(4f) }
|
||||
* Number::from(60f));
|
||||
|
||||
let s = chr / mx;
|
||||
|
||||
HSV::new(h, s, mx)
|
||||
|
||||
} else {
|
||||
HSV::new(Degrees(_0), _0, mx)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* HSV to RGB conversion
|
||||
*/
|
||||
#[inline(always)]
|
||||
pub pure fn to_rgb<T:Copy Float Sign>(color: &HSV<T>) -> RGB<T> {
|
||||
// Algorithm taken from the Wikipedia article on HSL and HSV:
|
||||
// http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSV
|
||||
|
||||
let _0: T = Number::from(0f);
|
||||
let _1: T = Number::from(1f);
|
||||
let _2: T = Number::from(2f);
|
||||
|
||||
let chr = color.v * color.s;
|
||||
let h_ = (* color.h) / Number::from(60f); // TODO: it'd be nice if Degrees / Degrees returned a scalar
|
||||
|
||||
// the 2nd largest component
|
||||
let x = chr * (_1 - ((h_ % _2) - _1).abs());
|
||||
|
||||
|
||||
let mut color_rgb =
|
||||
if h_ < Number::from(1f) { RGB::new(chr, x, _0) }
|
||||
else if h_ < Number::from(2f) { RGB::new( x, chr, _0) }
|
||||
else if h_ < Number::from(3f) { RGB::new( _0, chr, x) }
|
||||
else if h_ < Number::from(4f) { RGB::new( _0, x, chr) }
|
||||
else if h_ < Number::from(5f) { RGB::new( x, _0, chr) }
|
||||
else if h_ < Number::from(6f) { RGB::new(chr, _0, x) }
|
||||
else { RGB::new( _0, _0, _0) };
|
||||
|
||||
// match the value by adding the same amount to each component
|
||||
let mn = color.v - chr;
|
||||
|
||||
color_rgb.r += mn;
|
||||
color_rgb.g += mn;
|
||||
color_rgb.b += mn;
|
||||
|
||||
return color_rgb;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A RGB color type (red, green, blue)
|
||||
*
|
||||
* # Type parameters
|
||||
*
|
||||
* * `T` - A color component which should be one of the following primitive
|
||||
* types: `u8`, `u16`, `u32`, `u64`, `f32` or `f64`.
|
||||
*
|
||||
* # Fields
|
||||
*
|
||||
* * `r` - the red component
|
||||
* * `g` - the green component
|
||||
* * `b` - the blue component
|
||||
*/
|
||||
pub struct RGB<T> { r: T, g: T, b: T }
|
||||
|
||||
pub impl<T:Copy> RGB<T> {
|
||||
#[inline(always)]
|
||||
static pure fn new(r: T, g: T, b: T) -> RGB<T> {
|
||||
RGB { r: move r, g: move g, b: move b }
|
||||
}
|
||||
}
|
||||
|
||||
pub impl<T:Copy Number Channel> RGB<T>: Index<uint, T> {
|
||||
#[inline(always)]
|
||||
pure fn index(&self, i: uint) -> T {
|
||||
unsafe { do buf_as_slice(self.to_ptr(), 3) |slice| { slice[i] } }
|
||||
}
|
||||
}
|
||||
|
||||
pub impl<T:Copy Number Channel> RGB<T>: Color<T> {
|
||||
#[inline(always)]
|
||||
pure fn inverse(&self) -> RGB<T> {
|
||||
RGB::new(self.r.inverse(),
|
||||
self.g.inverse(),
|
||||
self.b.inverse())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pure fn to_rgb_u8(&self) -> RGB<u8> {
|
||||
RGB::new(self.r.to_channel_u8(),
|
||||
self.g.to_channel_u8(),
|
||||
self.b.to_channel_u8())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pure fn to_rgb_u16(&self) -> RGB<u16> {
|
||||
RGB::new(self.r.to_channel_u16(),
|
||||
self.g.to_channel_u16(),
|
||||
self.b.to_channel_u16())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pure fn to_rgb_u32(&self) -> RGB<u32> {
|
||||
RGB::new(self.r.to_channel_u32(),
|
||||
self.g.to_channel_u32(),
|
||||
self.b.to_channel_u32())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pure fn to_rgb_u64(&self) -> RGB<u64> {
|
||||
RGB::new(self.r.to_channel_u64(),
|
||||
self.g.to_channel_u64(),
|
||||
self.b.to_channel_u64())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pure fn to_rgb_f32(&self) -> RGB<f32> {
|
||||
RGB::new(self.r.to_channel_f32(),
|
||||
self.g.to_channel_f32(),
|
||||
self.b.to_channel_f32())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pure fn to_rgb_f64(&self) -> RGB<f64> {
|
||||
RGB::new(self.r.to_channel_f64(),
|
||||
self.g.to_channel_f64(),
|
||||
self.b.to_channel_f64())
|
||||
}
|
||||
|
||||
#[inline(always)] pure fn to_hsv_f32(&self) -> HSV<f32> { to_hsv(&self.to_rgb_f32()) }
|
||||
#[inline(always)] pure fn to_hsv_f64(&self) -> HSV<f64> { to_hsv(&self.to_rgb_f64()) }
|
||||
|
||||
#[inline(always)]
|
||||
pure fn to_ptr(&self) -> *T {
|
||||
unsafe {
|
||||
transmute::<*RGB<T>, *T>(
|
||||
to_unsafe_ptr(self)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub impl<T:Copy Channel> RGB<T>: MutableColor<T> {
|
||||
#[inline(always)]
|
||||
fn index_mut(&mut self, i: uint) -> &self/mut T {
|
||||
match i {
|
||||
0 => &mut self.r,
|
||||
1 => &mut self.g,
|
||||
2 => &mut self.b,
|
||||
_ => fail(fmt!("index out of bounds: expected an index from 0 to 2, but found %u", i))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn swap(&mut self, a: uint, b: uint) {
|
||||
util::swap(self.index_mut(a),
|
||||
self.index_mut(b));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn invert_self(&mut self) {
|
||||
self.r = self.r.inverse();
|
||||
self.g = self.g.inverse();
|
||||
self.b = self.b.inverse();
|
||||
}
|
||||
}
|
||||
|
||||
pub impl<T:Copy Number Channel> RGB<T>: Color3<T> {
|
||||
#[inline(always)] pure fn to_rgba_u8(&self, a: u8) -> RGBA<u8> { RGBA::from_rgb_a(&self.to_rgb_u8(), a) }
|
||||
#[inline(always)] pure fn to_rgba_u16(&self, a: u16) -> RGBA<u16> { RGBA::from_rgb_a(&self.to_rgb_u16(), a) }
|
||||
#[inline(always)] pure fn to_rgba_u32(&self, a: u32) -> RGBA<u32> { RGBA::from_rgb_a(&self.to_rgb_u32(), a) }
|
||||
#[inline(always)] pure fn to_rgba_u64(&self, a: u64) -> RGBA<u64> { RGBA::from_rgb_a(&self.to_rgb_u64(), a) }
|
||||
#[inline(always)] pure fn to_rgba_f32(&self, a: f32) -> RGBA<f32> { RGBA::from_rgb_a(&self.to_rgb_f32(), a) }
|
||||
#[inline(always)] pure fn to_rgba_f64(&self, a: f64) -> RGBA<f64> { RGBA::from_rgb_a(&self.to_rgb_f64(), a) }
|
||||
|
||||
#[inline(always)] pure fn to_hsva_f32(&self, a: f32) -> HSVA<f32> { HSVA::from_hsv_a(&self.to_hsv_f32(), a) }
|
||||
#[inline(always)] pure fn to_hsva_f64(&self, a: f64) -> HSVA<f64> { HSVA::from_hsv_a(&self.to_hsv_f64(), a) }
|
||||
}
|
||||
|
||||
pub impl<T:Copy Eq> RGB<T>: Eq {
|
||||
pure fn eq(&self, other: &RGB<T>) -> bool {
|
||||
self.r == other.r &&
|
||||
self.g == other.g &&
|
||||
self.b == other.b
|
||||
}
|
||||
|
||||
pure fn ne(&self, other: &RGB<T>) -> bool {
|
||||
!(self == other)
|
||||
}
|
||||
}
|
||||
|
||||
pub impl<T:Copy Float> RGB<T>: FuzzyEq {
|
||||
#[inline(always)]
|
||||
pure fn fuzzy_eq(other: &RGB<T>) -> bool {
|
||||
self.r.fuzzy_eq(&other.r) &&
|
||||
self.g.fuzzy_eq(&other.g) &&
|
||||
self.b.fuzzy_eq(&other.b)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A RGBA color type (red, green, blue, alpha)
|
||||
*
|
||||
* # Type parameters
|
||||
*
|
||||
* * `T` - A color component which should be one of the following primitive
|
||||
* types: `u8`, `u16`, `u32`, `u64`, `f32` or `f64`.
|
||||
*
|
||||
* # Fields
|
||||
*
|
||||
* * `r` - the red component
|
||||
* * `g` - the green component
|
||||
* * `b` - the blue component
|
||||
* * `a` - the alpha component
|
||||
*/
|
||||
pub struct RGBA<T> { r: T, g: T, b: T, a: T }
|
||||
|
||||
pub impl<T:Copy> RGBA<T> {
|
||||
#[inline(always)]
|
||||
static pure fn new(r: T, g: T, b: T, a: T) -> RGBA<T> {
|
||||
RGBA { r: move r, g: move g, b: move b, a: move a }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
static pure fn from_rgb_a(rgb: &RGB<T>, a: T) -> RGBA<T> {
|
||||
RGBA::new(rgb.r, rgb.g, rgb.b, move a)
|
||||
}
|
||||
}
|
||||
|
||||
pub impl<T:Copy Number Channel> RGBA<T>: Index<uint, T> {
|
||||
#[inline(always)]
|
||||
pure fn index(&self, i: uint) -> T {
|
||||
unsafe { do buf_as_slice(self.to_ptr(), 4) |slice| { slice[i] } }
|
||||
}
|
||||
}
|
||||
|
||||
pub impl<T:Copy Number Channel> RGBA<T>: Color<T> {
|
||||
#[inline(always)]
|
||||
pure fn inverse(&self) -> RGBA<T> {
|
||||
RGBA::new(self.r.inverse(),
|
||||
self.g.inverse(),
|
||||
self.b.inverse(),
|
||||
self.a.inverse())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pure fn to_rgb_u8(&self) -> RGB<u8> {
|
||||
RGB::new(self.r.to_channel_u8(),
|
||||
self.g.to_channel_u8(),
|
||||
self.b.to_channel_u8())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pure fn to_rgb_u16(&self) -> RGB<u16> {
|
||||
RGB::new(self.r.to_channel_u16(),
|
||||
self.g.to_channel_u16(),
|
||||
self.b.to_channel_u16())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pure fn to_rgb_u32(&self) -> RGB<u32> {
|
||||
RGB::new(self.r.to_channel_u32(),
|
||||
self.g.to_channel_u32(),
|
||||
self.b.to_channel_u32())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pure fn to_rgb_u64(&self) -> RGB<u64> {
|
||||
RGB::new(self.r.to_channel_u64(),
|
||||
self.g.to_channel_u64(),
|
||||
self.b.to_channel_u64())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pure fn to_rgb_f32(&self) -> RGB<f32> {
|
||||
RGB::new(self.r.to_channel_f32(),
|
||||
self.g.to_channel_f32(),
|
||||
self.b.to_channel_f32())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pure fn to_rgb_f64(&self) -> RGB<f64> {
|
||||
RGB::new(self.r.to_channel_f64(),
|
||||
self.g.to_channel_f64(),
|
||||
self.b.to_channel_f64())
|
||||
}
|
||||
|
||||
#[inline(always)] pure fn to_hsv_f32(&self) -> HSV<f32> { to_hsv(&self.to_rgb_f32()) }
|
||||
#[inline(always)] pure fn to_hsv_f64(&self) -> HSV<f64> { to_hsv(&self.to_rgb_f64()) }
|
||||
|
||||
#[inline(always)]
|
||||
pure fn to_ptr(&self) -> *T {
|
||||
unsafe {
|
||||
transmute::<*RGBA<T>, *T>(
|
||||
to_unsafe_ptr(self)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub impl<T:Copy Channel> RGBA<T>: MutableColor<T> {
|
||||
#[inline(always)]
|
||||
fn index_mut(&mut self, i: uint) -> &self/mut T {
|
||||
match i {
|
||||
0 => &mut self.r,
|
||||
1 => &mut self.g,
|
||||
2 => &mut self.b,
|
||||
3 => &mut self.a,
|
||||
_ => fail(fmt!("index out of bounds: expected an index from 0 to 2, but found %u", i))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn swap(&mut self, a: uint, b: uint) {
|
||||
util::swap(self.index_mut(a),
|
||||
self.index_mut(b));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn invert_self(&mut self) {
|
||||
self.r = self.r.inverse();
|
||||
self.g = self.g.inverse();
|
||||
self.b = self.b.inverse();
|
||||
self.a = self.a.inverse();
|
||||
}
|
||||
}
|
||||
|
||||
pub impl<T:Copy Number Channel> RGBA<T>: Color4<T> {
|
||||
#[inline(always)] pure fn to_rgba_u8(&self) -> RGBA<u8> { RGBA::from_rgb_a(&self.to_rgb_u8(), self.a.to_channel_u8()) }
|
||||
#[inline(always)] pure fn to_rgba_u16(&self) -> RGBA<u16> { RGBA::from_rgb_a(&self.to_rgb_u16(), self.a.to_channel_u16()) }
|
||||
#[inline(always)] pure fn to_rgba_u32(&self) -> RGBA<u32> { RGBA::from_rgb_a(&self.to_rgb_u32(), self.a.to_channel_u32()) }
|
||||
#[inline(always)] pure fn to_rgba_u64(&self) -> RGBA<u64> { RGBA::from_rgb_a(&self.to_rgb_u64(), self.a.to_channel_u64()) }
|
||||
#[inline(always)] pure fn to_rgba_f32(&self) -> RGBA<f32> { RGBA::from_rgb_a(&self.to_rgb_f32(), self.a.to_channel_f32()) }
|
||||
#[inline(always)] pure fn to_rgba_f64(&self) -> RGBA<f64> { RGBA::from_rgb_a(&self.to_rgb_f64(), self.a.to_channel_f64()) }
|
||||
|
||||
#[inline(always)] pure fn to_hsva_f32(&self) -> HSVA<f32> { HSVA::from_hsv_a(&self.to_hsv_f32(), self.a.to_channel_f32()) }
|
||||
#[inline(always)] pure fn to_hsva_f64(&self) -> HSVA<f64> { HSVA::from_hsv_a(&self.to_hsv_f64(), self.a.to_channel_f64()) }
|
||||
}
|
||||
|
||||
pub impl<T:Copy Eq> RGBA<T>: Eq {
|
||||
pure fn eq(&self, other: &RGBA<T>) -> bool {
|
||||
self.r == other.r &&
|
||||
self.g == other.g &&
|
||||
self.b == other.b &&
|
||||
self.a == other.a
|
||||
}
|
||||
|
||||
pure fn ne(&self, other: &RGBA<T>) -> bool {
|
||||
!(self == other)
|
||||
}
|
||||
}
|
||||
|
||||
pub impl<T:Copy Float> RGBA<T>: FuzzyEq {
|
||||
#[inline(always)]
|
||||
pure fn fuzzy_eq(other: &RGBA<T>) -> bool {
|
||||
self.r.fuzzy_eq(&other.r) &&
|
||||
self.g.fuzzy_eq(&other.g) &&
|
||||
self.b.fuzzy_eq(&other.b) &&
|
||||
self.a.fuzzy_eq(&other.a)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A HSV color type (hue, saturation, value)
|
||||
*
|
||||
* # Type parameters
|
||||
*
|
||||
* * `T` - A color component which should be either an `f32` or `f64`.
|
||||
*
|
||||
* # Fields
|
||||
*
|
||||
* * `h` - the hue component in degrees (from 0.0 to 360.0)
|
||||
* * `s` - the saturation component
|
||||
* * `v` - the value (brightness) component
|
||||
*/
|
||||
pub struct HSV<T> { h: Degrees<T>, s: T, v: T }
|
||||
|
||||
pub impl<T:Copy> HSV<T> {
|
||||
static pure fn new(h: Degrees<T>, s: T, v: T) -> HSV<T> {
|
||||
HSV { h: move h, s: move s, v: move v }
|
||||
}
|
||||
}
|
||||
|
||||
pub impl<T:Copy Float Channel> HSV<T>: Index<uint, T> {
|
||||
#[inline(always)]
|
||||
pure fn index(&self, i: uint) -> T {
|
||||
unsafe { do buf_as_slice(self.to_ptr(), 3) |slice| { slice[i] } }
|
||||
}
|
||||
}
|
||||
|
||||
pub impl<T:Copy Float Channel> HSV<T>: Color<T> {
|
||||
#[inline(always)]
|
||||
pure fn inverse(&self) -> HSV<T> {
|
||||
HSV::new(self.h.opposite(),
|
||||
self.s.inverse(),
|
||||
self.v.inverse())
|
||||
}
|
||||
|
||||
#[inline(always)] pure fn to_rgb_u8(&self) -> RGB<u8> { to_rgb(&self.to_hsv_f32()).to_rgb_u8() }
|
||||
#[inline(always)] pure fn to_rgb_u16(&self) -> RGB<u16> { to_rgb(&self.to_hsv_f32()).to_rgb_u16() }
|
||||
#[inline(always)] pure fn to_rgb_u32(&self) -> RGB<u32> { to_rgb(&self.to_hsv_f32()).to_rgb_u32() }
|
||||
#[inline(always)] pure fn to_rgb_u64(&self) -> RGB<u64> { to_rgb(&self.to_hsv_f32()).to_rgb_u64() }
|
||||
#[inline(always)] pure fn to_rgb_f32(&self) -> RGB<f32> { to_rgb(&self.to_hsv_f32()).to_rgb_f32() }
|
||||
#[inline(always)] pure fn to_rgb_f64(&self) -> RGB<f64> { to_rgb(&self.to_hsv_f64()).to_rgb_f64() }
|
||||
|
||||
#[inline(always)]
|
||||
pure fn to_hsv_f32(&self) -> HSV<f32> {
|
||||
HSV::new(Degrees((*self.h).to_f32()),
|
||||
self.s.to_channel_f32(),
|
||||
self.v.to_channel_f32())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pure fn to_hsv_f64(&self) -> HSV<f64> {
|
||||
HSV::new(Degrees((*self.h).to_f64()),
|
||||
self.s.to_channel_f64(),
|
||||
self.v.to_channel_f64())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pure fn to_ptr(&self) -> *T {
|
||||
unsafe {
|
||||
transmute::<*HSV<T>, *T>(
|
||||
to_unsafe_ptr(self)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub impl<T:Copy Channel Float> HSV<T>: MutableColor<T> {
|
||||
#[inline(always)]
|
||||
fn index_mut(&mut self, i: uint) -> &self/mut T {
|
||||
match i {
|
||||
0 => fail(~"can't swap the hue component at index 0 in a HSVA type"),
|
||||
1 => &mut self.s,
|
||||
2 => &mut self.v,
|
||||
_ => fail(fmt!("index out of bounds: expected an index from 0 to 2, but found %u", i))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn swap(&mut self, a: uint, b: uint) {
|
||||
if a != 0 && b != 0 { fail(fmt!("can't swap the hue component (at index 0) in a HSV type: found a: %u, b: %u", a, b)); }
|
||||
util::swap(self.index_mut(a),
|
||||
self.index_mut(b));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn invert_self(&mut self) {
|
||||
self.h = self.h.opposite();
|
||||
self.s = self.s.inverse();
|
||||
self.v = self.v.inverse();
|
||||
}
|
||||
}
|
||||
|
||||
pub impl<T:Copy Float Channel> HSV<T>: Color3<T> {
|
||||
#[inline(always)] pure fn to_rgba_u8(&self, a: u8) -> RGBA<u8> { RGBA::from_rgb_a(&self.to_rgb_u8(), a) }
|
||||
#[inline(always)] pure fn to_rgba_u16(&self, a: u16) -> RGBA<u16> { RGBA::from_rgb_a(&self.to_rgb_u16(), a) }
|
||||
#[inline(always)] pure fn to_rgba_u32(&self, a: u32) -> RGBA<u32> { RGBA::from_rgb_a(&self.to_rgb_u32(), a) }
|
||||
#[inline(always)] pure fn to_rgba_u64(&self, a: u64) -> RGBA<u64> { RGBA::from_rgb_a(&self.to_rgb_u64(), a) }
|
||||
#[inline(always)] pure fn to_rgba_f32(&self, a: f32) -> RGBA<f32> { RGBA::from_rgb_a(&self.to_rgb_f32(), a) }
|
||||
#[inline(always)] pure fn to_rgba_f64(&self, a: f64) -> RGBA<f64> { RGBA::from_rgb_a(&self.to_rgb_f64(), a) }
|
||||
|
||||
#[inline(always)] pure fn to_hsva_f32(&self, a: f32) -> HSVA<f32> { HSVA::from_hsv_a(&self.to_hsv_f32(), a) }
|
||||
#[inline(always)] pure fn to_hsva_f64(&self, a: f64) -> HSVA<f64> { HSVA::from_hsv_a(&self.to_hsv_f64(), a) }
|
||||
}
|
||||
|
||||
pub impl<T:Copy Float> HSV<T>: Eq {
|
||||
pure fn eq(&self, other: &HSV<T>) -> bool {
|
||||
self.h == other.h &&
|
||||
self.s == other.s &&
|
||||
self.v == other.v
|
||||
}
|
||||
|
||||
pure fn ne(&self, other: &HSV<T>) -> bool {
|
||||
!(self == other)
|
||||
}
|
||||
}
|
||||
|
||||
pub impl<T:Copy Float> HSV<T>: FuzzyEq {
|
||||
#[inline(always)]
|
||||
pure fn fuzzy_eq(other: &HSV<T>) -> bool {
|
||||
self.h.fuzzy_eq(&other.h) &&
|
||||
self.s.fuzzy_eq(&other.s) &&
|
||||
self.v.fuzzy_eq(&other.v)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A HSVA color type (hue, saturation, value, alpha)
|
||||
*
|
||||
* # Type parameters
|
||||
*
|
||||
* * `T` - A color component which should be either an `f32` or `f64`.
|
||||
*
|
||||
* # Fields
|
||||
*
|
||||
* * `h` - the hue component in degrees (from 0.0 to 360.0)
|
||||
* * `s` - the saturation component
|
||||
* * `v` - the value (brightness) component
|
||||
* * `v` - the alpha component
|
||||
*/
|
||||
pub struct HSVA<T> { h: Degrees<T>, s: T, v: T, a: T }
|
||||
|
||||
pub impl<T:Copy> HSVA<T> {
|
||||
#[inline(always)]
|
||||
static pure fn new(h: Degrees<T>, s: T, v: T, a: T) -> HSVA<T> {
|
||||
HSVA { h: move h, s: move s, v: move v, a: move a }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
static pure fn from_hsv_a(hsv: &HSV<T>, a: T) -> HSVA<T> {
|
||||
HSVA::new(hsv.h, hsv.s, hsv.v, move a)
|
||||
}
|
||||
}
|
||||
|
||||
pub impl<T:Copy Float Channel> HSVA<T>: Index<uint, T> {
|
||||
#[inline(always)]
|
||||
pure fn index(&self, i: uint) -> T {
|
||||
unsafe { do buf_as_slice(self.to_ptr(), 4) |slice| { slice[i] } }
|
||||
}
|
||||
}
|
||||
|
||||
pub impl<T:Copy Float Channel> HSVA<T>: Color<T> {
|
||||
#[inline(always)]
|
||||
pure fn inverse(&self) -> HSVA<T> {
|
||||
HSVA::new(self.h.opposite(),
|
||||
self.s.inverse(),
|
||||
self.v.inverse(),
|
||||
self.a.inverse())
|
||||
}
|
||||
|
||||
#[inline(always)] pure fn to_rgb_u8(&self) -> RGB<u8> { to_rgb(&self.to_hsv_f32()).to_rgb_u8() }
|
||||
#[inline(always)] pure fn to_rgb_u16(&self) -> RGB<u16> { to_rgb(&self.to_hsv_f32()).to_rgb_u16() }
|
||||
#[inline(always)] pure fn to_rgb_u32(&self) -> RGB<u32> { to_rgb(&self.to_hsv_f32()).to_rgb_u32() }
|
||||
#[inline(always)] pure fn to_rgb_u64(&self) -> RGB<u64> { to_rgb(&self.to_hsv_f32()).to_rgb_u64() }
|
||||
#[inline(always)] pure fn to_rgb_f32(&self) -> RGB<f32> { to_rgb(&self.to_hsv_f32()).to_rgb_f32() }
|
||||
#[inline(always)] pure fn to_rgb_f64(&self) -> RGB<f64> { to_rgb(&self.to_hsv_f64()).to_rgb_f64() }
|
||||
|
||||
#[inline(always)]
|
||||
pure fn to_hsv_f32(&self) -> HSV<f32> {
|
||||
HSV::new(Degrees((*self.h).to_f32()),
|
||||
self.s.to_channel_f32(),
|
||||
self.v.to_channel_f32())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pure fn to_hsv_f64(&self) -> HSV<f64> {
|
||||
HSV::new(Degrees((*self.h).to_f64()),
|
||||
self.s.to_channel_f64(),
|
||||
self.v.to_channel_f64())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pure fn to_ptr(&self) -> *T {
|
||||
unsafe {
|
||||
transmute::<*HSVA<T>, *T>(
|
||||
to_unsafe_ptr(self)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub impl<T:Copy Channel Float> HSVA<T>: MutableColor<T> {
|
||||
#[inline(always)]
|
||||
fn index_mut(&mut self, i: uint) -> &self/mut T {
|
||||
match i {
|
||||
0 => fail(~"can't swap the hue component at index 0 in a HSVA type"),
|
||||
1 => &mut self.s,
|
||||
2 => &mut self.v,
|
||||
3 => &mut self.a,
|
||||
_ => fail(fmt!("index out of bounds: expected an index from 0 to 2, but found %u", i))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn swap(&mut self, a: uint, b: uint) {
|
||||
util::swap(self.index_mut(a),
|
||||
self.index_mut(b));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn invert_self(&mut self) {
|
||||
self.h = self.h.opposite();
|
||||
self.s = self.s.inverse();
|
||||
self.v = self.v.inverse();
|
||||
self.a = self.a.inverse();
|
||||
}
|
||||
}
|
||||
|
||||
pub impl<T:Copy Float Channel> HSVA<T>: Color4<T> {
|
||||
#[inline(always)] pure fn to_rgba_u8(&self) -> RGBA<u8> { RGBA::from_rgb_a(&self.to_rgb_u8(), self.a.to_channel_u8()) }
|
||||
#[inline(always)] pure fn to_rgba_u16(&self) -> RGBA<u16> { RGBA::from_rgb_a(&self.to_rgb_u16(), self.a.to_channel_u16()) }
|
||||
#[inline(always)] pure fn to_rgba_u32(&self) -> RGBA<u32> { RGBA::from_rgb_a(&self.to_rgb_u32(), self.a.to_channel_u32()) }
|
||||
#[inline(always)] pure fn to_rgba_u64(&self) -> RGBA<u64> { RGBA::from_rgb_a(&self.to_rgb_u64(), self.a.to_channel_u64()) }
|
||||
#[inline(always)] pure fn to_rgba_f32(&self) -> RGBA<f32> { RGBA::from_rgb_a(&self.to_rgb_f32(), self.a.to_channel_f32()) }
|
||||
#[inline(always)] pure fn to_rgba_f64(&self) -> RGBA<f64> { RGBA::from_rgb_a(&self.to_rgb_f64(), self.a.to_channel_f64()) }
|
||||
|
||||
#[inline(always)] pure fn to_hsva_f32(&self) -> HSVA<f32> { HSVA::from_hsv_a(&self.to_hsv_f32(), self.a.to_channel_f32()) }
|
||||
#[inline(always)] pure fn to_hsva_f64(&self) -> HSVA<f64> { HSVA::from_hsv_a(&self.to_hsv_f64(), self.a.to_channel_f64()) }
|
||||
}
|
||||
|
||||
pub impl<T:Copy Float> HSVA<T>: Eq {
|
||||
pure fn eq(&self, other: &HSVA<T>) -> bool {
|
||||
self.h == other.h &&
|
||||
self.s == other.s &&
|
||||
self.v == other.v &&
|
||||
self.a == other.a
|
||||
}
|
||||
|
||||
pure fn ne(&self, other: &HSVA<T>) -> bool {
|
||||
!(self == other)
|
||||
}
|
||||
}
|
||||
|
||||
pub impl<T:Copy Float> HSVA<T>: FuzzyEq {
|
||||
#[inline(always)]
|
||||
pure fn fuzzy_eq(other: &HSVA<T>) -> bool {
|
||||
self.h.fuzzy_eq(&other.h) &&
|
||||
self.s.fuzzy_eq(&other.s) &&
|
||||
self.v.fuzzy_eq(&other.v) &&
|
||||
self.a.fuzzy_eq(&other.a)
|
||||
}
|
||||
}
|
|
@ -1,275 +0,0 @@
|
|||
use channel::*;
|
||||
|
||||
#[test]
|
||||
fn test_channel_u8() {
|
||||
assert 0x00_u8.to_channel_u8() == 0x00_u8;
|
||||
assert 0x30_u8.to_channel_u8() == 0x30_u8;
|
||||
assert 0x66_u8.to_channel_u8() == 0x66_u8;
|
||||
assert 0xA0_u8.to_channel_u8() == 0xA0_u8;
|
||||
assert 0xFF_u8.to_channel_u8() == 0xFF_u8;
|
||||
|
||||
assert 0x00_u8.to_channel_u16() == 0x0000_u16;
|
||||
assert 0x30_u8.to_channel_u16() == 0x3030_u16;
|
||||
assert 0x66_u8.to_channel_u16() == 0x6666_u16;
|
||||
assert 0xA0_u8.to_channel_u16() == 0xA0A0_u16;
|
||||
assert 0xFF_u8.to_channel_u16() == 0xFFFF_u16;
|
||||
|
||||
assert 0x00_u8.to_channel_u32() == 0x0000_0000_u32;
|
||||
assert 0x30_u8.to_channel_u32() == 0x3030_3030_u32;
|
||||
assert 0x66_u8.to_channel_u32() == 0x6666_6666_u32;
|
||||
assert 0xA0_u8.to_channel_u32() == 0xA0A0_A0A0_u32;
|
||||
assert 0xFF_u8.to_channel_u32() == 0xFFFF_FFFF_u32;
|
||||
|
||||
assert 0x00_u8.to_channel_u64() == 0x0000_0000_0000_0000_u64;
|
||||
assert 0x30_u8.to_channel_u64() == 0x3030_3030_3030_3030_u64;
|
||||
assert 0x66_u8.to_channel_u64() == 0x6666_6666_6666_6666_u64;
|
||||
assert 0xA0_u8.to_channel_u64() == 0xA0A0_A0A0_A0A0_A0A0_u64;
|
||||
assert 0xFF_u8.to_channel_u64() == 0xFFFF_FFFF_FFFF_FFFF_u64;
|
||||
|
||||
assert 0x00_u8.to_channel_f32() == 0f32;
|
||||
assert 0xFF_u8.to_channel_f32() == 1f32;
|
||||
|
||||
assert 0x00_u8.to_channel_f64() == 0f64;
|
||||
assert 0xFF_u8.to_channel_f64() == 1f64;
|
||||
|
||||
assert 0x00_u8.to_channel_float() == 0f;
|
||||
assert 0xFF_u8.to_channel_float() == 1f;
|
||||
|
||||
|
||||
// Test inverse
|
||||
|
||||
assert 0x00_u8.inverse() == 0xFF_u8;
|
||||
assert 0x66_u8.inverse() == 0x99_u8;
|
||||
assert 0xFF_u8.inverse() == 0x00_u8;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_channel_u16() {
|
||||
assert 0x0000_u16.to_channel_u8() == 0x00_u8;
|
||||
assert 0x3300_u16.to_channel_u8() == 0x33_u8;
|
||||
assert 0x6666_u16.to_channel_u8() == 0x66_u8;
|
||||
assert 0xAA00_u16.to_channel_u8() == 0xAA_u8;
|
||||
assert 0xFFFF_u16.to_channel_u8() == 0xFF_u8;
|
||||
|
||||
assert 0x0000_u16.to_channel_u16() == 0x0000_u16;
|
||||
assert 0x3300_u16.to_channel_u16() == 0x3300_u16;
|
||||
assert 0x6666_u16.to_channel_u16() == 0x6666_u16;
|
||||
assert 0xAA00_u16.to_channel_u16() == 0xAA00_u16;
|
||||
assert 0xFFFF_u16.to_channel_u16() == 0xFFFF_u16;
|
||||
|
||||
assert 0x0000_u16.to_channel_u32() == 0x0000_0000_u32;
|
||||
assert 0x3300_u16.to_channel_u32() == 0x3300_3300_u32;
|
||||
assert 0x6666_u16.to_channel_u32() == 0x6666_6666_u32;
|
||||
assert 0xAA00_u16.to_channel_u32() == 0xAA00_AA00_u32;
|
||||
assert 0xFFFF_u16.to_channel_u32() == 0xFFFF_FFFF_u32;
|
||||
|
||||
assert 0x0000_u16.to_channel_u64() == 0x0000_0000_0000_0000_u64;
|
||||
assert 0x3300_u16.to_channel_u64() == 0x3300_3300_3300_3300_u64;
|
||||
assert 0x6666_u16.to_channel_u64() == 0x6666_6666_6666_6666_u64;
|
||||
assert 0xAA00_u16.to_channel_u64() == 0xAA00_AA00_AA00_AA00_u64;
|
||||
assert 0xFFFF_u16.to_channel_u64() == 0xFFFF_FFFF_FFFF_FFFF_u64;
|
||||
|
||||
assert 0x0000_u16.to_channel_f32() == 0f32;
|
||||
assert 0xFFFF_u16.to_channel_f32() == 1f32;
|
||||
|
||||
assert 0x0000_u16.to_channel_f64() == 0f64;
|
||||
assert 0xFFFF_u16.to_channel_f64() == 1f64;
|
||||
|
||||
assert 0x0000_u16.to_channel_float() == 0f;
|
||||
assert 0xFFFF_u16.to_channel_float() == 1f;
|
||||
|
||||
|
||||
// Test inverse
|
||||
|
||||
assert 0x0000_u16.inverse() == 0xFFFF_u16;
|
||||
assert 0x6666_u16.inverse() == 0x9999_u16;
|
||||
assert 0xFFFF_u16.inverse() == 0x0000_u16;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_channel_u32() {
|
||||
assert 0x0000_0000_u32.to_channel_u8() == 0x00_u8;
|
||||
assert 0x3333_0000_u32.to_channel_u8() == 0x33_u8;
|
||||
assert 0x6666_6666_u32.to_channel_u8() == 0x66_u8;
|
||||
assert 0xAAAA_0000_u32.to_channel_u8() == 0xAA_u8;
|
||||
assert 0xFFFF_FFFF_u32.to_channel_u8() == 0xFF_u8;
|
||||
|
||||
assert 0x0000_0000_u32.to_channel_u16() == 0x0000_u16;
|
||||
assert 0x3333_0000_u32.to_channel_u16() == 0x3333_u16;
|
||||
assert 0x6666_6666_u32.to_channel_u16() == 0x6666_u16;
|
||||
assert 0xAAAA_0000_u32.to_channel_u16() == 0xAAAA_u16;
|
||||
assert 0xFFFF_FFFF_u32.to_channel_u16() == 0xFFFF_u16;
|
||||
|
||||
assert 0x0000_0000_u32.to_channel_u32() == 0x0000_0000_u32;
|
||||
assert 0x3333_0000_u32.to_channel_u32() == 0x3333_0000_u32;
|
||||
assert 0x6666_6666_u32.to_channel_u32() == 0x6666_6666_u32;
|
||||
assert 0xAAAA_0000_u32.to_channel_u32() == 0xAAAA_0000_u32;
|
||||
assert 0xFFFF_FFFF_u32.to_channel_u32() == 0xFFFF_FFFF_u32;
|
||||
|
||||
assert 0x0000_0000_u32.to_channel_u64() == 0x0000_0000_0000_0000_u64;
|
||||
assert 0x3333_0000_u32.to_channel_u64() == 0x3333_0000_3333_0000_u64;
|
||||
assert 0x6666_6666_u32.to_channel_u64() == 0x6666_6666_6666_6666_u64;
|
||||
assert 0xAAAA_0000_u32.to_channel_u64() == 0xAAAA_0000_AAAA_0000_u64;
|
||||
assert 0xFFFF_FFFF_u32.to_channel_u64() == 0xFFFF_FFFF_FFFF_FFFF_u64;
|
||||
|
||||
assert 0x0000_0000_u32.to_channel_f32() == 0f32;
|
||||
assert 0xFFFF_FFFF_u32.to_channel_f32() == 1f32;
|
||||
|
||||
assert 0x0000_0000_u32.to_channel_f64() == 0f64;
|
||||
assert 0xFFFF_FFFF_u32.to_channel_f64() == 1f64;
|
||||
|
||||
assert 0x0000_0000_u32.to_channel_float() == 0f;
|
||||
assert 0xFFFF_FFFF_u32.to_channel_float() == 1f;
|
||||
|
||||
|
||||
// Test inverse
|
||||
|
||||
assert 0x0000_0000_u32.inverse() == 0xFFFF_FFFF_u32;
|
||||
assert 0x6666_6666_u32.inverse() == 0x9999_9999_u32;
|
||||
assert 0xFFFF_FFFF_u32.inverse() == 0x0000_0000_u32;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_channel_u64() {
|
||||
assert 0x0000_0000_0000_0000_u64.to_channel_u8() == 0x00_u8;
|
||||
assert 0x3333_3333_0000_0000_u64.to_channel_u8() == 0x33_u8; // FIXME: color shift?
|
||||
assert 0x6666_6666_6666_6666_u64.to_channel_u8() == 0x66_u8;
|
||||
assert 0xAAAA_AAAA_0000_0000_u64.to_channel_u8() == 0xAA_u8; // FIXME: color shift?
|
||||
assert 0xFFFF_FFFF_FFFF_FFFF_u64.to_channel_u8() == 0xFF_u8;
|
||||
|
||||
assert 0x0000_0000_0000_0000_u64.to_channel_u16() == 0x0000_u16;
|
||||
assert 0x3333_3333_0000_0000_u64.to_channel_u16() == 0x3333_u16; // FIXME: color shift?
|
||||
assert 0x6666_6666_6666_6666_u64.to_channel_u16() == 0x6666_u16;
|
||||
assert 0xAAAA_AAAA_0000_0000_u64.to_channel_u16() == 0xAAAA_u16; // FIXME: color shift?
|
||||
assert 0xFFFF_FFFF_FFFF_FFFF_u64.to_channel_u16() == 0xFFFF_u16;
|
||||
|
||||
assert 0x0000_0000_0000_0000_u64.to_channel_u32() == 0x0000_0000_u32;
|
||||
assert 0x3333_3333_0000_0000_u64.to_channel_u32() == 0x3333_3333_u32; // FIXME: color shift?
|
||||
assert 0x6666_6666_6666_6666_u64.to_channel_u32() == 0x6666_6666_u32;
|
||||
assert 0xAAAA_AAAA_0000_0000_u64.to_channel_u32() == 0xAAAA_AAAA_u32; // FIXME: color shift?
|
||||
assert 0xFFFF_FFFF_FFFF_FFFF_u64.to_channel_u32() == 0xFFFF_FFFF_u32;
|
||||
|
||||
assert 0x0000_0000_0000_0000_u64.to_channel_u64() == 0x0000_0000_0000_0000_u64;
|
||||
assert 0x3333_3333_0000_0000_u64.to_channel_u64() == 0x3333_3333_0000_0000_u64;
|
||||
assert 0x6666_6666_6666_6666_u64.to_channel_u64() == 0x6666_6666_6666_6666_u64;
|
||||
assert 0xAAAA_AAAA_0000_0000_u64.to_channel_u64() == 0xAAAA_AAAA_0000_0000_u64;
|
||||
assert 0xFFFF_FFFF_FFFF_FFFF_u64.to_channel_u64() == 0xFFFF_FFFF_FFFF_FFFF_u64;
|
||||
|
||||
assert 0x0000_0000_0000_0000_u64.to_channel_f32() == 0f32;
|
||||
assert 0xFFFF_FFFF_FFFF_FFFF_u64.to_channel_f32() == 1f32;
|
||||
|
||||
assert 0x0000_0000_0000_0000_u64.to_channel_f64() == 0f64;
|
||||
assert 0xFFFF_FFFF_FFFF_FFFF_u64.to_channel_f64() == 1f64;
|
||||
|
||||
assert 0x0000_0000_0000_0000_u64.to_channel_float() == 0f;
|
||||
assert 0xFFFF_FFFF_FFFF_FFFF_u64.to_channel_float() == 1f;
|
||||
|
||||
|
||||
// Test inverse
|
||||
|
||||
assert 0x0000_0000_0000_0000_u64.inverse() == 0xFFFF_FFFF_FFFF_FFFF_u64;
|
||||
assert 0x6666_6666_6666_6666_u64.inverse() == 0x9999_9999_9999_9999_u64;
|
||||
assert 0xFFFF_FFFF_FFFF_FFFF_u64.inverse() == 0x0000_0000_0000_0000_u64;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_channel_f32() {
|
||||
assert 0.00f32.to_channel_u8() == 0x00;
|
||||
assert 0.25f32.to_channel_u8() == 0x3F;
|
||||
assert 0.50f32.to_channel_u8() == 0x7F;
|
||||
assert 0.75f32.to_channel_u8() == 0xBF;
|
||||
assert 1.00f32.to_channel_u8() == 0xFF;
|
||||
|
||||
assert 0.00f32.to_channel_u16() == 0x0000;
|
||||
assert 0.25f32.to_channel_u16() == 0x3FFF;
|
||||
assert 0.50f32.to_channel_u16() == 0x7FFF;
|
||||
assert 0.75f32.to_channel_u16() == 0xBFFF;
|
||||
assert 1.00f32.to_channel_u16() == 0xFFFF;
|
||||
|
||||
// TODO: test to_channel_u32()
|
||||
|
||||
// TODO: test to_channel_u64()
|
||||
|
||||
// TODO: test to_channel_f32()
|
||||
|
||||
// TODO: test to_channel_f64()
|
||||
|
||||
// TODO: test to_channel_float()
|
||||
|
||||
|
||||
// Test inverse
|
||||
|
||||
assert 0.00f32.inverse() == 1.00f32;
|
||||
assert 0.25f32.inverse() == 0.75f32;
|
||||
assert 0.50f32.inverse() == 0.50f32;
|
||||
assert 0.75f32.inverse() == 0.25f32;
|
||||
assert 1.00f32.inverse() == 0.00f32;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_channel_f64() {
|
||||
assert 0.00f64.to_channel_u8() == 0x00;
|
||||
assert 0.25f64.to_channel_u8() == 0x3F;
|
||||
assert 0.50f64.to_channel_u8() == 0x7F;
|
||||
assert 0.75f64.to_channel_u8() == 0xBF;
|
||||
assert 1.00f64.to_channel_u8() == 0xFF;
|
||||
|
||||
assert 0.00f64.to_channel_u16() == 0x0000;
|
||||
assert 0.25f64.to_channel_u16() == 0x3FFF;
|
||||
assert 0.50f64.to_channel_u16() == 0x7FFF;
|
||||
assert 0.75f64.to_channel_u16() == 0xBFFF;
|
||||
assert 1.00f64.to_channel_u16() == 0xFFFF;
|
||||
|
||||
// TODO: test to_channel_u32()
|
||||
|
||||
// TODO: test to_channel_u64()
|
||||
|
||||
// TODO: test to_channel_f32()
|
||||
|
||||
// TODO: test to_channel_f64()
|
||||
|
||||
// TODO: test to_channel_float()
|
||||
|
||||
|
||||
// Test inverse
|
||||
|
||||
assert 0.00f64.inverse() == 1.00f64;
|
||||
assert 0.25f64.inverse() == 0.75f64;
|
||||
assert 0.50f64.inverse() == 0.50f64;
|
||||
assert 0.75f64.inverse() == 0.25f64;
|
||||
assert 1.00f64.inverse() == 0.00f64;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_channel_float() {
|
||||
assert 0.00f.to_channel_u8() == 0x00;
|
||||
assert 0.25f.to_channel_u8() == 0x3F;
|
||||
assert 0.50f.to_channel_u8() == 0x7F;
|
||||
assert 0.75f.to_channel_u8() == 0xBF;
|
||||
assert 1.00f.to_channel_u8() == 0xFF;
|
||||
|
||||
assert 0.00f.to_channel_u16() == 0x0000;
|
||||
assert 0.25f.to_channel_u16() == 0x3FFF;
|
||||
assert 0.50f.to_channel_u16() == 0x7FFF;
|
||||
assert 0.75f.to_channel_u16() == 0xBFFF;
|
||||
assert 1.00f.to_channel_u16() == 0xFFFF;
|
||||
|
||||
// TODO: test to_channel_u32()
|
||||
|
||||
// TODO: test to_channel_u64()
|
||||
|
||||
// TODO: test to_channel_f32()
|
||||
|
||||
// TODO: test to_channel_f64()
|
||||
|
||||
// TODO: test to_channel_float()
|
||||
|
||||
|
||||
// Test inverse
|
||||
|
||||
assert 0.00f.inverse() == 1.00f;
|
||||
assert 0.25f.inverse() == 0.75f;
|
||||
assert 0.50f.inverse() == 0.50f;
|
||||
assert 0.75f.inverse() == 0.25f;
|
||||
assert 1.00f.inverse() == 0.00f;
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
use angle::*;
|
||||
use color::*;
|
||||
|
||||
#[test]
|
||||
fn test_color_rgb() {
|
||||
// TODO
|
||||
assert RGB::new::<u8>(0xA0, 0xA0, 0xA0).to_rgb_u8() == RGB::new(0xA0, 0xA0, 0xA0);
|
||||
assert RGB::new::<u8>(0xA0, 0xA0, 0xA0).to_rgb_u16() == RGB::new(0xA0A0, 0xA0A0, 0xA0A0);
|
||||
assert RGB::new::<u8>(0xA0, 0xA0, 0xA0).to_rgb_u32() == RGB::new(0xA0A0_A0A0, 0xA0A0_A0A0, 0xA0A0_A0A0);
|
||||
assert RGB::new::<u8>(0xA0, 0xA0, 0xA0).to_rgb_u64() == RGB::new(0xA0A0_A0A0_A0A0_A0A0, 0xA0A0_A0A0_A0A0_A0A0, 0xA0A0_A0A0_A0A0_A0A0);
|
||||
|
||||
// assert RGB::new::<u8>(0xFF, 0xFF, 0xFF).to_hsv_f32() == HSV::new(Degrees(0.0), 0.0, 1.0); // FIXME: causes an ICE
|
||||
// RGB::new::<u8>(0xFF, 0xFF, 0xFF).to_hsv_f32(); // FIXME: causes an ICE as well :(
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_color_rgba() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_color_hsv() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_color_hsva() {
|
||||
// TODO
|
||||
}
|
|
@ -23,7 +23,6 @@
|
|||
use core::sys::size_of;
|
||||
|
||||
use angle::{Angle, Radians, Degrees};
|
||||
use color::color::{RGB, RGBA, HSV, HSVA};
|
||||
use mat::{Matrix, Mat2, Mat3, Mat4};
|
||||
use vec::{Vector, NumericVector, Vec2, Vec3, Vec4};
|
||||
use quat::{/*Quaternion, */Quat};
|
||||
|
@ -404,46 +403,8 @@ pub impl ddegrees {
|
|||
pub type quat4 = Quat<f32>; /// a single-precision floating-point quaternion
|
||||
pub type dquat4 = Quat<f64>; /// a double-precision floating-point quaternion
|
||||
|
||||
// Color type aliases. Prefixing the colors with the type letter looked a little
|
||||
// strange, so in this case I opted for a suffix. It actually loosely follows the
|
||||
// nomanclature defined in [this article](http://www.opengl.org/wiki/Image_Formats#Color_formats)
|
||||
// on the OpenGL wiki.
|
||||
|
||||
pub type rgb = rgbf; /// same as a `rgb32f`
|
||||
pub type rgba = rgbaf; /// same as a `rgba32f`
|
||||
|
||||
pub type rgbf = rgb32f; /// same as a `rgb32f`
|
||||
pub type rgb32f = RGB<f32>; /// a 32-bit floating-point RGB color with component values ranging from 0f32 to 1f32
|
||||
pub type rgb64f = RGB<f64>; /// a 64-bit floating-point RGB color with component values ranging from 0f64 to 1f64
|
||||
|
||||
pub type rgbaf = rgba32f; /// same as a `rgba32f`
|
||||
pub type rgba32f = RGBA<f32>; /// a 32-bit floating-point RGBA color with component values ranging from 0.0 to 1.0
|
||||
pub type rgba64f = RGBA<f64>; /// a 64-bit floating-point RGBA color with component values ranging from 0.0 to 1.0
|
||||
|
||||
pub type rgbu = rgb8u; /// same as a `rgb8u`
|
||||
pub type rgb8u = RGB<u8>; /// an 8-bit unsigned-integer RGB color with component values ranging from 0x00 to 0xFF
|
||||
pub type rgb16u = RGB<u16>; /// a 16-bit unsigned-integer RGB color with component values ranging from 0x0000 to 0xFFFF
|
||||
pub type rgb32u = RGB<u32>; /// a 32-bit unsigned-integer RGB color with component values ranging from 0x0000_0000 to 0xFFFF_FFFF
|
||||
pub type rgb64u = RGB<u64>; /// a 64-bit unsigned-integer RGB color with component values ranging from 0x0000_0000 to 0xFFFF_FFFF
|
||||
|
||||
pub type rgbau = rgba8u; /// same as a `rgba8u`
|
||||
pub type rgba8u = RGBA<u8>; /// an 8-bit unsigned-integer RGB color with component values ranging from 0x00 to 0xFF
|
||||
pub type rgba16u = RGBA<u16>; /// a 16-bit unsigned-integer RGB color with component values ranging from 0x0000 to 0xFFFF
|
||||
pub type rgba32u = RGBA<u32>; /// a 32-bit unsigned-integer RGB color with component values ranging from 0x0000_0000 to 0xFFFF_FFFF
|
||||
pub type rgba64u = RGBA<u64>; /// a 364bit unsigned-integer RGB color with component values ranging from 0x0000_0000 to 0xFFFF_FFFF
|
||||
|
||||
pub type hsv = hsvaf; /// same as a `hsv32f`
|
||||
pub type hsva = hsvaf; /// same as a `hsva32f`
|
||||
|
||||
pub type hsvf = hsv32f; /// same as a `hsv32f`
|
||||
pub type hsv32f = HSV<f32>; /// TODO: documentation
|
||||
pub type hsv64f = HSV<f64>; /// TODO: documentation
|
||||
|
||||
pub type hsvaf = hsva32f; /// same as a `hsva32f`
|
||||
pub type hsva32f = HSVA<f32>; /// TODO: documentation
|
||||
pub type hsva64f = HSVA<f64>; /// TODO: documentation
|
||||
|
||||
// TODO: Color method wrappers
|
||||
|
||||
// prevents "error: expected item"
|
||||
priv fn hack() {}
|
15
src/lmath.rc
15
src/lmath.rc
|
@ -30,21 +30,6 @@ mod test {
|
|||
mod test_vec;
|
||||
}
|
||||
|
||||
pub mod color {
|
||||
#[path = "color/channel.rs"]
|
||||
pub mod channel;
|
||||
#[path = "color/color.rs"]
|
||||
pub mod color;
|
||||
|
||||
#[test]
|
||||
mod test {
|
||||
#[path = "color/test/test_channel.rs"]
|
||||
mod test_channel;
|
||||
#[path = "color/test/test_color.rs"]
|
||||
mod test_color;
|
||||
}
|
||||
}
|
||||
|
||||
pub mod funs {
|
||||
#[path = "funs/common.rs"]
|
||||
pub mod common;
|
||||
|
|
Loading…
Reference in a new issue