/// <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; */ }
/// <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; */ }