retro_pixel/
macros.rs

1//! All of our macros go in here.
2
3/// Builds a `u32` value from the given RGBA values.
4///
5/// Each channel is cast into `u32`, and then they're combined into a single
6/// `u32` value. If inputs aren't within the range `0..=255` you'll get some
7/// unintentional output.
8///
9/// ```rust
10/// #[macro_use]
11/// extern crate retro_pixel;
12///
13/// fn main() {
14///   const TRANSPARENT_BLACK: u32 = rgba32!(0, 0, 0, 0);
15///   const SOLID_WHITE: u32 = rgba32!(255u8, 255u16, 255u32, 255.0);
16///   assert_eq!(TRANSPARENT_BLACK, 0);
17///   assert_eq!(SOLID_WHITE, ::std::u32::MAX);
18///   // all other outputs depend on what endian the machine is,
19///   // currently only little endian is supported.
20///   assert_eq!(rgba32!(255, 0, 0, 0), 0x000000FF);
21///   assert_eq!(rgba32!(0, 255, 0, 0), 0x0000FF00);
22///   assert_eq!(rgba32!(0, 0, 255, 0), 0x00FF0000);
23///   assert_eq!(rgba32!(0, 0, 0, 255), 0xFF000000);
24/// }
25/// ```
26#[cfg(target_endian = "little")]
27#[macro_export]
28macro_rules! rgba32 {
29  ($r:expr, $g:expr, $b:expr, $a:expr) => {
30    (($a as u32) << 24) | (($b as u32) << 16) | (($g as u32) << 8) | ($r as u32)
31  };
32}
33
34/// As per [`rgba32`](macro.rgba32.html), but automatically selects alpha = 255.
35///
36/// ```rust
37/// #[macro_use]
38/// extern crate retro_pixel;
39///
40/// fn main() {
41///   assert_eq!(rgb32!(255, 0, 0), rgba32!(255, 0, 0, 255));
42///   assert_eq!(rgb32!(0, 255, 0), rgba32!(0, 255, 0, 255));
43///   assert_eq!(rgb32!(0, 0, 255), rgba32!(0, 0, 255, 255));
44/// }
45/// ```
46#[cfg(target_endian = "little")]
47#[macro_export]
48macro_rules! rgb32 {
49  ($r:expr, $g:expr, $b:expr) => {
50    rgba32!($r, $g, $b, 255)
51  };
52}
53
54/// Builds a `u16` value from the given RGBA values.
55///
56/// Each _color_ channel is cast into `u16`, and then they're combined into a
57/// single `u16` value (`A1_B5_G5_R5`). If color channel aren't within the range
58/// `0..=31` you'll get some unintentional output. The alpha value is given as a
59/// boolean expression, and flips the highest bit on or off.
60///
61/// ```rust
62/// #[macro_use]
63/// extern crate retro_pixel;
64///
65/// fn main() {
66///   const TRANSPARENT_BLACK: u16 = rgba16!(0, 0, 0, false);
67///   const SOLID_WHITE: u16 = rgba16!(31, 0x1F, 31.0, true);
68///   assert_eq!(TRANSPARENT_BLACK, 0u16);
69///   assert_eq!(SOLID_WHITE, ::std::u16::MAX);
70///   // all other outputs depend on what endian the machine is,
71///   // currently only little endian is supported.
72///   assert_eq!(rgba16!(31, 0, 0, false), 0b0000000000011111u16);
73///   assert_eq!(rgba16!(0, 31, 0, false), 0b0000001111100000u16);
74///   assert_eq!(rgba16!(0, 0, 31, false), 0b0111110000000000u16);
75///   assert_eq!(rgba16!(0, 0, 0, true),   0b1000000000000000u16);
76/// }
77/// ```
78#[cfg(target_endian = "little")]
79#[macro_export]
80macro_rules! rgba16 {
81  ($r:expr, $g:expr, $b:expr, $a:expr) => {
82    (($a as u16) << 15) | (($b as u16) << 10) | (($g as u16) << 5) | ($r as u16)
83  };
84}
85
86/// As per [`rgba16`](macro.rgba16.html), but automatically selects alpha=true.
87///
88/// ```rust
89/// #[macro_use]
90/// extern crate retro_pixel;
91///
92/// fn main() {
93///   assert_eq!(rgb16!(31, 0, 0), rgba16!(31, 0, 0, true));
94///   assert_eq!(rgb16!(0, 31, 0), rgba16!(0, 31, 0, true));
95///   assert_eq!(rgb16!(0, 0, 31), rgba16!(0, 0, 31, true));
96/// }
97/// ```
98#[cfg(target_endian = "little")]
99#[macro_export]
100macro_rules! rgb16 {
101  ($r:expr, $g:expr, $b:expr) => {
102    rgba16!($r, $g, $b, true)
103  };
104}
105
106/// Checks that an address is aligned to a 4 byte bound.
107///
108/// If the result is non-zero, that's how many bytes _past_ the most recent 4
109/// byte bound you are.
110///
111/// ```rust
112/// #[macro_use]
113/// extern crate retro_pixel;
114///
115/// fn main() {
116///   for x in 0 .. 100 {
117///     assert_eq!(check_misalign4!(x), x % 4);
118///   }
119/// }
120/// ```
121#[macro_export]
122macro_rules! check_misalign4 {
123  ($ptr:expr) => {
124    ($ptr as usize) & 3
125  };
126}
127
128/// Checks that an address is aligned to a 8 byte bound.
129///
130/// If the result is non-zero, that's how many bytes _past_ the most recent 8
131/// byte bound you are.
132///
133/// ```rust
134/// #[macro_use]
135/// extern crate retro_pixel;
136///
137/// fn main() {
138///   for x in 0 .. 100 {
139///     assert_eq!(check_misalign8!(x), x % 8);
140///   }
141/// }
142/// ```
143#[macro_export]
144macro_rules! check_misalign8 {
145  ($ptr:expr) => {
146    ($ptr as usize) & 7
147  };
148}
149
150/// Checks that an address is aligned to a 16 byte bound.
151///
152/// If the result is non-zero, that's how many bytes _past_ the most recent 16
153/// byte bound you are.
154///
155/// ```rust
156/// #[macro_use]
157/// extern crate retro_pixel;
158///
159/// fn main() {
160///   for x in 0 .. 100 {
161///     assert_eq!(check_misalign16!(x), x % 16);
162///   }
163/// }
164/// ```
165#[macro_export]
166macro_rules! check_misalign16 {
167  ($ptr:expr) => {
168    ($ptr as usize) & 15
169  };
170}
171
172/// Checks that an address is aligned to a 32 byte bound.
173///
174/// If the result is non-zero, that's how many bytes _past_ the most recent 32
175/// byte bound you are.
176///
177/// ```rust
178/// #[macro_use]
179/// extern crate retro_pixel;
180///
181/// fn main() {
182///   for x in 0 .. 100 {
183///     assert_eq!(check_misalign32!(x), x % 32);
184///   }
185/// }
186/// ```
187#[macro_export]
188macro_rules! check_misalign32 {
189  ($ptr:expr) => {
190    ($ptr as usize) & 31
191  };
192}
193
194// TODO: doc-tests, docs
195#[macro_export]
196macro_rules! determine_overlay {
197  ($dest:ident, $src:ident, $offset:ident) => {{
198    let offset_x = $offset.0;
199    let offset_y = $offset.1;
200    let dest_width = $dest.width() as isize;
201    let dest_height = $dest.height() as isize;
202    let src_width = $src.width() as isize;
203    let src_height = $src.height() as isize;
204    // establish that we should be drawing something at all
205    if offset_x < dest_width && offset_y < dest_height && -offset_x < src_width && -offset_y < src_height {
206      // determine where we'll be copying
207      let dest_start_x = (offset_x).max(0) as usize;
208      let dest_start_y = (offset_y).max(0) as usize;
209      let src_start_x = (-offset_x).max(0) as usize;
210      let src_start_y = (-offset_y).max(0) as usize;
211      debug_assert!(dest_width as usize > dest_start_x);
212      debug_assert!(dest_width as usize > dest_start_y);
213      debug_assert!(src_width as usize > src_start_x);
214      debug_assert!(src_height as usize > src_start_y);
215      let clip_width = (dest_width as usize - dest_start_x).min(src_width as usize - src_start_x);
216      let clip_height = (dest_height as usize - dest_start_y).min(src_height as usize - src_start_y);
217      debug_assert!(clip_width > 0);
218      debug_assert!(clip_height > 0);
219      let clip_width = clip_width as usize;
220      let clip_height = clip_height as usize;
221      let src_row_start_ptr = $src.as_ptr().offset(src_start_x as isize + src_start_y as isize * $src.pitch());
222      let dest_row_start_ptr = $dest.as_mut_ptr().offset(dest_start_x as isize + dest_start_y as isize * $dest.pitch());
223      (clip_width, clip_height, src_row_start_ptr, dest_row_start_ptr)
224    } else {
225      (0, 0, ::core::ptr::null(), ::core::ptr::null_mut())
226    }
227  }};
228}