/// <summary>Clears this scan line.</summary> public void Clear() { if (First == null) { return; } if (Scanner.PooledPixels < Scanner.MaxPoolCount) { // We're ok to add to the pool. // This will likely go over max, but that's fine - these things are tiny. Scanner.PooledPixels += Count; Last.Next = Scanner.FirstPooled; Scanner.FirstPooled = First; } First = null; Last = null; Count = 0; }
/// <summary>Clears this scan line.</summary> public void Clear(){ if(First==null){ return; } if(Scanner.PooledPixels<Scanner.MaxPoolCount){ // We're ok to add to the pool. // This will likely go over max, but that's fine - these things are tiny. Scanner.PooledPixels+=Count; Last.Next=Scanner.FirstPooled; Scanner.FirstPooled=First; } First=null; Last=null; Count=0; }
/// <summary>Adds a given pixel to the scanline buffer.</summary> private void AddPixel(float x, int y) { ScannerPixel pixel; if (FirstPooled == null) { pixel = new ScannerPixel(); } else { pixel = FirstPooled; FirstPooled = pixel.Next; pixel.Next = null; PooledPixels--; } // Set it up: pixel.X = x + HalfBlurSpread; // Add it: ScanLineBuffer[y].Add(pixel); }
/// <summary>Flattens this scan lines data into our base set of pixels.</summary> public SubScanPixel[] Flatten() { // Create the set: SubScanPixel[] set = new SubScanPixel[Count]; // Copy each: ScannerPixel current = First; int index = 0; while (current != null) { // Create and add in our pixel: set[index] = new SubScanPixel((ushort)current.X, current.Fill); // Increase the index: index++; // Hop to the next one: current = current.Next; } return(set); }
/// <summary>Adds the given intersect pixel to this scan line.</summary> public void Add(ScannerPixel pixel) { // Clear prev/next: pixel.Previous = null; pixel.Next = null; // Increase the count: Count++; if (First == null) { // One and only: First = Last = pixel; return; } if (pixel.X > Last.X) { // It's the furthest over. Stick it on the end: pixel.Previous = Last; Last = Last.Next = pixel; } else { // Find the place it needs to go between: ScannerPixel current = Last; while (current != null) { if (pixel.X <= current.X) { if (current.Previous != null) { if (pixel.X <= current.Previous.X) { current = current.Previous; continue; } // Ensure next is connected too. pixel.Previous = current.Previous; pixel.Previous.Next = pixel; } else { // It goes at the start: First = pixel; } // It goes before current. pixel.Next = current; current.Previous = pixel; return; } current = current.Previous; } } }
/// <summary>Adds a given pixel to the scanline buffer.</summary> private void AddPixel(float x,int y){ ScannerPixel pixel; if(FirstPooled==null){ pixel=new ScannerPixel(); }else{ pixel=FirstPooled; FirstPooled=pixel.Next; pixel.Next=null; PooledPixels--; } // Set it up: pixel.X=x + HalfBlurSpread; // Add it: ScanLineBuffer[y].Add(pixel); }
/// <summary>Adds the given intersect pixel to this scan line.</summary> public void Add(ScannerPixel pixel){ // Clear prev/next: pixel.Previous=null; pixel.Next=null; // Increase the count: Count++; if(First==null){ // One and only: First=Last=pixel; return; } if(pixel.X>Last.X){ // It's the furthest over. Stick it on the end: pixel.Previous=Last; Last=Last.Next=pixel; }else{ // Find the place it needs to go between: ScannerPixel current=Last; while(current!=null){ if(pixel.X<=current.X){ if(current.Previous!=null){ if(pixel.X<=current.Previous.X){ current=current.Previous; continue; } // Ensure next is connected too. pixel.Previous=current.Previous; pixel.Previous.Next=pixel; }else{ // It goes at the start: First=pixel; } // It goes before current. pixel.Next=current; current.Previous=pixel; return; } current=current.Previous; } } }
/// <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; */ }