using System.Collections; using System.Collections.Generic; using Godot; using System; using System.Threading.Tasks; using System.Linq; using TriangleNet.Geometry; namespace Rokojori { public class CPUTextureDilater { public static async Task Dilate( Texture2D texture, Func waiting, float alphaTreshold = 250f/255f ) { var dilater = new CPUTextureDilater(); dilater.alphaTreshold = alphaTreshold; dilater.waiting = waiting; var t = await dilater._Dilate( texture ); return t; } TextureCombinerBuffer _buffer; Func waiting; async Task _Dilate( Texture2D texture ) { _buffer = TextureCombinerBuffer.From( texture ); await _Process(); return _buffer.CreateImageTexture(); } public float alphaTreshold = 250f/255f; HashSet _processed = new HashSet(); HashSet _unprocessed = new HashSet(); HashSet _edges = new HashSet(); async Task _Process() { _buffer.UnblendBlack(); GrabInitialPixels(); if ( _unprocessed.Count == _buffer.numPixels ) { return; } int maxMessages = 100; int messageCounter = 0; int messageLimit = 200000; int max = 1000 * 1000; int it = 0; var random = LCG.WithSeed( 1984 ); while ( _edges.Count > 0 && it < max ) { it++; if ( messageCounter >= messageLimit ) { await waiting(); messageCounter = 0; RJLog.Log( RegexUtility._FF( 100f *_processed.Count / _buffer.numPixels ) + "%", "Valid: ", ( _processed.Count + _edges.Count + _unprocessed.Count ) == _buffer.numPixels, "P", _processed.Count, "E",_edges.Count, "U", _unprocessed.Count ); } else { messageCounter ++; } var edge = _edges.ElementAt( random.IntegerExclusive( _edges.Count ) ); _edges.Remove( edge ); if ( ! _processed.Contains( edge ) ) { var edgeColor = ComputeColor( edge ); _processed.Add( edge ); _buffer.SetIndexed( edge, edgeColor ); } AddEdgeNeighbors( edge ); } if ( _edges.Count > 0 && it == max ) { RJLog.Log( "Aborted, too many entries" ); } } void GrabInitialPixels() { for ( int i = 0; i < _buffer.numPixels; i++ ) { if ( ! HasColor( i ) ) { _unprocessed.Add( i ); } else { if ( IsEdge( i ) ) { _edges.Add( i ); } _processed.Add( i ); } } } void AddEdgeNeighbors( int i ) { var position = _buffer.ComputePositionFromIndex( i ); for ( int x = -1; x <= 1; x++ ) { for ( int y = -1; y <= 1; y++ ) { if ( x == 0 && y == 0 ) { continue; } var index = _buffer.ComputeIndexFromPosition( position.X + x, position.Y + y ); if ( index < 0 || index >= _buffer.numPixels ) { continue; } if ( ! _unprocessed.Contains( index ) ) { continue; } _unprocessed.Remove( index ); _edges.Add( index ); } } } bool HasColor( int x, int y ) { return HasColor( _buffer.ComputeIndexFromPosition( x, y ) ); } bool HasColor( int i ) { if ( i < 0 || i >= _buffer.numPixels ) { return false; } var pixel = _buffer.GetIndexed( i ); if ( pixel.A > alphaTreshold ) { return true; } return _processed.Contains( i ); } bool IsEdge( int i ) { if ( ! HasColor( i ) ) { return false; } var position = _buffer.ComputePositionFromIndex( i ); for ( int x = -1; x <= 1; x++ ) { for ( int y = -1; y <= 1; y++ ) { if ( x == 0 && y == 0 ) { continue; } if ( ! HasColor( position.X + x, position.Y + y ) ) { return true; } } } return false; } Color ComputeColor( int i ) { var position = _buffer.ComputePositionFromIndex( i ); var pixel = _buffer.GetIndexed( i ); var distance = 10f; var closestPixel = -1; for ( int x = -1; x <= 1; x++ ) { for ( int y = -1; y <= 1; y++ ) { if ( ( x == 0 && y == 0 ) || ! HasColor( position.X + x, position.Y + y ) ) { continue; } var d = new Vector2( x, y ).Length(); if ( d >= distance ) { continue; } distance = d; closestPixel = _buffer.ComputeIndexFromPosition( position.X + x, position.Y + y ); // var pixelColor = _buffer.GetAt( position.X + x, position.Y + y ); // rawColor += new Vector3( pixelColor.R, pixelColor.G, pixelColor.B); // numColors ++; } } // rawColor /= numColors; var rawColor = _buffer.GetIndexed( closestPixel ); return new Color( rawColor.R, rawColor.G, rawColor.B, pixel.A ); } } }