#[macro_use] mod fix; #[macro_use] mod helpers; #[macro_use] mod real; mod bezier; #[macro_use] mod aarasterizer; mod hwrasterizer; mod aacoverage; mod hwvertexbuffer;
mod types; mod geometry_sink; mod matrix;
mod nullable_ref;
#[cfg(feature = "c_bindings")] pubmod c_bindings;
#[cfg(test)] mod tri_rasterize;
use aarasterizer::CheckValidRange28_4; use hwrasterizer::CHwRasterizer; use hwvertexbuffer::{CHwVertexBuffer, CHwVertexBufferBuilder}; use real::CFloatFPU; use types::{MilFillMode, PathPointTypeStart, MilPoint2F, MilPointAndSizeL, PathPointTypeLine, MilVertexFormat, MilVertexFormatAttribute, DynArray, BYTE, PathPointTypeBezier, PathPointTypeCloseSubpath, CMILSurfaceRect, POINT};
impl PathBuilder { pubfn new() -> Self { Self {
points: Vec::new(),
types: Vec::new(),
initial_point: None,
current_point: None,
in_shape: false,
fill_mode: FillMode::EvenOdd,
outside_bounds: None,
need_inside: true,
valid_range: true,
rasterization_truncates: false,
}
} fn reset(&mutself) {
*self = Self {
points: std::mem::take(&mutself.points),
types: std::mem::take(&mutself.types),
..Self::new()
}; self.points.clear(); self.types.clear();
} fn add_point(&mutself, x: f32, y: f32) { self.current_point = Some(MilPoint2F{X: x, Y: y}); // Transform from pixel corner at 0.0 to pixel center at 0.0. Scale into 28.4 range. // Validate that the point before rounding is within expected bounds for the rasterizer. let (x, y) = ((x - 0.5) * 16.0, (y - 0.5) * 16.0); self.valid_range = self.valid_range && CheckValidRange28_4(x, y); self.points.push(POINT {
x: CFloatFPU::Round(x),
y: CFloatFPU::Round(y),
});
} pubfn line_to(&mutself, x: f32, y: f32) { iflet Some(initial_point) = self.initial_point { if !self.in_shape { self.types.push(PathPointTypeStart); self.add_point(initial_point.X, initial_point.Y); self.in_shape = true;
} self.types.push(PathPointTypeLine); self.add_point(x, y);
} else { self.initial_point = Some(MilPoint2F{X: x, Y: y})
}
} pubfn move_to(&mutself, x: f32, y: f32) { self.in_shape = false; self.initial_point = Some(MilPoint2F{X: x, Y: y}); self.current_point = self.initial_point;
} pubfn curve_to(&mutself, c1x: f32, c1y: f32, c2x: f32, c2y: f32, x: f32, y: f32) { let initial_point = matchself.initial_point {
Some(initial_point) => initial_point,
None => MilPoint2F{X:c1x, Y:c1y}
}; if !self.in_shape { self.types.push(PathPointTypeStart); self.add_point(initial_point.X, initial_point.Y); self.initial_point = Some(initial_point); self.in_shape = true;
} self.types.push(PathPointTypeBezier); self.add_point(c1x, c1y); self.add_point(c2x, c2y); self.add_point(x, y);
} pubfn quad_to(&mutself, cx: f32, cy: f32, x: f32, y: f32) { // For now we just implement quad_to on top of curve_to. // Long term we probably want to support quad curves // directly. let c0 = matchself.current_point {
Some(current_point) => current_point,
None => MilPoint2F{X:cx, Y:cy}
};
let c1x = c0.X + (2./3.) * (cx - c0.X); let c1y = c0.Y + (2./3.) * (cy - c0.Y);
let c2x = x + (2./3.) * (cx - x); let c2y = y + (2./3.) * (cy - y);
self.curve_to(c1x, c1y, c2x, c2y, x, y);
} pubfn close(&mutself) { ifself.in_shape { // Only close the path if we are inside a shape. Otherwise, the point // should be safe to elide. iflet Some(last) = self.types.last_mut() {
*last |= PathPointTypeCloseSubpath;
} self.in_shape = false;
} // Close must install a new initial point that is the same as the // initial point of the just-closed sub-path. Thus, just leave the // initial point unchanged. self.current_point = self.initial_point;
} pubfn set_fill_mode(&mutself, fill_mode: FillMode) { self.fill_mode = fill_mode;
} /// Enables rendering geometry for areas outside the shape but /// within the bounds. These areas will be created with /// zero alpha. /// /// This is useful for creating geometry for other blend modes. /// For example: /// - `IN(dest, geometry)` can be done with `outside_bounds` and `need_inside = false` /// - `IN(dest, geometry, alpha)` can be done with `outside_bounds` and `need_inside = true` /// /// Note: trapezoidal areas won't be clipped to outside_bounds pubfn set_outside_bounds(&mutself, outside_bounds: Option<(i32, i32, i32, i32)>, need_inside: bool) { self.outside_bounds = outside_bounds.map(|r| CMILSurfaceRect { left: r.0, top: r.1, right: r.2, bottom: r.3 }); self.need_inside = need_inside;
}
/// Set this to true if post vertex shader coordinates are converted to fixed point /// via truncation. This has been observed with OpenGL on AMD GPUs on macOS. pubfn set_rasterization_truncates(&mutself, rasterization_truncates: bool) { self.rasterization_truncates = rasterization_truncates;
}
/// Note: trapezoidal areas won't necessarily be clipped to the clip rect pubfn rasterize_to_tri_list(&self, clip_x: i32, clip_y: i32, clip_width: i32, clip_height: i32) -> Box<[OutputVertex]> { if !self.valid_range { // If any of the points are outside of valid 28.4 range, then just return an empty triangle list. returnBox::new([]);
} let (x, y, width, height, need_outside) = iflet Some(CMILSurfaceRect { left, top, right, bottom }) = self.outside_bounds { let x0 = clip_x.max(left); let y0 = clip_y.max(top); let x1 = (clip_x + clip_width).min(right); let y1 = (clip_y + clip_height).min(bottom);
(x0, y0, x1 - x0, y1 - y0, true)
} else {
(clip_x, clip_y, clip_width, clip_height, false)
};
rasterize_to_tri_list(self.fill_mode, &self.types, &e='color:red'>self.points, x, y, width, height, self.need_inside, need_outside, self.rasterization_truncates, None)
.flush_output()
}
// Converts a path that is specified as an array of edge types, each associated with a fixed number // of points that are serialized to the points array. Edge types are specified via PathPointType // masks, whereas points must be supplied in 28.4 signed fixed-point format. By default, users can // fill the inside of the path excluding the outside. It may alternatively be desirable to fill the // outside the path out to the clip boundary, optionally keeping the inside. PathBuilder may be // used instead as a simpler interface to this function that handles building the path arrays. pubfn rasterize_to_tri_list<'a>(
fill_mode: FillMode,
types: &[BYTE],
points: &[POINT],
clip_x: i32,
clip_y: i32,
clip_width: i32,
clip_height: i32,
need_inside: bool,
need_outside: bool,
rasterization_truncates: bool,
output_buffer: Option<&'a mut [OutputVertex]>,
) -> CHwVertexBuffer<'a> { let clipRect = MilPointAndSizeL {
X: clip_x,
Y: clip_y,
Width: clip_width,
Height: clip_height,
};
let mil_fill_mode = match fill_mode {
FillMode::EvenOdd => MilFillMode::Alternate,
FillMode::Winding => MilFillMode::Winding,
};
let m_mvfIn: MilVertexFormat = MilVertexFormatAttribute::MILVFAttrXY as MilVertexFormat; let m_mvfGenerated: MilVertexFormat = MilVertexFormatAttribute::MILVFAttrNone as MilVertexFormat; //let mvfaAALocation = MILVFAttrNone; const HWPIPELINE_ANTIALIAS_LOCATION: MilVertexFormatAttribute = MilVertexFormatAttribute::MILVFAttrDiffuse; let mvfaAALocation = HWPIPELINE_ANTIALIAS_LOCATION;
#[test] fn range() { // test for a start point out of range letmut p = PathBuilder::new();
p.curve_to(8.872974e16, 0., 0., 0., 0., 0.); let result = p.rasterize_to_tri_list(0, 0, 100, 100);
assert_eq!(result.len(), 0);
// test for a subsequent point out of range letmut p = PathBuilder::new();
p.curve_to(0., 0., 8.872974e16, 0., 0., 0.); let result = p.rasterize_to_tri_list(0, 0, 100, 100);
assert_eq!(result.len(), 0);
}
#[test] fn multiple_starts() { letmut p = PathBuilder::new();
p.line_to(10., 10.);
p.move_to(0., 0.); let result = p.rasterize_to_tri_list(0, 0, 100, 100);
assert_eq!(result.len(), 0);
}
#[test] fn clip_edge() { letmut p = PathBuilder::new(); // tests the bigNumerator < 0 case of aarasterizer::ClipEdge
p.curve_to(-24., -10., -300., 119., 0.0, 0.0); let result = p.rasterize_to_tri_list(0, 0, 100, 100); // The edge merging only happens between points inside the enumerate buffer. This means // that the vertex output can depend on the size of the enumerate buffer because there // the number of edges and positions of vertices will change depending on edge merging. if ENUMERATE_BUFFER_NUMBER!() == 32 {
assert_eq!(result.len(), 111);
} else {
assert_eq!(result.len(), 171);
}
assert_eq!(calculate_hash(&rasterize_to_mask(&result, 100, 100)), 0x50b887b09a4c16e);
}
#[test] fn traps_outside_bounds() { letmut p = PathBuilder::new();
p.move_to(10., 10.0);
p.line_to(30., 10.);
p.line_to(50., 20.);
p.line_to(30., 30.);
p.line_to(10., 30.);
p.close(); // The generated trapezoids are not necessarily clipped to the outside bounds rect // and in this case the outside bounds geometry ends up drawing on top of the // edge geometry which could be considered a bug.
p.set_outside_bounds(Some((0, 0, 50, 30)), true); let result = p.rasterize_to_tri_list(0, 0, 100, 100);
assert_eq!(calculate_hash(&rasterize_to_mask(&result, 100, 100)), 0x6514e3d79d641f09);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.