wip
This commit is contained in:
parent
7929e610bc
commit
d72bc29002
|
@ -745,128 +745,6 @@ impl Segment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_active_edge(&mut self, built_object: &mut BuiltObject, tile_y: i16) {
|
|
||||||
let tile_bottom = ((tile_y as i32 + 1) * TILE_HEIGHT as i32) as f32;
|
|
||||||
|
|
||||||
if self.is_line_segment() {
|
|
||||||
generate_fill_primitives_for_line_outer(self, built_object, tile_y);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(pcwalton): Don't degree elevate!
|
|
||||||
if !self.is_cubic_segment() {
|
|
||||||
*self = self.to_cubic();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut segment = *self;
|
|
||||||
let winds_up = segment.baseline.from_y() > segment.baseline.to_y();
|
|
||||||
if winds_up {
|
|
||||||
segment = segment.reversed();
|
|
||||||
}
|
|
||||||
|
|
||||||
let segment = segment.as_cubic_segment();
|
|
||||||
let flattener = segment.flattener();
|
|
||||||
let mut from = segment.0.baseline.from();
|
|
||||||
for to in flattener {
|
|
||||||
let mut subsegment = if !winds_up {
|
|
||||||
Segment::from_line(&LineSegmentF32::new(&from, &to))
|
|
||||||
} else {
|
|
||||||
Segment::from_line(&LineSegmentF32::new(&to, &from))
|
|
||||||
};
|
|
||||||
|
|
||||||
generate_fill_primitives_for_line_outer(&mut subsegment, built_object, tile_y);
|
|
||||||
|
|
||||||
if !subsegment.is_none() {
|
|
||||||
let mut bottom_part = segment.split_y_after(tile_bottom, 0.0, 1.0);
|
|
||||||
if winds_up {
|
|
||||||
bottom_part = bottom_part.reversed();
|
|
||||||
}
|
|
||||||
*self = bottom_part;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
from = to;
|
|
||||||
}
|
|
||||||
|
|
||||||
let to = segment.0.baseline.to();
|
|
||||||
let mut subsegment = if !winds_up {
|
|
||||||
Segment::from_line(&LineSegmentF32::new(&from, &to))
|
|
||||||
} else {
|
|
||||||
Segment::from_line(&LineSegmentF32::new(&to, &from))
|
|
||||||
};
|
|
||||||
|
|
||||||
generate_fill_primitives_for_line_outer(&mut subsegment, built_object, tile_y);
|
|
||||||
|
|
||||||
if !subsegment.is_none() {
|
|
||||||
let mut bottom_part = segment.split_y_after(tile_bottom, 0.0, 1.0);
|
|
||||||
if winds_up {
|
|
||||||
bottom_part = bottom_part.reversed();
|
|
||||||
}
|
|
||||||
*self = bottom_part;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
*self = Segment::new();
|
|
||||||
return;
|
|
||||||
|
|
||||||
fn generate_fill_primitives_for_line_outer(segment: &mut Segment,
|
|
||||||
built_object: &mut BuiltObject,
|
|
||||||
tile_y: i16) {
|
|
||||||
let tile_bottom = ((tile_y as i32 + 1) * TILE_HEIGHT as i32) as f32;
|
|
||||||
let (upper_segment, lower_segment) = segment.split_y(tile_bottom);
|
|
||||||
if let Some(upper_segment) = upper_segment {
|
|
||||||
generate_fill_primitives_for_line(upper_segment.as_line_segment().unwrap(),
|
|
||||||
built_object,
|
|
||||||
tile_y);
|
|
||||||
}
|
|
||||||
*segment = lower_segment.unwrap_or(Segment::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(pcwalton): Optimize this better with SIMD!
|
|
||||||
fn generate_fill_primitives_for_line(mut segment: LineSegmentF32,
|
|
||||||
built_object: &mut BuiltObject,
|
|
||||||
tile_y: i16) {
|
|
||||||
/*
|
|
||||||
println!("segment={:?} tile_y={} ({}-{})",
|
|
||||||
segment,
|
|
||||||
tile_y,
|
|
||||||
tile_y as f32 * TILE_HEIGHT,
|
|
||||||
(tile_y + 1) as f32 * TILE_HEIGHT);
|
|
||||||
*/
|
|
||||||
|
|
||||||
let winding = segment.from_x() > segment.to_x();
|
|
||||||
let (segment_left, segment_right) = if !winding {
|
|
||||||
(segment.from_x(), segment.to_x())
|
|
||||||
} else {
|
|
||||||
(segment.to_x(), segment.from_x())
|
|
||||||
};
|
|
||||||
|
|
||||||
let segment_tile_left = (f32::floor(segment_left) as i32 / TILE_WIDTH as i32) as i16;
|
|
||||||
let segment_tile_right = alignup_i32(f32::ceil(segment_right) as i32,
|
|
||||||
TILE_WIDTH as i32) as i16;
|
|
||||||
|
|
||||||
for subsegment_tile_x in segment_tile_left..segment_tile_right {
|
|
||||||
let (mut fill_from, mut fill_to) = (segment.from(), segment.to());
|
|
||||||
let subsegment_tile_right = ((subsegment_tile_x as i32 + 1) * TILE_HEIGHT as i32)
|
|
||||||
as f32;
|
|
||||||
if subsegment_tile_right < segment_right {
|
|
||||||
let x = subsegment_tile_right;
|
|
||||||
let point = Point2DF32::new(x, segment.solve_y_for_x(x));
|
|
||||||
if !winding {
|
|
||||||
fill_to = point;
|
|
||||||
segment = LineSegmentF32::new(&point, &segment.to());
|
|
||||||
} else {
|
|
||||||
fill_from = point;
|
|
||||||
segment = LineSegmentF32::new(&segment.from(), &point);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let fill_segment = LineSegmentF32::new(&fill_from, &fill_to);
|
|
||||||
built_object.add_fill(&fill_segment, subsegment_tile_x, tile_y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_none(&self) -> bool {
|
fn is_none(&self) -> bool {
|
||||||
!self.flags.contains(SegmentFlags::HAS_ENDPOINTS)
|
!self.flags.contains(SegmentFlags::HAS_ENDPOINTS)
|
||||||
}
|
}
|
||||||
|
@ -903,7 +781,11 @@ impl Segment {
|
||||||
fn reversed(&self) -> Segment {
|
fn reversed(&self) -> Segment {
|
||||||
Segment {
|
Segment {
|
||||||
baseline: self.baseline.reversed(),
|
baseline: self.baseline.reversed(),
|
||||||
ctrl: self.ctrl.reversed(),
|
ctrl: if !self.flags.contains(SegmentFlags::HAS_CONTROL_POINT_1) {
|
||||||
|
self.ctrl
|
||||||
|
} else {
|
||||||
|
self.ctrl.reversed()
|
||||||
|
},
|
||||||
flags: self.flags,
|
flags: self.flags,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -921,8 +803,40 @@ bitflags! {
|
||||||
struct CubicSegment<'s>(&'s Segment);
|
struct CubicSegment<'s>(&'s Segment);
|
||||||
|
|
||||||
impl<'s> CubicSegment<'s> {
|
impl<'s> CubicSegment<'s> {
|
||||||
fn flattener(self) -> CubicCurveFlattener {
|
fn flatten_once(self) -> Option<Segment> {
|
||||||
CubicCurveFlattener { curve: *self.0 }
|
let s2inv;
|
||||||
|
unsafe {
|
||||||
|
let (baseline, ctrl) = (self.0.baseline.0, self.0.ctrl.0);
|
||||||
|
let from_from = Sse41::shuffle_ps(baseline, baseline, 0b01000100);
|
||||||
|
|
||||||
|
let v0102 = Sse41::sub_ps(ctrl, from_from);
|
||||||
|
|
||||||
|
// v01.x v01.y v02.x v02.y
|
||||||
|
// * v01.x v01.y v01.y v01.x
|
||||||
|
// -------------------------
|
||||||
|
// v01.x^2 v01.y^2 ad bc
|
||||||
|
// | | | |
|
||||||
|
// +-------+ +-----+
|
||||||
|
// + -
|
||||||
|
// v01 len^2 determinant
|
||||||
|
let products = Sse41::mul_ps(v0102, Sse41::shuffle_ps(v0102, v0102, 0b00010100));
|
||||||
|
|
||||||
|
let det = products[2] - products[3];
|
||||||
|
if det == 0.0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
s2inv = (products[0] + products[1]).sqrt() / det;
|
||||||
|
}
|
||||||
|
|
||||||
|
let t = 2.0 * ((FLATTENING_TOLERANCE / 3.0) * s2inv.abs()).sqrt();
|
||||||
|
if t >= 1.0 - EPSILON || t == 0.0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Some(self.split_after(t));
|
||||||
|
|
||||||
|
const EPSILON: f32 = 0.005;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sample(self, t: f32) -> Point2DF32 {
|
fn sample(self, t: f32) -> Point2DF32 {
|
||||||
|
@ -1108,24 +1022,31 @@ impl<'o, 'z> Tiler<'o, 'z> {
|
||||||
|
|
||||||
let mut last_segment_x = -9999.0;
|
let mut last_segment_x = -9999.0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
println!("----------");
|
||||||
|
println!("old active edges: {:#?}", self.old_active_edges);
|
||||||
|
*/
|
||||||
|
|
||||||
for mut active_edge in self.old_active_edges.drain(..) {
|
for mut active_edge in self.old_active_edges.drain(..) {
|
||||||
// Determine x-intercept and winding.
|
// Determine x-intercept and winding.
|
||||||
let (segment_x, edge_winding) =
|
let segment_x = active_edge.crossing.x();
|
||||||
|
let edge_winding =
|
||||||
if active_edge.segment.baseline.from_y() < active_edge.segment.baseline.to_y() {
|
if active_edge.segment.baseline.from_y() < active_edge.segment.baseline.to_y() {
|
||||||
(active_edge.segment.baseline.from_x(), 1)
|
1
|
||||||
} else {
|
} else {
|
||||||
(active_edge.segment.baseline.to_x(), -1)
|
-1
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
println!("tile Y {}: segment_x={} edge_winding={} current_tile_x={} current_subtile_x={} current_winding={}",
|
println!("tile Y {}: segment_x={} edge_winding={} current_tile_x={} \
|
||||||
|
current_subtile_x={} current_winding={}",
|
||||||
tile_y,
|
tile_y,
|
||||||
segment_x,
|
segment_x,
|
||||||
edge_winding,
|
edge_winding,
|
||||||
current_tile_x,
|
current_tile_x,
|
||||||
current_subtile_x,
|
current_subtile_x,
|
||||||
current_winding);
|
current_winding);
|
||||||
println!("... segment={:#?}", active_edge.segment);
|
println!("... segment={:#?} crossing={:?}", active_edge.segment, active_edge.crossing);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// FIXME(pcwalton): Remove this debug code!
|
// FIXME(pcwalton): Remove this debug code!
|
||||||
|
@ -1174,7 +1095,10 @@ impl<'o, 'z> Tiler<'o, 'z> {
|
||||||
current_winding += edge_winding;
|
current_winding += edge_winding;
|
||||||
|
|
||||||
// Process the edge.
|
// Process the edge.
|
||||||
active_edge.segment.process_active_edge(&mut self.built_object, tile_y);
|
//println!("about to process existing active edge {:#?}", active_edge);
|
||||||
|
let tile_top = ((tile_y as i32) * TILE_HEIGHT as i32) as f32;
|
||||||
|
debug_assert!(f32::abs(active_edge.crossing.y() - tile_top) < 0.1);
|
||||||
|
active_edge.process(&mut self.built_object, tile_y);
|
||||||
if !active_edge.segment.is_none() {
|
if !active_edge.segment.is_none() {
|
||||||
self.active_edges.push(active_edge);
|
self.active_edges.push(active_edge);
|
||||||
}
|
}
|
||||||
|
@ -1193,7 +1117,8 @@ impl<'o, 'z> Tiler<'o, 'z> {
|
||||||
let prev_endpoint_index = contour.prev_endpoint_index_of(point_index.point());
|
let prev_endpoint_index = contour.prev_endpoint_index_of(point_index.point());
|
||||||
let next_endpoint_index = contour.next_endpoint_index_of(point_index.point());
|
let next_endpoint_index = contour.next_endpoint_index_of(point_index.point());
|
||||||
/*
|
/*
|
||||||
println!("adding new active edge, tile_y={} point_index={} prev={} next={} pos={:?} prevpos={:?} nextpos={:?}",
|
println!("adding new active edge, tile_y={} point_index={} prev={} next={} pos={:?} \
|
||||||
|
prevpos={:?} nextpos={:?}",
|
||||||
tile_y,
|
tile_y,
|
||||||
point_index.point(),
|
point_index.point(),
|
||||||
prev_endpoint_index,
|
prev_endpoint_index,
|
||||||
|
@ -1202,6 +1127,7 @@ impl<'o, 'z> Tiler<'o, 'z> {
|
||||||
contour.position_of(prev_endpoint_index),
|
contour.position_of(prev_endpoint_index),
|
||||||
contour.position_of(next_endpoint_index));
|
contour.position_of(next_endpoint_index));
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if contour.point_is_logically_above(point_index.point(), prev_endpoint_index) {
|
if contour.point_is_logically_above(point_index.point(), prev_endpoint_index) {
|
||||||
//println!("... adding prev endpoint");
|
//println!("... adding prev endpoint");
|
||||||
process_active_segment(contour,
|
process_active_segment(contour,
|
||||||
|
@ -1271,11 +1197,10 @@ fn process_active_segment(contour: &Contour,
|
||||||
active_edges: &mut SortedVector<ActiveEdge>,
|
active_edges: &mut SortedVector<ActiveEdge>,
|
||||||
built_object: &mut BuiltObject,
|
built_object: &mut BuiltObject,
|
||||||
tile_y: i16) {
|
tile_y: i16) {
|
||||||
let mut segment = contour.segment_after(from_endpoint_index);
|
let mut active_edge = ActiveEdge::from_segment(&contour.segment_after(from_endpoint_index));
|
||||||
segment.process_active_edge(built_object, tile_y);
|
active_edge.process(built_object, tile_y);
|
||||||
|
if !active_edge.segment.is_none() {
|
||||||
if !segment.is_none() {
|
active_edges.push(active_edge);
|
||||||
active_edges.push(ActiveEdge::new(segment));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2227,28 +2152,235 @@ impl PartialOrd<QueuedEndpoint> for QueuedEndpoint {
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
struct ActiveEdge {
|
struct ActiveEdge {
|
||||||
segment: Segment,
|
segment: Segment,
|
||||||
|
// TODO(pcwalton): Shrink `crossing` down to just one f32?
|
||||||
|
crossing: Point2DF32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ActiveEdge {
|
impl ActiveEdge {
|
||||||
fn new(segment: Segment) -> ActiveEdge {
|
fn from_segment(segment: &Segment) -> ActiveEdge {
|
||||||
ActiveEdge { segment }
|
let crossing = if segment.baseline.from_y() < segment.baseline.to_y() {
|
||||||
|
segment.baseline.from()
|
||||||
|
} else {
|
||||||
|
segment.baseline.to()
|
||||||
|
};
|
||||||
|
ActiveEdge::from_segment_and_crossing(segment, &crossing)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn from_segment_and_crossing(segment: &Segment, crossing: &Point2DF32) -> ActiveEdge {
|
||||||
|
ActiveEdge { segment: *segment, crossing: *crossing }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_none(&self) -> bool { self.segment.is_none() }
|
||||||
|
|
||||||
|
fn process(&mut self, built_object: &mut BuiltObject, tile_y: i16) {
|
||||||
|
let tile_bottom = ((tile_y as i32 + 1) * TILE_HEIGHT as i32) as f32;
|
||||||
|
// println!("process_active_edge({:#?}, tile_y={}({}))", self, tile_y, tile_bottom);
|
||||||
|
|
||||||
|
let mut down_segment = self.segment;
|
||||||
|
let winds_up = down_segment.baseline.from_y() > down_segment.baseline.to_y();
|
||||||
|
if winds_up {
|
||||||
|
down_segment = down_segment.reversed();
|
||||||
|
}
|
||||||
|
|
||||||
|
if down_segment.is_line_segment() {
|
||||||
|
let down_line_segment = down_segment.as_line_segment().unwrap();
|
||||||
|
if down_line_segment.to_y() > tile_bottom {
|
||||||
|
let (mut upper_part, mut lower_part) = down_line_segment.split_at_y(tile_bottom);
|
||||||
|
self.crossing = upper_part.to();
|
||||||
|
|
||||||
|
if winds_up {
|
||||||
|
upper_part = upper_part.reversed();
|
||||||
|
lower_part = lower_part.reversed();
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_fill_primitives_for_line(upper_part, built_object, tile_y);
|
||||||
|
self.segment = Segment::from_line(&lower_part);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut line_segment = down_line_segment;
|
||||||
|
if winds_up {
|
||||||
|
line_segment = line_segment.reversed();
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_fill_primitives_for_line(line_segment, built_object, tile_y);
|
||||||
|
self.segment = Segment::new();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(pcwalton): Don't degree elevate!
|
||||||
|
if !down_segment.is_cubic_segment() {
|
||||||
|
down_segment = down_segment.to_cubic();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If necessary, draw initial line.
|
||||||
|
//assert!(down_segment.baseline.from_y() < tile_bottom);
|
||||||
|
if self.crossing.y() < down_segment.baseline.from_y() {
|
||||||
|
let first_down_line_segment = LineSegmentF32::new(&self.crossing,
|
||||||
|
&down_segment.baseline.from());
|
||||||
|
if first_down_line_segment.to_y() > tile_bottom {
|
||||||
|
// Shouldn't happen much…
|
||||||
|
// FIXME(pcwalton): Reduce duplication!
|
||||||
|
|
||||||
|
let (down_upper_part, _) = first_down_line_segment.split_at_y(tile_bottom);
|
||||||
|
self.crossing = down_upper_part.to();
|
||||||
|
|
||||||
|
let mut upper_part = down_upper_part;
|
||||||
|
if winds_up {
|
||||||
|
upper_part = upper_part.reversed();
|
||||||
|
}
|
||||||
|
|
||||||
|
// println!("... FIRST crossed tile Y {}", tile_bottom);
|
||||||
|
generate_fill_primitives_for_line(upper_part, built_object, tile_y);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut first_line_segment = first_down_line_segment;
|
||||||
|
if winds_up {
|
||||||
|
first_line_segment = first_line_segment.reversed();
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_fill_primitives_for_line(first_line_segment, built_object, tile_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let rest_down_segment = match down_segment.as_cubic_segment().flatten_once() {
|
||||||
|
None => {
|
||||||
|
let down_line_segment = down_segment.baseline;
|
||||||
|
|
||||||
|
// FIXME(pcwalton): Eliminate duplication with below!!
|
||||||
|
if down_line_segment.to_y() > tile_bottom {
|
||||||
|
let (down_upper_part, down_lower_part) =
|
||||||
|
down_line_segment.split_at_y(tile_bottom);
|
||||||
|
self.crossing = down_upper_part.to();
|
||||||
|
|
||||||
|
let mut upper_part = down_upper_part;
|
||||||
|
if winds_up {
|
||||||
|
upper_part = upper_part.reversed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
println!("... cubic crossed tile Y {} (LAST); down_line_segment={:#?} \
|
||||||
|
down_lower_part={:#?}",
|
||||||
|
tile_bottom,
|
||||||
|
down_line_segment,
|
||||||
|
down_lower_part);
|
||||||
|
*/
|
||||||
|
|
||||||
|
generate_fill_primitives_for_line(upper_part, built_object, tile_y);
|
||||||
|
|
||||||
|
let mut lower_part = down_lower_part;
|
||||||
|
if winds_up {
|
||||||
|
lower_part = lower_part.reversed();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.segment = Segment::from_line(&lower_part);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut line_segment = down_segment.baseline;
|
||||||
|
if winds_up {
|
||||||
|
line_segment = line_segment.reversed();
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_fill_primitives_for_line(line_segment, built_object, tile_y);
|
||||||
|
|
||||||
|
self.segment = Segment::new();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Some(rest_down_segment) => rest_down_segment,
|
||||||
|
};
|
||||||
|
|
||||||
|
debug_assert!(down_segment.baseline.from_y() <= tile_bottom);
|
||||||
|
let down_line_segment = LineSegmentF32::new(&down_segment.baseline.from(),
|
||||||
|
&rest_down_segment.baseline.from());
|
||||||
|
if down_line_segment.to_y() > tile_bottom {
|
||||||
|
let (down_upper_part, _) = down_line_segment.split_at_y(tile_bottom);
|
||||||
|
self.crossing = down_upper_part.to();
|
||||||
|
|
||||||
|
let mut upper_part = down_upper_part;
|
||||||
|
if winds_up {
|
||||||
|
upper_part = upper_part.reversed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
println!("... cubic crossed tile Y {}; down_line_segment={:#?} down_rest={:#?}",
|
||||||
|
tile_bottom,
|
||||||
|
down_line_segment,
|
||||||
|
rest_down_segment);
|
||||||
|
*/
|
||||||
|
|
||||||
|
generate_fill_primitives_for_line(upper_part, built_object, tile_y);
|
||||||
|
|
||||||
|
let mut rest_segment = rest_down_segment;
|
||||||
|
if winds_up {
|
||||||
|
rest_segment = rest_segment.reversed();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.segment = rest_segment;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
down_segment = rest_down_segment;
|
||||||
|
|
||||||
|
let mut line_segment = down_line_segment;
|
||||||
|
if winds_up {
|
||||||
|
line_segment = line_segment.reversed();
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_fill_primitives_for_line(line_segment, built_object, tile_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(pcwalton): Optimize this better with SIMD!
|
||||||
|
fn generate_fill_primitives_for_line(mut segment: LineSegmentF32,
|
||||||
|
built_object: &mut BuiltObject,
|
||||||
|
tile_y: i16) {
|
||||||
|
/*
|
||||||
|
println!("... generate_fill_primitives_for_line(): segment={:?} tile_y={} ({}-{})",
|
||||||
|
segment,
|
||||||
|
tile_y,
|
||||||
|
tile_y as f32 * TILE_HEIGHT as f32,
|
||||||
|
(tile_y + 1) as f32 * TILE_HEIGHT as f32);
|
||||||
|
*/
|
||||||
|
|
||||||
|
let winding = segment.from_x() > segment.to_x();
|
||||||
|
let (segment_left, segment_right) = if !winding {
|
||||||
|
(segment.from_x(), segment.to_x())
|
||||||
|
} else {
|
||||||
|
(segment.to_x(), segment.from_x())
|
||||||
|
};
|
||||||
|
|
||||||
|
let segment_tile_left = (f32::floor(segment_left) as i32 / TILE_WIDTH as i32) as i16;
|
||||||
|
let segment_tile_right = alignup_i32(f32::ceil(segment_right) as i32,
|
||||||
|
TILE_WIDTH as i32) as i16;
|
||||||
|
|
||||||
|
for subsegment_tile_x in segment_tile_left..segment_tile_right {
|
||||||
|
let (mut fill_from, mut fill_to) = (segment.from(), segment.to());
|
||||||
|
let subsegment_tile_right = ((subsegment_tile_x as i32 + 1) * TILE_HEIGHT as i32)
|
||||||
|
as f32;
|
||||||
|
if subsegment_tile_right < segment_right {
|
||||||
|
let x = subsegment_tile_right;
|
||||||
|
let point = Point2DF32::new(x, segment.solve_y_for_x(x));
|
||||||
|
if !winding {
|
||||||
|
fill_to = point;
|
||||||
|
segment = LineSegmentF32::new(&point, &segment.to());
|
||||||
|
} else {
|
||||||
|
fill_from = point;
|
||||||
|
segment = LineSegmentF32::new(&segment.from(), &point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let fill_segment = LineSegmentF32::new(&fill_from, &fill_to);
|
||||||
|
built_object.add_fill(&fill_segment, subsegment_tile_x, tile_y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd<ActiveEdge> for ActiveEdge {
|
impl PartialOrd<ActiveEdge> for ActiveEdge {
|
||||||
// FIXME(pcwalton): SIMDify?
|
|
||||||
fn partial_cmp(&self, other: &ActiveEdge) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &ActiveEdge) -> Option<Ordering> {
|
||||||
let this_x = if self.segment.baseline.from_y() < self.segment.baseline.to_y() {
|
self.crossing.x().partial_cmp(&other.crossing.x())
|
||||||
self.segment.baseline.from_x()
|
|
||||||
} else {
|
|
||||||
self.segment.baseline.to_x()
|
|
||||||
};
|
|
||||||
let other_x = if other.segment.baseline.from_y() < other.segment.baseline.to_y() {
|
|
||||||
other.segment.baseline.from_x()
|
|
||||||
} else {
|
|
||||||
other.segment.baseline.to_x()
|
|
||||||
};
|
|
||||||
this_x.partial_cmp(&other_x)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2402,6 +2534,8 @@ impl LineSegmentF32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split(&self, t: f32) -> (LineSegmentF32, LineSegmentF32) {
|
fn split(&self, t: f32) -> (LineSegmentF32, LineSegmentF32) {
|
||||||
|
//println!("LineSegmentF32::split(t={})", t);
|
||||||
|
debug_assert!(t >= 0.0 && t <= 1.0);
|
||||||
unsafe {
|
unsafe {
|
||||||
let from_from = Sse41::castpd_ps(Sse41::unpacklo_pd(Sse41::castps_pd(self.0),
|
let from_from = Sse41::castpd_ps(Sse41::unpacklo_pd(Sse41::castps_pd(self.0),
|
||||||
Sse41::castps_pd(self.0)));
|
Sse41::castps_pd(self.0)));
|
||||||
|
@ -2416,6 +2550,11 @@ impl LineSegmentF32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(pcwalton): Optimize?
|
||||||
|
fn split_at_y(&self, y: f32) -> (LineSegmentF32, LineSegmentF32) {
|
||||||
|
self.split((y - self.from_y()) / (self.to_y() - self.from_y()))
|
||||||
|
}
|
||||||
|
|
||||||
fn to_line_segment_u4(&self) -> LineSegmentU4 {
|
fn to_line_segment_u4(&self) -> LineSegmentU4 {
|
||||||
unsafe {
|
unsafe {
|
||||||
let values = Sse41::cvtps_epi32(Sse41::fastfloor_ps(self.0));
|
let values = Sse41::cvtps_epi32(Sse41::fastfloor_ps(self.0));
|
||||||
|
@ -2481,53 +2620,6 @@ struct LineSegmentU4(u16);
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
struct LineSegmentU8(u32);
|
struct LineSegmentU8(u32);
|
||||||
|
|
||||||
// Curve flattening
|
|
||||||
|
|
||||||
struct CubicCurveFlattener {
|
|
||||||
curve: Segment,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Iterator for CubicCurveFlattener {
|
|
||||||
type Item = Point2DF32;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Point2DF32> {
|
|
||||||
let s2inv;
|
|
||||||
unsafe {
|
|
||||||
let (baseline, ctrl) = (self.curve.baseline.0, self.curve.ctrl.0);
|
|
||||||
let from_from = Sse41::shuffle_ps(baseline, baseline, 0b01000100);
|
|
||||||
|
|
||||||
let v0102 = Sse41::sub_ps(ctrl, from_from);
|
|
||||||
|
|
||||||
// v01.x v01.y v02.x v02.y
|
|
||||||
// * v01.x v01.y v01.y v01.x
|
|
||||||
// -------------------------
|
|
||||||
// v01.x^2 v01.y^2 ad bc
|
|
||||||
// | | | |
|
|
||||||
// +-------+ +-----+
|
|
||||||
// + -
|
|
||||||
// v01 len^2 determinant
|
|
||||||
let products = Sse41::mul_ps(v0102, Sse41::shuffle_ps(v0102, v0102, 0b00010100));
|
|
||||||
|
|
||||||
let det = products[2] - products[3];
|
|
||||||
if det == 0.0 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
s2inv = (products[0] + products[1]).sqrt() / det;
|
|
||||||
}
|
|
||||||
|
|
||||||
let t = 2.0 * ((FLATTENING_TOLERANCE / 3.0) * s2inv.abs()).sqrt();
|
|
||||||
if t >= 1.0 - EPSILON || t == 0.0 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.curve = self.curve.as_cubic_segment().split_after(t);
|
|
||||||
return Some(self.curve.baseline.from());
|
|
||||||
|
|
||||||
const EPSILON: f32 = 0.005;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Path utilities
|
// Path utilities
|
||||||
|
|
||||||
const TINY_EPSILON: f32 = 0.1;
|
const TINY_EPSILON: f32 = 0.1;
|
||||||
|
|
Loading…
Reference in New Issue