rj-action-library/Runtime/Procedural/Textures/CPUTextureDilater/CPUTextureDilater.cs

254 lines
5.3 KiB
C#

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<Texture2D> Dilate( Texture2D texture, Func<Task> 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<Task> waiting;
async Task<Texture2D> _Dilate( Texture2D texture )
{
_buffer = TextureCombinerBuffer.From( texture );
await _Process();
return _buffer.CreateImageTexture();
}
public float alphaTreshold = 250f/255f;
HashSet<int> _processed = new HashSet<int>();
HashSet<int> _unprocessed = new HashSet<int>();
HashSet<int> _edges = new HashSet<int>();
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 );
}
}
}