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}