Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  lib.rs   Sprache: unbekannt

 
/*!
Converts a 2D path into a set of vertices of a triangle strip mesh that represents the antialiased fill of that path.

```rust
    use wpf_gpu_raster::PathBuilder;
    let mut p = PathBuilder::new();
    p.move_to(10., 10.);
    p.line_to(40., 10.);
    p.line_to(40., 40.);
    let result = p.rasterize_to_tri_list(0, 0, 100, 100);
```

*/
#![allow(unused_parens)]
#![allow(overflowing_literals)]
#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]
#![allow(dead_code)]
#![allow(unused_macros)]

#[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")]
pub mod 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};

#[repr(C)]
#[derive(Clone, Debug, Default)]
pub struct OutputVertex {
    pub x: f32,
    pub y: f32,
    pub coverage: f32
}

#[repr(C)]
#[derive(Copy, Clone)]
pub enum FillMode {
    EvenOdd = 0,
    Winding = 1,
}

impl Default for FillMode {
    fn default() -> Self {
        FillMode::EvenOdd
    }
}

#[derive(Clone, Default)]
pub struct OutputPath {
    fill_mode: FillMode,
    points: Box<[POINT]>,
    types: Box<[BYTE]>,
}

impl std::hash::Hash for OutputVertex {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.x.to_bits().hash(state);
        self.y.to_bits().hash(state);
        self.coverage.to_bits().hash(state);
    }
}

pub struct PathBuilder {
    points: DynArray<POINT>,
    types: DynArray<BYTE>,
    initial_point: Option<MilPoint2F>,
    current_point: Option<MilPoint2F>,
    in_shape: bool,
    fill_mode: FillMode,
    outside_bounds: Option<CMILSurfaceRect>,
    need_inside: bool,
    valid_range: bool,
    rasterization_truncates: bool,
}

impl PathBuilder {
    pub fn 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(&mut self) {
        *self = Self {
            points: std::mem::take(&mut self.points),
            types: std::mem::take(&mut self.types),
            ..Self::new()
        };
        self.points.clear();
        self.types.clear();
    }
    fn add_point(&mut self, 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),
        });
    }
    pub fn line_to(&mut self, x: f32, y: f32) {
        if let 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})
        }
    }
    pub fn move_to(&mut self, x: f32, y: f32) {
        self.in_shape = false;
        self.initial_point = Some(MilPoint2F{X: x, Y: y});
        self.current_point = self.initial_point;
    }
    pub fn curve_to(&mut self, c1x: f32, c1y: f32, c2x: f32, c2y: f32, x: f32, y: f32) {
        let initial_point = match self.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);
    }
    pub fn quad_to(&mut self, 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 = match self.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);
    }
    pub fn close(&mut self) {
        if self.in_shape {
          // Only close the path if we are inside a shape. Otherwise, the point
          // should be safe to elide.
          if let 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;
    }
    pub fn set_fill_mode(&mut self, 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
    pub fn set_outside_bounds(&mut self, 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. 
    pub fn set_rasterization_truncates(&mut self, rasterization_truncates: bool) {
        self.rasterization_truncates = rasterization_truncates;
    }

    /// Note: trapezoidal areas won't necessarily be clipped to the clip rect
    pub fn 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.
            return Box::new([]);
        }
        let (x, y, width, height, need_outside) = if let 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, &self.points, x, y, width, height, self.need_inside, need_outside, self.rasterization_truncates, None)
            .flush_output()
    }

    pub fn get_path(&mut self) -> Option<OutputPath> {
        if self.valid_range && !self.points.is_empty() && !self.types.is_empty() {
            Some(OutputPath {
                fill_mode: self.fill_mode,
                points: Box::from(self.points.as_slice()),
                types: Box::from(self.types.as_slice()),
            })
        } else {
            None
        }
    }
}

// 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.
pub fn 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;

    let outside_bounds = if need_outside {
        Some(CMILSurfaceRect {
            left: clip_x,
            top: clip_y,
            right: clip_x + clip_width,
            bottom: clip_y + clip_height,
        })
    } else {
        None
    };

    let mut vertexBuffer = CHwVertexBuffer::new(rasterization_truncates, output_buffer);
    {
        let mut vertexBuilder = CHwVertexBufferBuilder::Create(
            m_mvfIn, m_mvfIn | m_mvfGenerated, mvfaAALocation, &mut vertexBuffer);
        vertexBuilder.SetOutsideBounds(outside_bounds.as_ref(), need_inside);
        vertexBuilder.BeginBuilding();
        {
            let mut rasterizer = CHwRasterizer::new(
                &mut vertexBuilder, mil_fill_mode, None, clipRect);
            rasterizer.SendGeometry(points, types);
        }
        vertexBuilder.EndBuilding();
    }

    vertexBuffer
}

#[cfg(test)]
mod tests {
    use std::{hash::{Hash, Hasher}, collections::hash_map::DefaultHasher};
    use crate::{*, tri_rasterize::rasterize_to_mask};
    fn calculate_hash<T: Hash>(t: &T) -> u64 {
        let mut s = DefaultHasher::new();
        t.hash(&mut s);
        s.finish()
    }
    #[test]
    fn basic() {
        let mut p = PathBuilder::new();
        p.move_to(10., 10.);
        p.line_to(10., 30.);
        p.line_to(30., 30.);
        p.line_to(30., 10.);
        p.close();
        let result = p.rasterize_to_tri_list(0, 0, 100, 100);
        assert_eq!(result.len(), 18);
        //assert_eq!(dbg!(calculate_hash(&result)), 0x5851570566450135);
        assert_eq!(calculate_hash(&rasterize_to_mask(&result, 100, 100)), 0xfbb7c3932059e240);
    }

    #[test]
    fn simple() {
        let mut p = PathBuilder::new();
        p.move_to(10., 10.);
        p.line_to(40., 10.);
        p.line_to(40., 40.);
        let result = p.rasterize_to_tri_list(0, 0, 100, 100);
        //assert_eq!(dbg!(calculate_hash(&result)), 0x81a9af7769f88e68);
        assert_eq!(calculate_hash(&rasterize_to_mask(&result, 100, 100)), 0x6d1595533d40ef92);
    }

    #[test]
    fn rust() {
        let mut p = PathBuilder::new();
        p.move_to(10., 10.);
        p.line_to(40., 10.);
        p.line_to(40., 40.);
        let result = p.rasterize_to_tri_list(0, 0, 100, 100);
        //assert_eq!(dbg!(calculate_hash(&result)), 0x81a9af7769f88e68);
        assert_eq!(calculate_hash(&rasterize_to_mask(&result, 100, 100)), 0x6d1595533d40ef92);
    }

    #[test]
    fn fill_mode() {
        let mut p = PathBuilder::new();
        p.move_to(10., 10.);
        p.line_to(40., 10.);
        p.line_to(40., 40.);
        p.line_to(10., 40.);
        p.close();
        p.move_to(15., 15.);
        p.line_to(35., 15.);
        p.line_to(35., 35.);
        p.line_to(15., 35.);
        p.close();
        let result = p.rasterize_to_tri_list(0, 0, 100, 100);
        //assert_eq!(dbg!(calculate_hash(&result)), 0xb34344234f2f75a8);
        assert_eq!(calculate_hash(&rasterize_to_mask(&result, 100, 100)), 0xc7bf999c56ccfc34);

        let mut p = PathBuilder::new();
        p.move_to(10., 10.);
        p.line_to(40., 10.);
        p.line_to(40., 40.);
        p.line_to(10., 40.);
        p.close();
        p.move_to(15., 15.);
        p.line_to(35., 15.);
        p.line_to(35., 35.);
        p.line_to(15., 35.);
        p.close();
        p.set_fill_mode(FillMode::Winding);
        let result = p.rasterize_to_tri_list(0, 0, 100, 100);
        //assert_eq!(dbg!(calculate_hash(&result)), 0xee4ecd8a738fc42c);
        assert_eq!(calculate_hash(&rasterize_to_mask(&result, 100, 100)), 0xfafad659db9a2efd);

    }

    #[test]
    fn range() {
        // test for a start point out of range
        let mut 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
        let mut 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() {
        let mut 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 path_closing() {
        let mut p = PathBuilder::new();
        p.curve_to(0., 0., 0., 0., 0., 32.0);
        p.close();
        p.curve_to(0., 0., 0., 0., 0., 32.0);
        let result = p.rasterize_to_tri_list(0, 0, 100, 100);
        assert_eq!(result.len(), 0);
    }

    #[test]
    fn curve() {
        let mut p = PathBuilder::new();
        p.move_to(10., 10.);
        p.curve_to(40., 10., 40., 10., 40., 40.);
        p.close();
        let result = p.rasterize_to_tri_list(0, 0, 100, 100);
        assert_eq!(calculate_hash(&rasterize_to_mask(&result, 100, 100)), 0xa92aae8dba7b8cd4);
        assert_eq!(dbg!(calculate_hash(&result)), 0x8dbc4d23f9bba38d);
    }

    #[test]
    fn partial_coverage_last_line() {
        let mut p = PathBuilder::new();

        p.move_to(10., 10.);
        p.line_to(40., 10.);
        p.line_to(40., 39.6);
        p.line_to(10., 39.6);

        let result = p.rasterize_to_tri_list(0, 0, 100, 100);
        assert_eq!(result.len(), 21);
        assert_eq!(calculate_hash(&rasterize_to_mask(&result, 100, 100)), 0xfa200c3bae144952);
        assert_eq!(dbg!(calculate_hash(&result)), 0xf90cb6afaadfb559);
    }

    #[test]
    fn delta_upper_bound() {
        let mut p = PathBuilder::new();
        p.move_to(-122.3 + 200.,84.285);
        p.curve_to(-122.3 + 200., 84.285, -122.2 + 200.,86.179, -123.03 + 200., 86.16);
        p.curve_to(-123.85 + 200., 86.141, -140.3 + 200., 38.066, -160.83 + 200., 40.309);
        p.curve_to(-160.83 + 200., 40.309, -143.05 + 200., 32.956,  -122.3 + 200., 84.285);
        p.close();

        let result = p.rasterize_to_tri_list(0, 0, 400, 400);
        assert_eq!(result.len(), 429);
        assert_eq!(calculate_hash(&rasterize_to_mask(&result, 100, 100)), 0x5e82d98fdb47a796);
        assert_eq!(dbg!(calculate_hash(&result)), 0x52d52992e249587a);
    }


    #[test]
    fn self_intersect() {
        let mut p = PathBuilder::new();
        p.move_to(10., 10.);
        p.line_to(40., 10.);
        p.line_to(10., 40.);
        p.line_to(40., 40.);
        p.close();
        let result = p.rasterize_to_tri_list(0, 0, 100, 100);
        assert_eq!(calculate_hash(&rasterize_to_mask(&result, 100, 100)), 0x49ecc769e1d4ec01);
        assert_eq!(dbg!(calculate_hash(&result)), 0xf10babef5c619d19);
    }

    #[test]
    fn grid() {
        let mut p = PathBuilder::new();

        for i in 0..200 {
            let offset = i as f32 * 1.3;
            p.move_to(0. + offset, -8.);
            p.line_to(0.5 + offset, -8.);
            p.line_to(0.5 + offset, 40.);
            p.line_to(0. + offset, 40.);
            p.close();
        }
        let result = p.rasterize_to_tri_list(0, 0, 100, 100);
        assert_eq!(result.len(), 12000);
        assert_eq!(calculate_hash(&rasterize_to_mask(&result, 100, 100)), 0x5a7df39d9e9292f0);
    }

    #[test]
    fn outside() {
        let mut p = PathBuilder::new();
        p.move_to(10., 10.);
        p.line_to(40., 10.);
        p.line_to(10., 40.);
        p.line_to(40., 40.);
        p.close();
        p.set_outside_bounds(Some((0, 0, 50, 50)), false);
        let result = p.rasterize_to_tri_list(0, 0, 100, 100);
        assert_eq!(calculate_hash(&rasterize_to_mask(&result, 100, 100)), 0x59403ddbb7e1d09a);
        assert_eq!(dbg!(calculate_hash(&result)), 0x805fd385e47e6f2);

        // ensure that adjusting the outside bounds changes the results
        p.set_outside_bounds(Some((5, 5, 50, 50)), false);
        let result = p.rasterize_to_tri_list(0, 0, 100, 100);
        assert_eq!(calculate_hash(&rasterize_to_mask(&result, 100, 100)), 0x59403ddbb7e1d09a);
        assert_eq!(dbg!(calculate_hash(&result)), 0xcec2ed688999c966);
    }

    #[test]
    fn outside_inside() {
        let mut p = PathBuilder::new();
        p.move_to(10., 10.);
        p.line_to(40., 10.);
        p.line_to(10., 40.);
        p.line_to(40., 40.);
        p.close();
        p.set_outside_bounds(Some((0, 0, 50, 50)), true);
        let result = p.rasterize_to_tri_list(0, 0, 100, 100);
        assert_eq!(calculate_hash(&rasterize_to_mask(&result, 100, 100)), 0x49ecc769e1d4ec01);
        assert_eq!(dbg!(calculate_hash(&result)), 0xaf76b42a5244d1ec);
    }

    #[test]
    fn outside_clipped() {
        let mut p = PathBuilder::new();
        p.move_to(10., 10.);
        p.line_to(10., 40.);
        p.line_to(90., 40.);
        p.line_to(40., 10.);
        p.close();
        p.set_outside_bounds(Some((0, 0, 50, 50)), false);
        let result = p.rasterize_to_tri_list(0, 0, 50, 50);
        assert_eq!(calculate_hash(&rasterize_to_mask(&result, 100, 100)), 0x3d2a08f5d0bac999);
        assert_eq!(dbg!(calculate_hash(&result)), 0xbd42b934ab52be39);
    }

    #[test]
    fn clip_edge() {
        let mut 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 enum_buffer_num() {
        let mut p = PathBuilder::new();
        p.curve_to(0.0, 0.0, 0.0, 12.0, 0.0, 44.919434);
        p.line_to(64.0, 36.0 );
        p.line_to(0.0, 80.0,);
        let result = p.rasterize_to_tri_list(0, 0, 100, 100);
        assert_eq!(result.len(), 300);
        assert_eq!(calculate_hash(&rasterize_to_mask(&result, 100, 100)), 0x659cc742f16b42f2);
    }

    #[test]
    fn fill_alternating_empty_interior_pairs() {
        let mut p = PathBuilder::new();
        p.line_to( 0., 2. );
        p.curve_to(0.0, 0.0,1., 6., 0.0, 0.0);
        let result = p.rasterize_to_tri_list(0, 0, 100, 100);
        assert_eq!(result.len(), 9);
        assert_eq!(calculate_hash(&rasterize_to_mask(&result, 100, 100)), 0x726606a662fe46a0);
    }

    #[test]
    fn fill_winding_empty_interior_pairs() {
        let mut p = PathBuilder::new();
        p.curve_to(45., 61., 0.09, 0., 0., 0.);
        p.curve_to(45., 61., 0.09, 0., 0., 0.);
        p.curve_to(0., 0., 0., 38., 0.09, 15.);
        p.set_fill_mode(FillMode::Winding);
        let result = p.rasterize_to_tri_list(0, 0, 100, 100);
        assert_eq!(result.len(), 462);
        assert_eq!(calculate_hash(&rasterize_to_mask(&result, 100, 100)), 0x651ea4ade5543087);
    }

    #[test]
    fn empty_fill() {
        let mut p = PathBuilder::new();
        p.move_to(0., 0.);
        p.line_to(10., 100.);
        let result = p.rasterize_to_tri_list(0, 0, 100, 100);
        assert_eq!(result.len(), 0);
    }

    #[test]
    fn rasterize_line() {
        let mut p = PathBuilder::new();
        p.move_to(1., 1.);
        p.line_to(2., 1.);
        p.line_to(2., 2.);
        p.line_to(1., 2.);
        p.close();
        let result = p.rasterize_to_tri_list(0, 0, 100, 100);
        let mask = rasterize_to_mask(&result, 3, 3);
        assert_eq!(&mask[..], &[0,   0, 0,
                                0, 255, 0,
                                0,   0, 0][..]);
    }

    #[test]
    fn triangle() {
        let mut p = PathBuilder::new();
        p.move_to(1., 10.);
        p.line_to(100., 13.);
        p.line_to(1., 16.);
        p.close();
        let result = p.rasterize_to_tri_list(0, 0, 100, 100);
        assert_eq!(calculate_hash(&rasterize_to_mask(&result, 100, 100)), 0x4757b0c5a19b02f0);
    }

    #[test]
    fn single_pixel() {
        let mut p = PathBuilder::new();
        p.move_to(1.5, 1.5);
        p.line_to(2., 1.5);
        p.line_to(2., 2.);
        p.line_to(1.5, 2.);
        p.close();
        let result = p.rasterize_to_tri_list(0, 0, 100, 100);
        assert_eq!(result.len(), 3);
        assert_eq!(calculate_hash(&rasterize_to_mask(&result, 4, 4)), 0x9f481fe5588e341c);
    }

    #[test]
    fn traps_outside_bounds() {
        let mut 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);

    }

    #[test]
    fn quad_to() {
        let mut p = PathBuilder::new();
        p.move_to(10., 10.0);
        p.quad_to(30., 10., 30., 30.);
        p.quad_to(10., 30., 30., 30.);
        p.quad_to(60., 30., 60., 10.);
        p.close();
        let result = p.rasterize_to_tri_list(0, 0, 70, 40);
        assert_eq!(result.len(), 279);
        assert_eq!(calculate_hash(&rasterize_to_mask(&result, 70, 40)), 0xbd2eec3cfe9bd30b);
    }

    #[test]
    fn close_after_move_to() {
        let mut p = PathBuilder::new();
        p.move_to(10., 0.);
        p.close();
        p.move_to(0., 0.);
        p.line_to(0., 10.);
        p.line_to(10., 10.);
        p.move_to(10., 0.);
        p.close();
        let result = p.rasterize_to_tri_list(0, 0, 20, 20);
        assert_eq!(result.len(), 27);
        assert_eq!(dbg!(calculate_hash(&result)), 0xecfdf5bdfa25a1dd);
    }
}

[ Dauer der Verarbeitung: 0.13 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge