Last active
July 16, 2019 12:38
-
-
Save Gankra/46e705f504a4d49b21594c41e2974b6d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// Generate a color ramp filling the indices in [start_idx, end_idx) and interpolating | |
/// from start_color to end_color. | |
fn fill_colors( | |
start_idx: usize, | |
end_idx: usize, | |
start_color: &PremultipliedColorF, | |
end_color: &PremultipliedColorF, | |
entries: &mut ArrayVec<[GradientDataEntry; GRADIENT_DATA_SIZE]>, | |
) { | |
debug_assert_eq!(start_idx, entries.len()); | |
// Calculate the color difference for individual steps in the ramp. | |
let inv_steps = 1.0 / (end_idx - start_idx) as f32; | |
let step_r = (end_color.r - start_color.r) * inv_steps; | |
let step_g = (end_color.g - start_color.g) * inv_steps; | |
let step_b = (end_color.b - start_color.b) * inv_steps; | |
let step_a = (end_color.a - start_color.a) * inv_steps; | |
let mut cur_color = *start_color; | |
// Walk the ramp writing start and end colors for each entry. | |
for index in start_idx .. end_idx { | |
entries.push(GradientDataEntry { | |
start_color: cur_color, | |
end_color: { | |
cur_color.r += step_r; | |
cur_color.g += step_g; | |
cur_color.b += step_b; | |
cur_color.a += step_a; | |
cur_color | |
} | |
}); | |
} | |
} | |
/// Compute an index into the gradient entry table based on a gradient stop offset. This | |
/// function maps offsets from [0, 1] to indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END]. | |
#[inline] | |
fn get_index(offset: f32) -> usize { | |
(offset.max(0.0).min(1.0) * GRADIENT_DATA_TABLE_SIZE as f32 + | |
GRADIENT_DATA_TABLE_BEGIN as f32) | |
.round() as usize | |
} | |
// Build the gradient data from the supplied stops, reversing them if necessary. | |
fn build( | |
reverse_stops: bool, | |
request: &mut GpuDataRequest, | |
src_stops: &[GradientStop], | |
) { | |
// Preconditions (should be ensured by DisplayListBuilder): | |
// * we have at least two stops | |
// * first stop has offset 0.0 | |
// * last stop has offset 1.0 | |
let mut src_stops = src_stops.into_iter(); | |
let mut cur_color = match src_stops.next() { | |
Some(stop) => { | |
debug_assert_eq!(stop.offset, 0.0); | |
stop.color.premultiplied() | |
} | |
None => { | |
error!("Zero gradient stops found!"); | |
PremultipliedColorF::BLACK | |
} | |
}; | |
// A table of gradient entries, with two colors per entry, that specify the start and end color | |
// within the segment of the gradient space represented by that entry. To lookup a gradient result, | |
// first the entry index is calculated to determine which two colors to interpolate between, then | |
// the offset within that entry bucket is used to interpolate between the two colors in that entry. | |
// This layout preserves hard stops, as the end color for a given entry can differ from the start | |
// color for the following entry, despite them being adjacent. Colors are stored within in BGRA8 | |
// format for texture upload. This table requires the gradient color stops to be normalized to the | |
// range [0, 1]. The first and last entries hold the first and last color stop colors respectively, | |
// while the entries in between hold the interpolated color stop values for the range [0, 1]. | |
let mut entries = ArrayVec::<[GradientDataEntry; GRADIENT_DATA_SIZE]>::new(); | |
// Fill in the first entry with the first color stop | |
GradientGpuBlockBuilder::fill_colors( | |
GRADIENT_DATA_FIRST_STOP, | |
GRADIENT_DATA_FIRST_STOP + 1, | |
&cur_color, | |
&cur_color, | |
&mut entries, | |
); | |
// Fill in the center of the gradient table, generating a color ramp between each consecutive pair | |
// of gradient stops. Each iteration of a loop will fill the indices in [cur_idx, next_idx). The | |
// loop will then fill indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END). | |
let mut cur_idx = GRADIENT_DATA_TABLE_BEGIN; | |
for next in src_stops { | |
let next_color = next.color.premultiplied(); | |
let next_idx = Self::get_index(next.offset); | |
if next_idx > cur_idx { | |
GradientGpuBlockBuilder::fill_colors( | |
cur_idx, | |
next_idx, | |
&cur_color, | |
&next_color, | |
&mut entries, | |
); | |
cur_idx = next_idx; | |
} | |
cur_color = next_color; | |
} | |
if cur_idx != GRADIENT_DATA_TABLE_END { | |
error!("Gradient stops abruptly at {}, auto-completing to white", cur_idx); | |
GradientGpuBlockBuilder::fill_colors( | |
cur_idx, | |
GRADIENT_DATA_TABLE_END, | |
&PremultipliedColorF::WHITE, | |
&cur_color, | |
&mut entries, | |
); | |
} | |
// Fill in the last entry with the last color stop | |
GradientGpuBlockBuilder::fill_colors( | |
GRADIENT_DATA_LAST_STOP, | |
GRADIENT_DATA_LAST_STOP + 1, | |
&cur_color, | |
&cur_color, | |
&mut entries, | |
); | |
debug_assert!(entries.is_full()); | |
if reverse_stops { | |
for entry in entries.as_slice().iter().rev() { | |
request.push(entry.start_color); | |
request.push(entry.end_color); | |
} | |
} else { | |
for entry in entries.as_slice().iter() { | |
request.push(entry.start_color); | |
request.push(entry.end_color); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment