Skip to content

Commit

Permalink
Merge pull request #6 from Toreole/dev
Browse files Browse the repository at this point in the history
Basically v0.2
  • Loading branch information
Toreole authored Jul 31, 2023
2 parents b924231 + 207768a commit 88a7654
Show file tree
Hide file tree
Showing 28 changed files with 742 additions and 158 deletions.
11 changes: 8 additions & 3 deletions BlazorWebAssembly/BlazorWebAssembly.sln
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.6.33712.159
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorWebAssembly.Server", "Server\BlazorWebAssembly.Server.csproj", "{5D0639F4-35F5-4070-BD81-E8F9994D58B3}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorWebAssembly.Server", "Server\BlazorWebAssembly.Server.csproj", "{5D0639F4-35F5-4070-BD81-E8F9994D58B3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorWebAssembly.Client", "Client\BlazorWebAssembly.Client.csproj", "{DCBFC8DF-785B-41D0-B7CA-F5D36808875B}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorWebAssembly.Client", "Client\BlazorWebAssembly.Client.csproj", "{DCBFC8DF-785B-41D0-B7CA-F5D36808875B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BlazorWebAssembly.Shared", "Shared\BlazorWebAssembly.Shared.csproj", "{2ABAA9E5-A116-4201-88F1-2A7832267CF0}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorWebAssembly.Shared", "Shared\BlazorWebAssembly.Shared.csproj", "{2ABAA9E5-A116-4201-88F1-2A7832267CF0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TexMetadataGenerator", "TexMetadataGenerator\TexMetadataGenerator.csproj", "{7AE0A7E1-CA81-4EAB-B203-2944D40111D2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -27,6 +29,9 @@ Global
{2ABAA9E5-A116-4201-88F1-2A7832267CF0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2ABAA9E5-A116-4201-88F1-2A7832267CF0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2ABAA9E5-A116-4201-88F1-2A7832267CF0}.Release|Any CPU.Build.0 = Release|Any CPU
{7AE0A7E1-CA81-4EAB-B203-2944D40111D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7AE0A7E1-CA81-4EAB-B203-2944D40111D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7AE0A7E1-CA81-4EAB-B203-2944D40111D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
1 change: 1 addition & 0 deletions BlazorWebAssembly/Client/BlazorWebAssembly.Client.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.9" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.9" PrivateAssets="all" />
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
<PackageReference Include="MudBlazor" Version="6.7.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.0.1" />
</ItemGroup>

Expand Down
97 changes: 97 additions & 0 deletions BlazorWebAssembly/Client/Core/ImageReconstructor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
using Img2mc.Shared;
using Microsoft.AspNetCore.Components;
using SixLabors.ImageSharp.ColorSpaces;
using SixLabors.ImageSharp.Processing.Processors.Transforms;
using System.Collections.Concurrent;

namespace BlazorWebAssembly.Client.Core;

public class ImageReconstructor
{
public MinecraftBlock[] blockData = Array.Empty<MinecraftBlock>();

[Inject]
public HttpClient? HttpClient { get; set; }

public OutputTexture[,] OutputTextures { get; private set; } = new OutputTexture[0,0];
public int OutputRows { get; private set; } = 0;
public int OutputColumns { get; private set; } = 0;
public int MaxOutputImageSize { get; set; } = 128;
public float ContrastBias { get; set; } = 2f;

public event Action? OnOutputChanged;

public readonly Dictionary<MinecraftBlock, int> blockCounts = new();

public async void ReadFile(Stream stream)
{
try
{
using Image<Rgba32> img = await Image.LoadAsync<Rgba32>(stream);
Console.WriteLine($"Read the file as image: {img}");
blockCounts.Clear();
var px = img[0, 0];
// Available Resamplers:
// Bicubic NearestNeighbor Box MitchellNetravali CatmullRom Lanczos2 Lanczos3 Lanczos5 Lanczos8 Welch Robidoux RobidouxSharp Spline Hermite
if (img.Width > MaxOutputImageSize)
{
img.Mutate(x => x.Resize(MaxOutputImageSize, 0, new BicubicResampler()));
}
else if (img.Height > MaxOutputImageSize)
{
img.Mutate(x => x.Resize(0, MaxOutputImageSize, new BicubicResampler()));
}
Console.WriteLine($"Resized image to {img}");
OutputColumns = img.Width;
OutputRows = img.Height;
var buffer = new OutputTexture[OutputColumns, OutputRows];
for (int x = 0; x < OutputColumns; x++)
{
for (int y = 0; y < OutputRows; y++)
{
var result = FindBestMatchingTexture(img[x, y]);
buffer[x, y] = new(result.Item1.blockName, result.Item2.fileName);
if (blockCounts.ContainsKey(result.Item1))
{
blockCounts[result.Item1]++;
}
else
{
blockCounts[result.Item1] = 1;
}
}
}
Console.WriteLine("Done processing image.");
OutputTextures = buffer;
img.Dispose();
OnOutputChanged?.Invoke();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}

private (MinecraftBlock, TextureMetadata) FindBestMatchingTexture(Rgba32 pixel)
{
RGB col = new() { r = pixel.R, g = pixel.G, b = pixel.B };
var query = from block in blockData
from texture in block.textures
orderby texture.ColorDistance(block, col, ContrastBias) ascending
select (block, texture);
return query.First();
}

public readonly struct OutputTexture
{
public readonly string blockName;
public readonly string fileName;

public OutputTexture(string blockName, string fileName)
{
this.blockName = blockName;
this.fileName = fileName;
}
}

}
162 changes: 62 additions & 100 deletions BlazorWebAssembly/Client/Pages/Index.razor
Original file line number Diff line number Diff line change
@@ -1,149 +1,115 @@
@page "/"
@using System.Text.Json;
@using static System.Net.WebRequestMethods;
@using System.Text.Json
@using static System.Net.WebRequestMethods
@using Img2mc.Shared
@using BlazorWebAssembly.Client.Core

@implements IHandleEvent

@inject HttpClient Http
@inject ImageReconstructor imageReconstructor

@code {
string json = "[]";
string firstPixelRGBA = "";
TextureMetadata[] textureData = new TextureMetadata[0];

TextureMetadata[,]? outputTextures = null;
int outputRows = 0;
int outputColumns = 0;
const long fileSizeLimit = 52428800;
int blockSize = 8;
public string BlockSizeString => blockSize.ToString() + "px";

bool check = true;
private IBrowserFile? selectedFile;

protected override async Task OnInitializedAsync()
{
//blockData persists across page swaps. no need to get it again.
if (imageReconstructor.blockData.Length != 0)
return;
var path = "images/tex_metadata.json";
string json = await Http.GetStringAsync(path);
textureData = JsonSerializer.Deserialize<TextureMetadata[]>(json) ?? new TextureMetadata[0];
imageReconstructor.blockData = JsonSerializer.Deserialize<MinecraftBlock[]>(json) ?? new MinecraftBlock[0];
imageReconstructor.OnOutputChanged += StateHasChanged;
}

private void ButtonClicked()
~Index()
{
Console.WriteLine("hello button");
imageReconstructor.OnOutputChanged -= StateHasChanged;
}

private void SelectedFileChanged(InputFileChangeEventArgs args)
{
selectedFile = args.File;
Console.WriteLine($"File Selected: {args.File.Name} ||| is: {args.File.ContentType}");
ReadFile(args.File.OpenReadStream());
imageReconstructor.ReadFile(args.File.OpenReadStream(fileSizeLimit));
}

private async void ReadFile(Stream stream)
private void RetryButtonPressed()
{
try
if(selectedFile != null)
{
using Image<Rgba32> img = await Image.LoadAsync<Rgba32>(stream);
Console.WriteLine($"Read the file as image: {img.ToString()}");
var px = img[0, 0];

if(img.Width > 128)
{
img.Mutate(x => x.Resize(128, 0));
}
else if(img.Height > 128)
{
img.Mutate(x => x.Resize(0, 128));
}
Console.WriteLine($"Resized image to {img.ToString()}");
outputColumns = img.Width;
outputRows = img.Height;
var buffer = new TextureMetadata[outputColumns, outputRows];
for(int x = 0; x < outputColumns; x++)
{
for(int y = 0; y < outputRows; y++)
{
buffer[x, y] = FindBestMatchingTexture(img[x, y]);
}
}
Console.WriteLine("Done processing image.");
outputTextures = buffer;
img.Dispose();
this.StateHasChanged();
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
imageReconstructor.ReadFile(selectedFile.OpenReadStream(fileSizeLimit));
}
}

private TextureMetadata FindBestMatchingTexture(Rgba32 pixel)
{
RGB col = new() { r = pixel.R, g = pixel.G, b = pixel.B };
return textureData.OrderBy(x => x.averageRGB.RGBDistance(col)).First();
}
Task IHandleEvent.HandleEventAsync(EventCallbackWorkItem callback, object? arg)
=> callback.InvokeAsync(arg);
}
<style>
.mc_block {
width: 16px;
height: 16px;
max-height: 16px;
line-height: 10px;
margin: 0;
border: none;
padding: 0;
}
.mc_row {
height: 16px;
max-height: 16px;
line-height: 16px;
margin: 0;
padding: 0;
border: none;
}
</style>
<PageTitle>Index</PageTitle>

<h1>Hello, world!</h1>

Welcome to your new app.
<PageTitle>Img2Mc</PageTitle>

<SurveyPrompt Title="How is Blazor working for you?" />
<MudSlider Min="16" Max="128" @bind-Value="imageReconstructor.MaxOutputImageSize" Style="width: 200px">
Max Size: @imageReconstructor.MaxOutputImageSize
</MudSlider>
<MudSlider Min="3" Max="16" @bind-Value="blockSize" Style=@($"width: 200px; --block-size:{ BlockSizeString }")>
Block Size: @blockSize
</MudSlider>
<MudSlider Min="0f" Max="10f" @bind-Value="imageReconstructor.ContrastBias" Style="width: 200px" Step="0.05f">
Contrast Bias: @imageReconstructor.ContrastBias
</MudSlider>
<MudButton OnClick="RetryButtonPressed">Retry</MudButton>

<button @onclick="ButtonClicked">
Hello Button
<!--<Tooltip Placement="TooltipPlacement.Top" Title="Block"> tooltip </Tooltip>Hello There!-->
</button>

<InputCheckbox Value="check" ValueExpression="() => check"/>
Please Select an image file:

<InputFile OnChange="SelectedFileChanged"></InputFile>
<InputFile OnChange="SelectedFileChanged"></InputFile>

@if(outputTextures != null)
@if (imageReconstructor.OutputTextures != null)
{
<div>Output texture is not null here</div>
<table>
<thead></thead>
@for(int row = 0; row < outputRows; row++)
@for (int row = 0; row < imageReconstructor.OutputRows; row++)
{
<tr class="mc_row">
@for(int column = 0; column < outputColumns; column ++)
{
<td class="mc_block">
<img src="images/mc_textures/@outputTextures[column, row].fileName" class="mc_block" />

</td>
}
<tr class="mc_row" style="--block-size: @BlockSizeString">
@for (int column = 0; column < imageReconstructor.OutputColumns; column++)
{
var tex = imageReconstructor.OutputTextures[column, row];

<td class="mc_block">
<MudTooltip Text="@tex.blockName" Arrow Placement="Placement.Top">
<img src="images/mc_textures/@tex.fileName" class="mc_block" />
</MudTooltip>
</td>
}
</tr>
}
</table>
}

<h2>Required blocks</h2>
<ol>
@foreach (var pair in imageReconstructor.blockCounts.OrderByDescending(x => x.Value))
{
<li>
<img src="images/mc_textures/@pair.Key.FirstTexture" /> @pair.Key.blockName x @pair.Value
</li>
}
</ol>


<!-- take this out for now.
<table>
<thead>
<tr>
<th scope="col">Texture</th>
<th scope="col">Block</th>
<th scope="col">RGB</th>
<th scope="col">Hue V</th>
<th scope="col">Sat V</th>
<th scope="col">Sat A</th>
<th scope="col">Val V</th>
</tr>
</thead>
@@foreach (var entry in textureData)
Expand All @@ -152,10 +118,6 @@ Welcome to your new app.
<td><img src="images/mc_textures/@@entry.fileName" /></td>
<td>@@entry.blockName</td>
<td style="background-color: @@entry.averageRGB.AsHexString()">@@entry.averageRGB</td>
<td>@@entry.hueVariance</td>
<td>@@entry.saturationVariance</td>
<td>@@entry.averageSaturation</td>
<td>@@entry.valueVariance</td>
</tr>
}
</table>
Expand Down
32 changes: 32 additions & 0 deletions BlazorWebAssembly/Client/Pages/Settings.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@page "/settings"

@using BlazorWebAssembly.Client.Core;
@using Img2mc.Shared;

@inject ImageReconstructor imageReconstructor

<h3>Settings</h3>

<MudTable Items="@imageReconstructor.blockData">
<HeaderContent>
<MudTh>
<MudTableSortLabel SortBy="new Func<MinecraftBlock, string>(x => x.blockName)">Block</MudTableSortLabel>
</MudTh>
<MudTh>
Exclude
</MudTh>
</HeaderContent>
<RowTemplate>
<MudTd DataLabel="Block">@context.blockName</MudTd>
<MudTd DataLabel="Exclude">
<MudCheckBox @bind-Checked="@context.exclude"></MudCheckBox>
</MudTd>
</RowTemplate>
<PagerContent>
<MudTablePager PageSizeOptions="new int[]{50, 100}" />
</PagerContent>
</MudTable>

@code {

}
Loading

0 comments on commit 88a7654

Please sign in to comment.