Collapse linear gradients down to a single 256x1 sliver instead of a 256x256
square
This commit is contained in:
parent
2548ab853a
commit
0b0c45a49a
|
@ -20,7 +20,7 @@ use std::mem;
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub struct Gradient {
|
pub struct Gradient {
|
||||||
geometry: GradientGeometry,
|
pub geometry: GradientGeometry,
|
||||||
stops: SortedVector<ColorStop>,
|
stops: SortedVector<ColorStop>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,11 @@ impl Matrix2x2F {
|
||||||
Matrix2x2F(F32x4::splat(1.0 / self.det()) * self.adjugate().0)
|
Matrix2x2F(F32x4::splat(1.0 / self.det()) * self.adjugate().0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn scale(&self, factor: f32) -> Matrix2x2F {
|
||||||
|
Matrix2x2F(self.0 * F32x4::splat(factor))
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn m11(&self) -> f32 {
|
pub fn m11(&self) -> f32 {
|
||||||
self.0[0]
|
self.0[0]
|
||||||
|
|
|
@ -213,6 +213,7 @@ impl Palette {
|
||||||
|
|
||||||
// Assign paint locations.
|
// Assign paint locations.
|
||||||
let mut solid_color_tile_builder = SolidColorTileBuilder::new();
|
let mut solid_color_tile_builder = SolidColorTileBuilder::new();
|
||||||
|
let mut gradient_tile_builder = GradientTileBuilder::new();
|
||||||
for paint in &self.paints {
|
for paint in &self.paints {
|
||||||
let (texture_location, mut sampling_flags);
|
let (texture_location, mut sampling_flags);
|
||||||
match paint {
|
match paint {
|
||||||
|
@ -220,7 +221,12 @@ impl Palette {
|
||||||
texture_location = solid_color_tile_builder.allocate(&mut allocator);
|
texture_location = solid_color_tile_builder.allocate(&mut allocator);
|
||||||
sampling_flags = TextureSamplingFlags::empty();
|
sampling_flags = TextureSamplingFlags::empty();
|
||||||
}
|
}
|
||||||
Paint::Gradient(_) => {
|
Paint::Gradient(Gradient { geometry: GradientGeometry::Linear(_), .. }) => {
|
||||||
|
// FIXME(pcwalton): The gradient size might not be big enough. Detect this.
|
||||||
|
texture_location = gradient_tile_builder.allocate(&mut allocator);
|
||||||
|
sampling_flags = TextureSamplingFlags::empty();
|
||||||
|
}
|
||||||
|
Paint::Gradient(Gradient { geometry: GradientGeometry::Radial { .. }, .. }) => {
|
||||||
// TODO(pcwalton): Optimize this:
|
// TODO(pcwalton): Optimize this:
|
||||||
// 1. Use repeating/clamp on the sides.
|
// 1. Use repeating/clamp on the sides.
|
||||||
// 2. Choose an optimal size for the gradient that minimizes memory usage while
|
// 2. Choose an optimal size for the gradient that minimizes memory usage while
|
||||||
|
@ -279,7 +285,19 @@ impl Palette {
|
||||||
let vector = rect_to_inset_uv(metadata.location.rect, texture_scale).origin();
|
let vector = rect_to_inset_uv(metadata.location.rect, texture_scale).origin();
|
||||||
Transform2F { matrix: Matrix2x2F(F32x4::default()), vector }
|
Transform2F { matrix: Matrix2x2F(F32x4::default()), vector }
|
||||||
}
|
}
|
||||||
Paint::Gradient(_) => {
|
Paint::Gradient(Gradient {
|
||||||
|
geometry: GradientGeometry::Linear(gradient_line),
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
let v0 = metadata.location.rect.to_f32().center().y() * texture_scale.y();
|
||||||
|
let length_inv = 1.0 / gradient_line.square_length();
|
||||||
|
let (p0, d) = (gradient_line.from(), gradient_line.vector());
|
||||||
|
Transform2F {
|
||||||
|
matrix: Matrix2x2F::row_major(d.x(), d.y(), 0.0, 0.0).scale(length_inv),
|
||||||
|
vector: Vector2F::new(-p0.dot(d) * length_inv, v0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Paint::Gradient(Gradient { geometry: GradientGeometry::Radial { .. }, .. }) => {
|
||||||
let texture_origin_uv =
|
let texture_origin_uv =
|
||||||
rect_to_uv(metadata.location.rect, texture_scale).origin();
|
rect_to_uv(metadata.location.rect, texture_scale).origin();
|
||||||
let gradient_tile_scale = texture_scale.scale(GRADIENT_TILE_LENGTH as f32);
|
let gradient_tile_scale = texture_scale.scale(GRADIENT_TILE_LENGTH as f32);
|
||||||
|
@ -318,9 +336,10 @@ impl Palette {
|
||||||
// Allocate the texels.
|
// Allocate the texels.
|
||||||
//
|
//
|
||||||
// TODO(pcwalton): This is slow. Do more on GPU.
|
// TODO(pcwalton): This is slow. Do more on GPU.
|
||||||
let mut page_texels: Vec<_> = paint_metadata.iter().map(|metadata| {
|
let mut page_texels: Vec<_> =
|
||||||
Texels::new(allocator.page_size(metadata.location.page))
|
texture_page_descriptors.iter()
|
||||||
}).collect();
|
.map(|descriptor| Texels::new(descriptor.size))
|
||||||
|
.collect();
|
||||||
|
|
||||||
// Draw to texels.
|
// Draw to texels.
|
||||||
//
|
//
|
||||||
|
@ -383,24 +402,15 @@ impl Palette {
|
||||||
tex_transform: &Transform2F,
|
tex_transform: &Transform2F,
|
||||||
texels: &mut Texels) {
|
texels: &mut Texels) {
|
||||||
match *gradient.geometry() {
|
match *gradient.geometry() {
|
||||||
GradientGeometry::Linear(gradient_line) => {
|
GradientGeometry::Linear(_) => {
|
||||||
// FIXME(pcwalton): Paint transparent if gradient line has zero size, per spec.
|
// FIXME(pcwalton): Paint transparent if gradient line has zero size, per spec.
|
||||||
let gradient_line = *tex_transform * gradient_line;
|
|
||||||
|
|
||||||
// TODO(pcwalton): Optimize this:
|
// TODO(pcwalton): Optimize this:
|
||||||
// 1. Calculate ∇t up front and use differencing in the inner loop.
|
// 1. Calculate ∇t up front and use differencing in the inner loop.
|
||||||
// 2. Go four pixels at a time with SIMD.
|
// 2. Go four pixels at a time with SIMD.
|
||||||
for y in 0..(GRADIENT_TILE_LENGTH as i32) {
|
for x in 0..(GRADIENT_TILE_LENGTH as i32) {
|
||||||
for x in 0..(GRADIENT_TILE_LENGTH as i32) {
|
let point = tex_rect.origin() + Vector2I::new(x, 0);
|
||||||
let point = tex_rect.origin() + Vector2I::new(x, y);
|
let t = (x as f32 + 0.5) / GRADIENT_TILE_LENGTH as f32;
|
||||||
let vector = point.to_f32().scale_xy(texels.scale()) -
|
texels.put_texel(point, gradient.sample(t));
|
||||||
gradient_line.from();
|
|
||||||
|
|
||||||
let mut t = gradient_line.vector().projection_coefficient(vector);
|
|
||||||
t = util::clamp(t, 0.0, 1.0);
|
|
||||||
|
|
||||||
texels.put_texel(point, gradient.sample(t));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -579,10 +589,6 @@ impl Texels {
|
||||||
fn put_texel(&mut self, position: Vector2I, color: ColorU) {
|
fn put_texel(&mut self, position: Vector2I, color: ColorU) {
|
||||||
self.blit_scanline(position, &[color])
|
self.blit_scanline(position, &[color])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scale(&self) -> Vector2F {
|
|
||||||
Vector2F::splat(1.0) / self.size.to_f32()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rect_to_uv(rect: RectI, texture_scale: Vector2F) -> RectF {
|
fn rect_to_uv(rect: RectI, texture_scale: Vector2F) -> RectF {
|
||||||
|
@ -638,3 +644,46 @@ impl SolidColorTileBuilder {
|
||||||
location
|
location
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gradient allocation
|
||||||
|
|
||||||
|
struct GradientTileBuilder(Option<GradientTileBuilderData>);
|
||||||
|
|
||||||
|
struct GradientTileBuilderData {
|
||||||
|
page: TexturePageId,
|
||||||
|
next_index: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GradientTileBuilder {
|
||||||
|
fn new() -> GradientTileBuilder {
|
||||||
|
GradientTileBuilder(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn allocate(&mut self, allocator: &mut TextureAllocator) -> TextureLocation {
|
||||||
|
if self.0.is_none() {
|
||||||
|
let size = Vector2I::splat(GRADIENT_TILE_LENGTH as i32);
|
||||||
|
self.0 = Some(GradientTileBuilderData {
|
||||||
|
page: allocator.allocate(size, AllocationMode::OwnPage).page,
|
||||||
|
next_index: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let (location, tile_full);
|
||||||
|
{
|
||||||
|
let mut data = self.0.as_mut().unwrap();
|
||||||
|
location = TextureLocation {
|
||||||
|
page: data.page,
|
||||||
|
rect: RectI::new(Vector2I::new(0, data.next_index as i32),
|
||||||
|
Vector2I::new(GRADIENT_TILE_LENGTH as i32, 1)),
|
||||||
|
};
|
||||||
|
data.next_index += 1;
|
||||||
|
tile_full = data.next_index == GRADIENT_TILE_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
if tile_full {
|
||||||
|
self.0 = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
location
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue