public void AddMesh(VectorPath path, float xOffset, float yOffset, Triangulator triangulator) { XOffset = xOffset; YOffset = yOffset; StartIndex = CurrentVertex; // Start triangulator (also clears VC): triangulator.Reset(); // Update triangulator vert offset: triangulator.VertexOffset = CurrentVertex; // Triangulator chain: FirstInContour = null; LastInContour = null; VectorPoint contourStart = path.FirstPathNode; VectorPoint current = contourStart; // Update acc: Triangulator = triangulator; CurrentAccuracy = TextureCameras.Accuracy; Winding = true; while (current != null) { if (current.IsClose || current.Next == null) { // Triangulate current listing: if (FirstInContour != null) { // Triangulate: int triangleCount = triangulator.VertexCount - 2; // Complete the triangulator chain: triangulator.Complete(FirstInContour, LastInContour); #if !TEXT_IS_CLOCKWISE // Text directionality: triangulator.FindWinding(); Winding = triangulator.Clockwise; #endif if (triangleCount > 0) { triangulator.Triangulate(Triangles, triangleCount, CurrentTriangle); CurrentTriangle += triangleCount * 3; } } FirstInContour = null; LastInContour = null; // Add the edges: VectorPoint edge = contourStart; // Reset: Moved = true; CloseBeforeThis = true; // Update acc: Triangulator = null; CurrentAccuracy = TextureCameras.Accuracy; while (edge != null) { if (edge.IsClose || edge.Next == null) { IsClosing = true; CloseX = edge.X; CloseY = edge.Y; } else { IsClosing = false; } if (edge.IsCurve || !edge.HasLine) { // Curves and moveto's edge.ComputeLinePoints(this); } else { if (TextureCameras.SD) { // Add the last point only: AddPoint(edge.X, edge.Y); } else { edge.ComputeLinePoints(this); } } if (IsClosing) { // End of this contour: break; } CloseBeforeThis = false; // Hop to the next one: edge = edge.Next; } // Clear VC: triangulator.VertexCount = 0; // Update triangulator vert offset: triangulator.VertexOffset = CurrentVertex; // Update start: contourStart = current.Next; // Restore acc/ triangulator: Triangulator = triangulator; CurrentAccuracy = TextureCameras.Accuracy; } else if (current.HasLine && current.IsCurve) { current.ComputeLinePoints(this); } else { AddPoint(current.X, current.Y); } // Hop to the next one: current = current.Next; } }
/// <summary>Rasterises a generic vector.</summary> public bool Rasterise(VectorPath glyph, Color32[] atlasPixels, int atlasWidth, int baseIndex, int width, int height, float hOffset, float vOffset, Color32 fill, bool clear) { if (glyph.FirstPathNode == null) { return(false); } if (RequiresStart) { // Start now: Start(); } PreviousY = -1; // Offset the rasteriser: VerticalOffset = vOffset; HorizontalOffset = hOffset; if (Blurred) { // Add some space for the blur: width += BlurSpread; height += BlurSpread; } if (clear) { int atlasIndex = baseIndex; for (int i = 0; i < height; i++) { // Clear the row: Array.Clear(atlasPixels, atlasIndex, width); atlasIndex += atlasWidth; } } if (Blurred) { if (width > (DistanceCacheWidth * BlurSpread)) { // Resize time! DistanceCacheWidth = ((width - 1) / BlurSpread) + 1; // Rebuild: CreateDistanceCache(); } else if (height > (DistanceCacheHeight * BlurSpread)) { // Resize time! DistanceCacheHeight = ((height - 1) / BlurSpread) + 1; // Rebuild: CreateDistanceCache(); } else { // Clear the cache: ClearDistanceCache(); } } if (ScanLineBuffer == null || ScanLineBuffer.Length <= height) { // Create the shared buffer: ScanLineBuffer = new ScannerScanLine[height + 1]; } // Clear the buffer: for (int i = 0; i <= height; i++) { // Grab the line: ScannerScanLine line = ScanLineBuffer[i]; if (line == null) { // Create it now: ScanLineBuffer[i] = new ScannerScanLine(this); continue; } // Empty the line (into the pool): line.Clear(); } // For each line in the glyph, resolve it into a series of points. // Each one is at most one pixel away from the previous point. // Get the next point (exists due to check above): VectorPoint point = glyph.FirstPathNode; while (point != null) { // Compute the points along the line which are fairly dense and regularly spaced (when possible): point.ComputeLinePoints(this); if ((point.IsClose || point.Next == null) && LineChangeY != -1 && MoveToY == -1) { // Test if we need to add our special point: if (LineChangeWentUp != WentUp) { if (LineChangeY >= 0 && LineChangeY < ScanLineBuffer.Length) { AddPixel(LineChangeX, LineChangeY); } } // Clear Y: LineChangeY = -1; } // Go to the next one: point = point.Next; } int blurOffset = HalfBlurSpread; // For each scan line, skipping the ones we know won't contain anything at this point: int verticalMax = height - blurOffset; // Stop only one blurspread from the end - we know there's nothing beyond that. int lineMax = width - blurOffset; // The current pixel index - offset over the blur spread: int pixelIndex = baseIndex + (atlasWidth * blurOffset); if (Blurred) { // SDF mode for (int i = blurOffset; i < verticalMax; i++) { // Grab the line: ScannerScanLine line = ScanLineBuffer[i]; if (line.First == null) { // Advance to the next line: pixelIndex += atlasWidth; continue; } // Grab the first pixel (each one represents an intersect): ScannerPixel inwardPixel = line.First; while (inwardPixel != null) { // Grab the next one: ScannerPixel outwardPixel = inwardPixel.Next; if (outwardPixel == null) { break; } // Index of the last pixel (exclusive): int max = outwardPixel.PixelIndex; if (max > lineMax) { // Clip it: max = lineMax; } // The section from inwardPixel to outwardPixel is filled. for (int p = inwardPixel.PixelIndex; p < max; p++) { // Fill: atlasPixels[pixelIndex + p] = fill; } if (outwardPixel == null) { // No more pairs: break; } // Go to the next intersect: inwardPixel = outwardPixel.Next; } // Advance to the next line: pixelIndex += atlasWidth; } // "blur" time! int cacheSquareIndex = 0; int blurSpread = BlurSpread; int atlasSize = atlasPixels.Length; // For each distance cache square.. for (int dY = 0; dY < DistanceCacheHeight; dY++) { for (int dX = 0; dX < DistanceCacheWidth; dX++) { // Get the square: DistanceCacheSquare currentSquare = DistanceCache[cacheSquareIndex]; pixelIndex = baseIndex + (currentSquare.PixelIndexY * atlasWidth) + currentSquare.PixelIndexX; if (currentSquare.InRange) { // Get the FX/FY: float squareFX = currentSquare.XOffset; float fY = currentSquare.YOffset; float fX = squareFX; // Grab the squares search set: DistanceCacheSquare[] toSearch = currentSquare.SearchSet; // How many? int searchCount = toSearch.Length; // Reset pixel index: pixelIndex = baseIndex + (currentSquare.PixelIndexY * atlasWidth) + currentSquare.PixelIndexX; // For each pixel in this square.. for (int y = 0; y < blurSpread; y++) { if (pixelIndex >= atlasSize) { break; } for (int x = 0; x < blurSpread; x++) { // Where's the pixel? int fullIndex = pixelIndex + x; if (fullIndex >= atlasSize) { break; } // Must be black otherwise we'll ignore it. if (atlasPixels[fullIndex].r != 0) { // It has colour. Skip. // Move float x along: fX++; continue; } // Start doing distance tests - look in all the nearest squares: float bestDistance = float.MaxValue; for (int i = 0; i < searchCount; i++) { DistanceCacheSquare square = toSearch[i]; if (square.Count == 0) { // Ignore empty square. continue; } // Temp grab the point set: List <DistanceCachePoint> points = square.Points; // How many points? int pointCount = points.Count; // For each node in the square, find the (relative) nearest. for (int p = 0; p < pointCount; p++) { // Get the point: DistanceCachePoint cachePoint = points[p]; // Distance check: float deltaX = cachePoint.X - fX; float deltaY = cachePoint.Y - fY; // Full distance: float distance = (deltaX * deltaX) + (deltaY * deltaY); if (distance < bestDistance) { // Closest distance found: bestDistance = distance; } } } // Result value: byte distancePixel; if (bestDistance > MaxDistanceSquared) { // The pixel should be black: distancePixel = 0; } else { // Map the distance to an accurate distance, and put it in range: distancePixel = (byte)(255f - (Math.Sqrt(bestDistance) * DistanceAdjuster)); } // Write the result: atlasPixels[fullIndex] = new Color32(fill.r, fill.g, fill.b, distancePixel); // Move float x along: fX++; } // Move float y along: fY++; fX = squareFX; // Move pixel index up a row: pixelIndex += atlasWidth; } } cacheSquareIndex++; } } } else { // Got alpha? bool fillAlpha = (fill.a != 255 && !clear); // "Invert" fill alpha: int alphaInvert = 255 - fill.a; int fillRA = fill.r * fill.a; int fillGA = fill.g * fill.a; int fillBA = fill.b * fill.a; for (int i = blurOffset; i < verticalMax; i++) { // Grab the line: ScannerScanLine line = ScanLineBuffer[i]; if (line.First == null) { // Advance to the next line: pixelIndex += atlasWidth; continue; } // Grab the first pixel (each one represents an intersect): ScannerPixel inwardPixel = line.First; while (inwardPixel != null) { // Grab the next one: ScannerPixel outwardPixel = inwardPixel.Next; if (outwardPixel == null) { break; } // Index of the last pixel (exclusive): int max = outwardPixel.PixelIndex; if (max > lineMax) { // Clip it: max = lineMax; } // The section from inwardPixel to outwardPixel is filled. if (fillAlpha) { for (int p = inwardPixel.PixelIndex; p < max; p++) { // Get the index: int index = pixelIndex + p; // Grab the colour: Color32 colour = atlasPixels[index]; // Alpha blend: int dstA = (colour.a * alphaInvert) / 255; int cA = fill.a + dstA; colour.a = (byte)cA; colour.r = (byte)((fillRA + colour.r * dstA) / cA); colour.g = (byte)((fillGA + colour.g * dstA) / cA); colour.b = (byte)((fillBA + colour.b * dstA) / cA); // Fill: atlasPixels[index] = colour; } } else { for (int p = inwardPixel.PixelIndex; p < max; p++) { // Fill: atlasPixels[pixelIndex + p] = fill; } } if (outwardPixel == null) { // No more pairs: break; } // Go to the next intersect: inwardPixel = outwardPixel.Next; } // Advance to the next line: pixelIndex += atlasWidth; } } return(true); /* * // Create the output. * SubScanPixel[][] intersects=new SubScanPixel[height][]; * * for(int i=0;i<height;i++){ * * // Flatten the scan line into the pixel buffer: * intersects[i]=ScanLineBuffer[i].Flatten(); * * } * * // Finally create the raster: * RasterGlyph raster=new RasterGlyph(); * * // Apply intersects: * raster.Intersects=intersects; * * // Apply width: * raster.Width=width; * * return raster; */ }