2.0.0 #92
@@ -9,11 +9,6 @@ using Penumbra.Api.Enums;
|
|||||||
using Penumbra.Api.Helpers;
|
using Penumbra.Api.Helpers;
|
||||||
using Penumbra.Api.IpcSubscribers;
|
using Penumbra.Api.IpcSubscribers;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LightlessSync.Interop.Ipc;
|
namespace LightlessSync.Interop.Ipc;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
using System;
|
using LightlessSync.API.Data.Enum;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using LightlessSync.API.Data.Enum;
|
|
||||||
using LightlessSync.API.Dto.User;
|
using LightlessSync.API.Dto.User;
|
||||||
using LightlessSync.PlayerData.Pairs;
|
using LightlessSync.PlayerData.Pairs;
|
||||||
using LightlessSync.Services.Mediator;
|
using LightlessSync.Services.Mediator;
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using Dalamud.Game.Gui.ContextMenu;
|
using Dalamud.Game.Gui.ContextMenu;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using LightlessSync.API.Data;
|
using LightlessSync.API.Data;
|
||||||
using LightlessSync.API.Data.Enum;
|
using LightlessSync.API.Data.Enum;
|
||||||
using LightlessSync.API.Data.Extensions;
|
using LightlessSync.API.Data.Extensions;
|
||||||
using LightlessSync.API.Dto.User;
|
using LightlessSync.API.Dto.User;
|
||||||
using LightlessSync.PlayerData.Pairs;
|
|
||||||
using LightlessSync.Services.Mediator;
|
using LightlessSync.Services.Mediator;
|
||||||
using LightlessSync.Services.ServerConfiguration;
|
using LightlessSync.Services.ServerConfiguration;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using LightlessSync.API.Data;
|
using LightlessSync.API.Data;
|
||||||
using LightlessSync.API.Data.Enum;
|
|
||||||
using LightlessSync.API.Data.Extensions;
|
using LightlessSync.API.Data.Extensions;
|
||||||
using LightlessSync.API.Dto.User;
|
using LightlessSync.API.Dto.User;
|
||||||
using LightlessSync.Services.Events;
|
using LightlessSync.Services.Events;
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using LightlessSync.API.Dto.User;
|
using LightlessSync.API.Dto.User;
|
||||||
using LightlessSync.LightlessConfiguration;
|
using LightlessSync.LightlessConfiguration;
|
||||||
using LightlessSync.LightlessConfiguration.Models;
|
using LightlessSync.LightlessConfiguration.Models;
|
||||||
using LightlessSync.Services.Events;
|
|
||||||
using LightlessSync.Services.Mediator;
|
using LightlessSync.Services.Mediator;
|
||||||
using LightlessSync.Services.ServerConfiguration;
|
using LightlessSync.Services.ServerConfiguration;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|||||||
@@ -1,11 +1,5 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using LightlessSync.API.Data;
|
using LightlessSync.API.Data;
|
||||||
using LightlessSync.API.Data.Enum;
|
using LightlessSync.API.Data.Enum;
|
||||||
using LightlessSync.API.Data.Extensions;
|
using LightlessSync.API.Data.Extensions;
|
||||||
|
|||||||
@@ -1,10 +1,4 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using LightlessSync.API.Data.Enum;
|
|
||||||
using LightlessSync.API.Data.Extensions;
|
using LightlessSync.API.Data.Extensions;
|
||||||
using LightlessSync.API.Dto.CharaData;
|
|
||||||
using LightlessSync.API.Dto.User;
|
using LightlessSync.API.Dto.User;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,4 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using LightlessSync.API.Data;
|
|
||||||
using LightlessSync.API.Dto.Group;
|
using LightlessSync.API.Dto.Group;
|
||||||
using LightlessSync.Services.Events;
|
|
||||||
using LightlessSync.Services.Mediator;
|
using LightlessSync.Services.Mediator;
|
||||||
using LightlessSync.UI.Models;
|
using LightlessSync.UI.Models;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
|
||||||
using LightlessSync.API.Data;
|
using LightlessSync.API.Data;
|
||||||
using LightlessSync.API.Data.Enum;
|
using LightlessSync.API.Data.Enum;
|
||||||
using LightlessSync.API.Dto.Group;
|
using LightlessSync.API.Dto.Group;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using LightlessSync.API.Data;
|
using LightlessSync.API.Data;
|
||||||
using LightlessSync.API.Data.Enum;
|
using LightlessSync.API.Data.Enum;
|
||||||
using LightlessSync.API.Data.Extensions;
|
using LightlessSync.API.Data.Extensions;
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
|
||||||
using LightlessSync.API.Data;
|
using LightlessSync.API.Data;
|
||||||
using LightlessSync.Utils;
|
using LightlessSync.Utils;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using LightlessSync.API.Data;
|
using LightlessSync.API.Data;
|
||||||
using LightlessSync.API.Data.Comparer;
|
using LightlessSync.API.Data.Comparer;
|
||||||
using LightlessSync.Services;
|
using LightlessSync.Services;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using LightlessSync.API.Dto.Chat;
|
using LightlessSync.API.Dto.Chat;
|
||||||
|
|
||||||
namespace LightlessSync.Services.Chat;
|
namespace LightlessSync.Services.Chat;
|
||||||
|
|||||||
@@ -1,12 +1,5 @@
|
|||||||
using LightlessSync;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using LightlessSync.API.Dto;
|
using LightlessSync.API.Dto;
|
||||||
using LightlessSync.API.Dto.Chat;
|
using LightlessSync.API.Dto.Chat;
|
||||||
using LightlessSync.Services;
|
|
||||||
using LightlessSync.Services.ActorTracking;
|
using LightlessSync.Services.ActorTracking;
|
||||||
using LightlessSync.Services.Mediator;
|
using LightlessSync.Services.Mediator;
|
||||||
using LightlessSync.WebAPI;
|
using LightlessSync.WebAPI;
|
||||||
|
|||||||
312
LightlessSync/Services/TextureCompression/IndexDownscaler.cs
Normal file
312
LightlessSync/Services/TextureCompression/IndexDownscaler.cs
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using SixLabors.ImageSharp;
|
||||||
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Index upscaler code (converted/reversed for downscaling purposes) provided by Ny
|
||||||
|
* thank you!!
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace LightlessSync.Services.TextureCompression;
|
||||||
|
|
||||||
|
internal static class IndexDownscaler
|
||||||
|
{
|
||||||
|
private static readonly Vector2[] SampleOffsets =
|
||||||
|
{
|
||||||
|
new(0.25f, 0.25f),
|
||||||
|
new(0.75f, 0.25f),
|
||||||
|
new(0.25f, 0.75f),
|
||||||
|
new(0.75f, 0.75f),
|
||||||
|
};
|
||||||
|
|
||||||
|
public static Image<Rgba32> Downscale(Image<Rgba32> source, int targetWidth, int targetHeight, int blockMultiple)
|
||||||
|
{
|
||||||
|
var current = source.Clone();
|
||||||
|
|
||||||
|
while (current.Width > targetWidth || current.Height > targetHeight)
|
||||||
|
{
|
||||||
|
var nextWidth = Math.Max(targetWidth, Math.Max(blockMultiple, current.Width / 2));
|
||||||
|
var nextHeight = Math.Max(targetHeight, Math.Max(blockMultiple, current.Height / 2));
|
||||||
|
var next = new Image<Rgba32>(nextWidth, nextHeight);
|
||||||
|
|
||||||
|
for (var y = 0; y < nextHeight; y++)
|
||||||
|
{
|
||||||
|
var srcY = Math.Min(current.Height - 1, y * 2);
|
||||||
|
for (var x = 0; x < nextWidth; x++)
|
||||||
|
{
|
||||||
|
var srcX = Math.Min(current.Width - 1, x * 2);
|
||||||
|
|
||||||
|
var topLeft = current[srcX, srcY];
|
||||||
|
var topRight = current[Math.Min(current.Width - 1, srcX + 1), srcY];
|
||||||
|
var bottomLeft = current[srcX, Math.Min(current.Height - 1, srcY + 1)];
|
||||||
|
var bottomRight = current[Math.Min(current.Width - 1, srcX + 1), Math.Min(current.Height - 1, srcY + 1)];
|
||||||
|
|
||||||
|
next[x, y] = DownscaleIndexBlock(topLeft, topRight, bottomLeft, bottomRight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
current.Dispose();
|
||||||
|
current = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Rgba32 DownscaleIndexBlock(in Rgba32 topLeft, in Rgba32 topRight, in Rgba32 bottomLeft, in Rgba32 bottomRight)
|
||||||
|
{
|
||||||
|
Span<Rgba32> ordered = stackalloc Rgba32[4]
|
||||||
|
{
|
||||||
|
bottomLeft,
|
||||||
|
bottomRight,
|
||||||
|
topRight,
|
||||||
|
topLeft
|
||||||
|
};
|
||||||
|
|
||||||
|
Span<float> weights = stackalloc float[4];
|
||||||
|
var hasContribution = false;
|
||||||
|
|
||||||
|
foreach (var sample in SampleOffsets)
|
||||||
|
{
|
||||||
|
if (TryAccumulateSampleWeights(ordered, sample, weights))
|
||||||
|
{
|
||||||
|
hasContribution = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasContribution)
|
||||||
|
{
|
||||||
|
var bestIndex = IndexOfMax(weights);
|
||||||
|
if (bestIndex >= 0 && weights[bestIndex] > 0f)
|
||||||
|
{
|
||||||
|
return ordered[bestIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<Rgba32> fallback = stackalloc Rgba32[4] { topLeft, topRight, bottomLeft, bottomRight };
|
||||||
|
return PickMajorityColor(fallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryAccumulateSampleWeights(ReadOnlySpan<Rgba32> colors, in Vector2 sampleUv, Span<float> weights)
|
||||||
|
{
|
||||||
|
var red = new Vector4(
|
||||||
|
colors[0].R / 255f,
|
||||||
|
colors[1].R / 255f,
|
||||||
|
colors[2].R / 255f,
|
||||||
|
colors[3].R / 255f);
|
||||||
|
|
||||||
|
var symbols = QuantizeSymbols(red);
|
||||||
|
var cellUv = ComputeShiftedUv(sampleUv);
|
||||||
|
|
||||||
|
Span<int> order = stackalloc int[4];
|
||||||
|
order[0] = 0;
|
||||||
|
order[1] = 1;
|
||||||
|
order[2] = 2;
|
||||||
|
order[3] = 3;
|
||||||
|
|
||||||
|
ApplySymmetry(ref symbols, ref cellUv, order);
|
||||||
|
|
||||||
|
var equality = BuildEquality(symbols, symbols.W);
|
||||||
|
var selector = BuildSelector(equality, symbols, cellUv);
|
||||||
|
|
||||||
|
const uint lut = 0x00000C07u;
|
||||||
|
|
||||||
|
if (((lut >> (int)selector) & 1u) != 0u)
|
||||||
|
{
|
||||||
|
weights[order[3]] += 1f;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selector == 3u)
|
||||||
|
{
|
||||||
|
equality = BuildEquality(symbols, symbols.Z);
|
||||||
|
}
|
||||||
|
|
||||||
|
var weight = ComputeWeight(equality, cellUv);
|
||||||
|
if (weight <= 1e-6f)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var factor = 1f / weight;
|
||||||
|
|
||||||
|
var wW = equality.W * (1f - cellUv.X) * (1f - cellUv.Y) * factor;
|
||||||
|
var wX = equality.X * (1f - cellUv.X) * cellUv.Y * factor;
|
||||||
|
var wZ = equality.Z * cellUv.X * (1f - cellUv.Y) * factor;
|
||||||
|
var wY = equality.Y * cellUv.X * cellUv.Y * factor;
|
||||||
|
|
||||||
|
var contributed = false;
|
||||||
|
|
||||||
|
if (wW > 0f)
|
||||||
|
{
|
||||||
|
weights[order[3]] += wW;
|
||||||
|
contributed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wX > 0f)
|
||||||
|
{
|
||||||
|
weights[order[0]] += wX;
|
||||||
|
contributed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wZ > 0f)
|
||||||
|
{
|
||||||
|
weights[order[2]] += wZ;
|
||||||
|
contributed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wY > 0f)
|
||||||
|
{
|
||||||
|
weights[order[1]] += wY;
|
||||||
|
contributed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return contributed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vector4 QuantizeSymbols(in Vector4 channel)
|
||||||
|
=> new(
|
||||||
|
Quantize(channel.X),
|
||||||
|
Quantize(channel.Y),
|
||||||
|
Quantize(channel.Z),
|
||||||
|
Quantize(channel.W));
|
||||||
|
|
||||||
|
private static float Quantize(float value)
|
||||||
|
{
|
||||||
|
var clamped = Math.Clamp(value, 0f, 1f);
|
||||||
|
return (MathF.Round(clamped * 16f) + 0.5f) / 16f;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ApplySymmetry(ref Vector4 symbols, ref Vector2 cellUv, Span<int> order)
|
||||||
|
{
|
||||||
|
if (cellUv.X >= 0.5f)
|
||||||
|
{
|
||||||
|
symbols = SwapYxwz(symbols, order);
|
||||||
|
cellUv.X = 1f - cellUv.X;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cellUv.Y >= 0.5f)
|
||||||
|
{
|
||||||
|
symbols = SwapWzyx(symbols, order);
|
||||||
|
cellUv.Y = 1f - cellUv.Y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vector4 BuildEquality(in Vector4 symbols, float reference)
|
||||||
|
=> new(
|
||||||
|
AreEqual(symbols.X, reference) ? 1f : 0f,
|
||||||
|
AreEqual(symbols.Y, reference) ? 1f : 0f,
|
||||||
|
AreEqual(symbols.Z, reference) ? 1f : 0f,
|
||||||
|
AreEqual(symbols.W, reference) ? 1f : 0f);
|
||||||
|
|
||||||
|
private static uint BuildSelector(in Vector4 equality, in Vector4 symbols, in Vector2 cellUv)
|
||||||
|
{
|
||||||
|
uint selector = 0;
|
||||||
|
if (equality.X > 0.5f) selector |= 4u;
|
||||||
|
if (equality.Y > 0.5f) selector |= 8u;
|
||||||
|
if (equality.Z > 0.5f) selector |= 16u;
|
||||||
|
if (AreEqual(symbols.X, symbols.Z)) selector |= 2u;
|
||||||
|
if (cellUv.X + cellUv.Y >= 0.5f) selector |= 1u;
|
||||||
|
|
||||||
|
return selector;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static float ComputeWeight(in Vector4 equality, in Vector2 cellUv)
|
||||||
|
=> equality.W * (1f - cellUv.X) * (1f - cellUv.Y)
|
||||||
|
+ equality.X * (1f - cellUv.X) * cellUv.Y
|
||||||
|
+ equality.Z * cellUv.X * (1f - cellUv.Y)
|
||||||
|
+ equality.Y * cellUv.X * cellUv.Y;
|
||||||
|
|
||||||
|
private static Vector2 ComputeShiftedUv(in Vector2 uv)
|
||||||
|
{
|
||||||
|
var shifted = new Vector2(
|
||||||
|
uv.X - MathF.Floor(uv.X),
|
||||||
|
uv.Y - MathF.Floor(uv.Y));
|
||||||
|
|
||||||
|
shifted.X -= 0.5f;
|
||||||
|
if (shifted.X < 0f)
|
||||||
|
{
|
||||||
|
shifted.X += 1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
shifted.Y -= 0.5f;
|
||||||
|
if (shifted.Y < 0f)
|
||||||
|
{
|
||||||
|
shifted.Y += 1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shifted;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vector4 SwapYxwz(in Vector4 v, Span<int> order)
|
||||||
|
{
|
||||||
|
var o0 = order[0];
|
||||||
|
var o1 = order[1];
|
||||||
|
var o2 = order[2];
|
||||||
|
var o3 = order[3];
|
||||||
|
|
||||||
|
order[0] = o1;
|
||||||
|
order[1] = o0;
|
||||||
|
order[2] = o3;
|
||||||
|
order[3] = o2;
|
||||||
|
|
||||||
|
return new Vector4(v.Y, v.X, v.W, v.Z);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vector4 SwapWzyx(in Vector4 v, Span<int> order)
|
||||||
|
{
|
||||||
|
var o0 = order[0];
|
||||||
|
var o1 = order[1];
|
||||||
|
var o2 = order[2];
|
||||||
|
var o3 = order[3];
|
||||||
|
|
||||||
|
order[0] = o3;
|
||||||
|
order[1] = o2;
|
||||||
|
order[2] = o1;
|
||||||
|
order[3] = o0;
|
||||||
|
|
||||||
|
return new Vector4(v.W, v.Z, v.Y, v.X);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int IndexOfMax(ReadOnlySpan<float> values)
|
||||||
|
{
|
||||||
|
var bestIndex = -1;
|
||||||
|
var bestValue = 0f;
|
||||||
|
|
||||||
|
for (var i = 0; i < values.Length; i++)
|
||||||
|
{
|
||||||
|
if (values[i] > bestValue)
|
||||||
|
{
|
||||||
|
bestValue = values[i];
|
||||||
|
bestIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool AreEqual(float a, float b) => MathF.Abs(a - b) <= 1e-5f;
|
||||||
|
|
||||||
|
private static Rgba32 PickMajorityColor(ReadOnlySpan<Rgba32> colors)
|
||||||
|
{
|
||||||
|
var counts = new Dictionary<Rgba32, int>(colors.Length);
|
||||||
|
foreach (var color in colors)
|
||||||
|
{
|
||||||
|
if (counts.TryGetValue(color, out var count))
|
||||||
|
{
|
||||||
|
counts[color] = count + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
counts[color] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return counts
|
||||||
|
.OrderByDescending(kvp => kvp.Value)
|
||||||
|
.ThenByDescending(kvp => kvp.Key.A)
|
||||||
|
.ThenByDescending(kvp => kvp.Key.R)
|
||||||
|
.ThenByDescending(kvp => kvp.Key.G)
|
||||||
|
.ThenByDescending(kvp => kvp.Key.B)
|
||||||
|
.First().Key;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Lumina.Data.Files;
|
using Lumina.Data.Files;
|
||||||
using OtterTex;
|
using OtterTex;
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using Penumbra.Api.Enums;
|
using Penumbra.Api.Enums;
|
||||||
|
|
||||||
namespace LightlessSync.Services.TextureCompression;
|
namespace LightlessSync.Services.TextureCompression;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace LightlessSync.Services.TextureCompression;
|
namespace LightlessSync.Services.TextureCompression;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using LightlessSync.Interop.Ipc;
|
using LightlessSync.Interop.Ipc;
|
||||||
using LightlessSync.FileCache;
|
using LightlessSync.FileCache;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using System;
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Numerics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using OtterTex;
|
using OtterTex;
|
||||||
using OtterImage = OtterTex.Image;
|
using OtterImage = OtterTex.Image;
|
||||||
@@ -15,7 +14,6 @@ using SixLabors.ImageSharp.PixelFormats;
|
|||||||
using SixLabors.ImageSharp.Processing;
|
using SixLabors.ImageSharp.Processing;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Index upscaler code (converted/reversed for downscaling purposes) provided by Ny
|
|
||||||
* OtterTex made by Ottermandias
|
* OtterTex made by Ottermandias
|
||||||
* thank you!!
|
* thank you!!
|
||||||
*/
|
*/
|
||||||
@@ -183,7 +181,7 @@ public sealed class TextureDownscaleService
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
using var resized = ReduceIndexTexture(originalImage, targetSize.width, targetSize.height);
|
using var resized = IndexDownscaler.Downscale(originalImage, targetSize.width, targetSize.height, BlockMultiple);
|
||||||
|
|
||||||
var resizedPixels = new byte[targetSize.width * targetSize.height * 4];
|
var resizedPixels = new byte[targetSize.width * targetSize.height * 4];
|
||||||
resized.CopyPixelDataTo(resizedPixels);
|
resized.CopyPixelDataTo(resizedPixels);
|
||||||
@@ -231,8 +229,7 @@ public sealed class TextureDownscaleService
|
|||||||
|
|
||||||
private static bool IsIndexMap(TextureMapKind kind)
|
private static bool IsIndexMap(TextureMapKind kind)
|
||||||
=> kind is TextureMapKind.Mask
|
=> kind is TextureMapKind.Mask
|
||||||
or TextureMapKind.Index
|
or TextureMapKind.Index;
|
||||||
or TextureMapKind.Ui;
|
|
||||||
|
|
||||||
private Task<bool> TryDropTopMipAsync(
|
private Task<bool> TryDropTopMipAsync(
|
||||||
string hash,
|
string hash,
|
||||||
@@ -423,39 +420,6 @@ public sealed class TextureDownscaleService
|
|||||||
private static int ReduceDimension(int value)
|
private static int ReduceDimension(int value)
|
||||||
=> value <= 1 ? 1 : Math.Max(1, value / 2);
|
=> value <= 1 ? 1 : Math.Max(1, value / 2);
|
||||||
|
|
||||||
private static Image<Rgba32> ReduceIndexTexture(Image<Rgba32> source, int targetWidth, int targetHeight)
|
|
||||||
{
|
|
||||||
var current = source.Clone();
|
|
||||||
|
|
||||||
while (current.Width > targetWidth || current.Height > targetHeight)
|
|
||||||
{
|
|
||||||
var nextWidth = Math.Max(targetWidth, Math.Max(BlockMultiple, current.Width / 2));
|
|
||||||
var nextHeight = Math.Max(targetHeight, Math.Max(BlockMultiple, current.Height / 2));
|
|
||||||
var next = new Image<Rgba32>(nextWidth, nextHeight);
|
|
||||||
|
|
||||||
for (int y = 0; y < nextHeight; y++)
|
|
||||||
{
|
|
||||||
var srcY = Math.Min(current.Height - 1, y * 2);
|
|
||||||
for (int x = 0; x < nextWidth; x++)
|
|
||||||
{
|
|
||||||
var srcX = Math.Min(current.Width - 1, x * 2);
|
|
||||||
|
|
||||||
var topLeft = current[srcX, srcY];
|
|
||||||
var topRight = current[Math.Min(current.Width - 1, srcX + 1), srcY];
|
|
||||||
var bottomLeft = current[srcX, Math.Min(current.Height - 1, srcY + 1)];
|
|
||||||
var bottomRight = current[Math.Min(current.Width - 1, srcX + 1), Math.Min(current.Height - 1, srcY + 1)];
|
|
||||||
|
|
||||||
next[x, y] = DownscaleIndexBlock(topLeft, topRight, bottomLeft, bottomRight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
current.Dispose();
|
|
||||||
current = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Image<Rgba32> ReduceLinearTexture(Image<Rgba32> source, int targetWidth, int targetHeight)
|
private static Image<Rgba32> ReduceLinearTexture(Image<Rgba32> source, int targetWidth, int targetHeight)
|
||||||
{
|
{
|
||||||
var clone = source.Clone();
|
var clone = source.Clone();
|
||||||
@@ -470,271 +434,6 @@ public sealed class TextureDownscaleService
|
|||||||
return clone;
|
return clone;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Rgba32 DownscaleIndexBlock(in Rgba32 topLeft, in Rgba32 topRight, in Rgba32 bottomLeft, in Rgba32 bottomRight)
|
|
||||||
{
|
|
||||||
Span<Rgba32> ordered = stackalloc Rgba32[4]
|
|
||||||
{
|
|
||||||
bottomLeft,
|
|
||||||
bottomRight,
|
|
||||||
topRight,
|
|
||||||
topLeft
|
|
||||||
};
|
|
||||||
|
|
||||||
Span<float> weights = stackalloc float[4];
|
|
||||||
var hasContribution = false;
|
|
||||||
|
|
||||||
foreach (var sample in SampleOffsets)
|
|
||||||
{
|
|
||||||
if (TryAccumulateSampleWeights(ordered, sample, weights))
|
|
||||||
{
|
|
||||||
hasContribution = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasContribution)
|
|
||||||
{
|
|
||||||
var bestIndex = IndexOfMax(weights);
|
|
||||||
if (bestIndex >= 0 && weights[bestIndex] > 0f)
|
|
||||||
{
|
|
||||||
return ordered[bestIndex];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Span<Rgba32> fallback = stackalloc Rgba32[4] { topLeft, topRight, bottomLeft, bottomRight };
|
|
||||||
return PickMajorityColor(fallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly Vector2[] SampleOffsets =
|
|
||||||
{
|
|
||||||
new(0.25f, 0.25f),
|
|
||||||
new(0.75f, 0.25f),
|
|
||||||
new(0.25f, 0.75f),
|
|
||||||
new(0.75f, 0.75f),
|
|
||||||
};
|
|
||||||
|
|
||||||
private static bool TryAccumulateSampleWeights(ReadOnlySpan<Rgba32> colors, in Vector2 sampleUv, Span<float> weights)
|
|
||||||
{
|
|
||||||
var red = new Vector4(
|
|
||||||
colors[0].R / 255f,
|
|
||||||
colors[1].R / 255f,
|
|
||||||
colors[2].R / 255f,
|
|
||||||
colors[3].R / 255f);
|
|
||||||
|
|
||||||
var symbols = QuantizeSymbols(red);
|
|
||||||
var cellUv = ComputeShiftedUv(sampleUv);
|
|
||||||
|
|
||||||
Span<int> order = stackalloc int[4];
|
|
||||||
order[0] = 0;
|
|
||||||
order[1] = 1;
|
|
||||||
order[2] = 2;
|
|
||||||
order[3] = 3;
|
|
||||||
|
|
||||||
ApplySymmetry(ref symbols, ref cellUv, order);
|
|
||||||
|
|
||||||
var equality = BuildEquality(symbols, symbols.W);
|
|
||||||
var selector = BuildSelector(equality, symbols, cellUv);
|
|
||||||
|
|
||||||
const uint lut = 0x00000C07u;
|
|
||||||
|
|
||||||
if (((lut >> (int)selector) & 1u) != 0u)
|
|
||||||
{
|
|
||||||
weights[order[3]] += 1f;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selector == 3u)
|
|
||||||
{
|
|
||||||
equality = BuildEquality(symbols, symbols.Z);
|
|
||||||
}
|
|
||||||
|
|
||||||
var weight = ComputeWeight(equality, cellUv);
|
|
||||||
if (weight <= 1e-6f)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var factor = 1f / weight;
|
|
||||||
|
|
||||||
var wW = equality.W * (1f - cellUv.X) * (1f - cellUv.Y) * factor;
|
|
||||||
var wX = equality.X * (1f - cellUv.X) * cellUv.Y * factor;
|
|
||||||
var wZ = equality.Z * cellUv.X * (1f - cellUv.Y) * factor;
|
|
||||||
var wY = equality.Y * cellUv.X * cellUv.Y * factor;
|
|
||||||
|
|
||||||
var contributed = false;
|
|
||||||
|
|
||||||
if (wW > 0f)
|
|
||||||
{
|
|
||||||
weights[order[3]] += wW;
|
|
||||||
contributed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wX > 0f)
|
|
||||||
{
|
|
||||||
weights[order[0]] += wX;
|
|
||||||
contributed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wZ > 0f)
|
|
||||||
{
|
|
||||||
weights[order[2]] += wZ;
|
|
||||||
contributed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wY > 0f)
|
|
||||||
{
|
|
||||||
weights[order[1]] += wY;
|
|
||||||
contributed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return contributed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Vector4 QuantizeSymbols(in Vector4 channel)
|
|
||||||
=> new(
|
|
||||||
Quantize(channel.X),
|
|
||||||
Quantize(channel.Y),
|
|
||||||
Quantize(channel.Z),
|
|
||||||
Quantize(channel.W));
|
|
||||||
|
|
||||||
private static float Quantize(float value)
|
|
||||||
{
|
|
||||||
var clamped = Math.Clamp(value, 0f, 1f);
|
|
||||||
return (MathF.Round(clamped * 16f) + 0.5f) / 16f;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ApplySymmetry(ref Vector4 symbols, ref Vector2 cellUv, Span<int> order)
|
|
||||||
{
|
|
||||||
if (cellUv.X >= 0.5f)
|
|
||||||
{
|
|
||||||
symbols = SwapYxwz(symbols, order);
|
|
||||||
cellUv.X = 1f - cellUv.X;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cellUv.Y >= 0.5f)
|
|
||||||
{
|
|
||||||
symbols = SwapWzyx(symbols, order);
|
|
||||||
cellUv.Y = 1f - cellUv.Y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Vector4 BuildEquality(in Vector4 symbols, float reference)
|
|
||||||
=> new(
|
|
||||||
AreEqual(symbols.X, reference) ? 1f : 0f,
|
|
||||||
AreEqual(symbols.Y, reference) ? 1f : 0f,
|
|
||||||
AreEqual(symbols.Z, reference) ? 1f : 0f,
|
|
||||||
AreEqual(symbols.W, reference) ? 1f : 0f);
|
|
||||||
|
|
||||||
private static uint BuildSelector(in Vector4 equality, in Vector4 symbols, in Vector2 cellUv)
|
|
||||||
{
|
|
||||||
uint selector = 0;
|
|
||||||
if (equality.X > 0.5f) selector |= 4u;
|
|
||||||
if (equality.Y > 0.5f) selector |= 8u;
|
|
||||||
if (equality.Z > 0.5f) selector |= 16u;
|
|
||||||
if (AreEqual(symbols.X, symbols.Z)) selector |= 2u;
|
|
||||||
if (cellUv.X + cellUv.Y >= 0.5f) selector |= 1u;
|
|
||||||
|
|
||||||
return selector;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static float ComputeWeight(in Vector4 equality, in Vector2 cellUv)
|
|
||||||
=> equality.W * (1f - cellUv.X) * (1f - cellUv.Y)
|
|
||||||
+ equality.X * (1f - cellUv.X) * cellUv.Y
|
|
||||||
+ equality.Z * cellUv.X * (1f - cellUv.Y)
|
|
||||||
+ equality.Y * cellUv.X * cellUv.Y;
|
|
||||||
|
|
||||||
private static Vector2 ComputeShiftedUv(in Vector2 uv)
|
|
||||||
{
|
|
||||||
var shifted = new Vector2(
|
|
||||||
uv.X - MathF.Floor(uv.X),
|
|
||||||
uv.Y - MathF.Floor(uv.Y));
|
|
||||||
|
|
||||||
shifted.X -= 0.5f;
|
|
||||||
if (shifted.X < 0f)
|
|
||||||
{
|
|
||||||
shifted.X += 1f;
|
|
||||||
}
|
|
||||||
|
|
||||||
shifted.Y -= 0.5f;
|
|
||||||
if (shifted.Y < 0f)
|
|
||||||
{
|
|
||||||
shifted.Y += 1f;
|
|
||||||
}
|
|
||||||
|
|
||||||
return shifted;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Vector4 SwapYxwz(in Vector4 v, Span<int> order)
|
|
||||||
{
|
|
||||||
var o0 = order[0];
|
|
||||||
var o1 = order[1];
|
|
||||||
var o2 = order[2];
|
|
||||||
var o3 = order[3];
|
|
||||||
|
|
||||||
order[0] = o1;
|
|
||||||
order[1] = o0;
|
|
||||||
order[2] = o3;
|
|
||||||
order[3] = o2;
|
|
||||||
|
|
||||||
return new Vector4(v.Y, v.X, v.W, v.Z);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Vector4 SwapWzyx(in Vector4 v, Span<int> order)
|
|
||||||
{
|
|
||||||
var o0 = order[0];
|
|
||||||
var o1 = order[1];
|
|
||||||
var o2 = order[2];
|
|
||||||
var o3 = order[3];
|
|
||||||
|
|
||||||
order[0] = o3;
|
|
||||||
order[1] = o2;
|
|
||||||
order[2] = o1;
|
|
||||||
order[3] = o0;
|
|
||||||
|
|
||||||
return new Vector4(v.W, v.Z, v.Y, v.X);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int IndexOfMax(ReadOnlySpan<float> values)
|
|
||||||
{
|
|
||||||
var bestIndex = -1;
|
|
||||||
var bestValue = 0f;
|
|
||||||
|
|
||||||
for (var i = 0; i < values.Length; i++)
|
|
||||||
{
|
|
||||||
if (values[i] > bestValue)
|
|
||||||
{
|
|
||||||
bestValue = values[i];
|
|
||||||
bestIndex = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bestIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool AreEqual(float a, float b) => MathF.Abs(a - b) <= 1e-5f;
|
|
||||||
|
|
||||||
private static Rgba32 PickMajorityColor(ReadOnlySpan<Rgba32> colors)
|
|
||||||
{
|
|
||||||
var counts = new Dictionary<Rgba32, int>(colors.Length);
|
|
||||||
foreach (var color in colors)
|
|
||||||
{
|
|
||||||
if (counts.TryGetValue(color, out var count))
|
|
||||||
{
|
|
||||||
counts[color] = count + 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
counts[color] = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return counts
|
|
||||||
.OrderByDescending(kvp => kvp.Value)
|
|
||||||
.ThenByDescending(kvp => kvp.Key.A)
|
|
||||||
.ThenByDescending(kvp => kvp.Key.R)
|
|
||||||
.ThenByDescending(kvp => kvp.Key.G)
|
|
||||||
.ThenByDescending(kvp => kvp.Key.B)
|
|
||||||
.First().Key;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool ShouldTrim(in TexMeta meta, int targetMaxDimension)
|
private static bool ShouldTrim(in TexMeta meta, int targetMaxDimension)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,7 +7,5 @@ public enum TextureMapKind
|
|||||||
Specular,
|
Specular,
|
||||||
Mask,
|
Mask,
|
||||||
Index,
|
Index,
|
||||||
Emissive,
|
|
||||||
Ui,
|
|
||||||
Unknown
|
Unknown
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Penumbra.Api.Enums;
|
|
||||||
using Penumbra.GameData.Files;
|
using Penumbra.GameData.Files;
|
||||||
|
|
||||||
namespace LightlessSync.Services.TextureCompression;
|
namespace LightlessSync.Services.TextureCompression;
|
||||||
@@ -37,9 +32,9 @@ public sealed class TextureMetadataHelper
|
|||||||
|
|
||||||
private static readonly (TextureUsageCategory Category, string Token)[] CategoryTokens =
|
private static readonly (TextureUsageCategory Category, string Token)[] CategoryTokens =
|
||||||
{
|
{
|
||||||
(TextureUsageCategory.Ui, "/ui/"),
|
(TextureUsageCategory.UI, "/ui/"),
|
||||||
(TextureUsageCategory.Ui, "/uld/"),
|
(TextureUsageCategory.UI, "/uld/"),
|
||||||
(TextureUsageCategory.Ui, "/icon/"),
|
(TextureUsageCategory.UI, "/icon/"),
|
||||||
|
|
||||||
(TextureUsageCategory.VisualEffect, "/vfx/"),
|
(TextureUsageCategory.VisualEffect, "/vfx/"),
|
||||||
|
|
||||||
@@ -104,9 +99,6 @@ public sealed class TextureMetadataHelper
|
|||||||
(TextureMapKind.Specular, "_s"),
|
(TextureMapKind.Specular, "_s"),
|
||||||
(TextureMapKind.Specular, "_spec"),
|
(TextureMapKind.Specular, "_spec"),
|
||||||
|
|
||||||
(TextureMapKind.Emissive, "_em"),
|
|
||||||
(TextureMapKind.Emissive, "_glow"),
|
|
||||||
|
|
||||||
(TextureMapKind.Index, "_id"),
|
(TextureMapKind.Index, "_id"),
|
||||||
(TextureMapKind.Index, "_idx"),
|
(TextureMapKind.Index, "_idx"),
|
||||||
(TextureMapKind.Index, "_index"),
|
(TextureMapKind.Index, "_index"),
|
||||||
@@ -133,10 +125,10 @@ public sealed class TextureMetadataHelper
|
|||||||
_dataManager = dataManager;
|
_dataManager = dataManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetRecommendationInfo(TextureCompressionTarget target, out (string Title, string Description) info)
|
public static bool TryGetRecommendationInfo(TextureCompressionTarget target, out (string Title, string Description) info)
|
||||||
=> RecommendationCatalog.TryGetValue(target, out info);
|
=> RecommendationCatalog.TryGetValue(target, out info);
|
||||||
|
|
||||||
public TextureUsageCategory DetermineCategory(string? gamePath)
|
public static TextureUsageCategory DetermineCategory(string? gamePath)
|
||||||
{
|
{
|
||||||
var normalized = Normalize(gamePath);
|
var normalized = Normalize(gamePath);
|
||||||
if (string.IsNullOrEmpty(normalized))
|
if (string.IsNullOrEmpty(normalized))
|
||||||
@@ -193,7 +185,7 @@ public sealed class TextureMetadataHelper
|
|||||||
return TextureUsageCategory.Unknown;
|
return TextureUsageCategory.Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string DetermineSlot(TextureUsageCategory category, string? gamePath)
|
public static string DetermineSlot(TextureUsageCategory category, string? gamePath)
|
||||||
{
|
{
|
||||||
if (category == TextureUsageCategory.Customization)
|
if (category == TextureUsageCategory.Customization)
|
||||||
return GuessCustomizationSlot(gamePath);
|
return GuessCustomizationSlot(gamePath);
|
||||||
@@ -218,7 +210,7 @@ public sealed class TextureMetadataHelper
|
|||||||
TextureUsageCategory.Companion => "Companion",
|
TextureUsageCategory.Companion => "Companion",
|
||||||
TextureUsageCategory.VisualEffect => "VFX",
|
TextureUsageCategory.VisualEffect => "VFX",
|
||||||
TextureUsageCategory.Housing => "Housing",
|
TextureUsageCategory.Housing => "Housing",
|
||||||
TextureUsageCategory.Ui => "UI",
|
TextureUsageCategory.UI => "UI",
|
||||||
_ => "General"
|
_ => "General"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -260,7 +252,7 @@ public sealed class TextureMetadataHelper
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddGameMaterialCandidates(string? gamePath, IList<MaterialCandidate> candidates)
|
private static void AddGameMaterialCandidates(string? gamePath, IList<MaterialCandidate> candidates)
|
||||||
{
|
{
|
||||||
var normalized = Normalize(gamePath);
|
var normalized = Normalize(gamePath);
|
||||||
if (string.IsNullOrEmpty(normalized))
|
if (string.IsNullOrEmpty(normalized))
|
||||||
@@ -286,7 +278,7 @@ public sealed class TextureMetadataHelper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddLocalMaterialCandidates(string? localTexturePath, IList<MaterialCandidate> candidates)
|
private static void AddLocalMaterialCandidates(string? localTexturePath, IList<MaterialCandidate> candidates)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(localTexturePath))
|
if (string.IsNullOrEmpty(localTexturePath))
|
||||||
return;
|
return;
|
||||||
@@ -397,7 +389,7 @@ public sealed class TextureMetadataHelper
|
|||||||
return TextureMapKind.Unknown;
|
return TextureMapKind.Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryMapFormatToTarget(string? format, out TextureCompressionTarget target)
|
public static bool TryMapFormatToTarget(string? format, out TextureCompressionTarget target)
|
||||||
{
|
{
|
||||||
var normalized = (format ?? string.Empty).ToUpperInvariant();
|
var normalized = (format ?? string.Empty).ToUpperInvariant();
|
||||||
if (normalized.Contains("BC1", StringComparison.Ordinal))
|
if (normalized.Contains("BC1", StringComparison.Ordinal))
|
||||||
@@ -434,7 +426,7 @@ public sealed class TextureMetadataHelper
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public (TextureCompressionTarget Target, string Reason)? GetSuggestedTarget(string? format, TextureMapKind mapKind)
|
public static (TextureCompressionTarget Target, string Reason)? GetSuggestedTarget(string? format, TextureMapKind mapKind)
|
||||||
{
|
{
|
||||||
TextureCompressionTarget? current = null;
|
TextureCompressionTarget? current = null;
|
||||||
if (TryMapFormatToTarget(format, out var mapped))
|
if (TryMapFormatToTarget(format, out var mapped))
|
||||||
@@ -446,7 +438,6 @@ public sealed class TextureMetadataHelper
|
|||||||
TextureMapKind.Mask => TextureCompressionTarget.BC4,
|
TextureMapKind.Mask => TextureCompressionTarget.BC4,
|
||||||
TextureMapKind.Index => TextureCompressionTarget.BC3,
|
TextureMapKind.Index => TextureCompressionTarget.BC3,
|
||||||
TextureMapKind.Specular => TextureCompressionTarget.BC4,
|
TextureMapKind.Specular => TextureCompressionTarget.BC4,
|
||||||
TextureMapKind.Emissive => TextureCompressionTarget.BC3,
|
|
||||||
TextureMapKind.Diffuse => TextureCompressionTarget.BC7,
|
TextureMapKind.Diffuse => TextureCompressionTarget.BC7,
|
||||||
_ => TextureCompressionTarget.BC7
|
_ => TextureCompressionTarget.BC7
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public enum TextureUsageCategory
|
|||||||
Companion,
|
Companion,
|
||||||
Monster,
|
Monster,
|
||||||
Housing,
|
Housing,
|
||||||
Ui,
|
UI,
|
||||||
VisualEffect,
|
VisualEffect,
|
||||||
Unknown
|
Unknown
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using Dalamud.Bindings.ImGui;
|
|||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Colors;
|
using Dalamud.Interface.Colors;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
using Dalamud.Utility;
|
|
||||||
using LightlessSync.API.Dto.Group;
|
using LightlessSync.API.Dto.Group;
|
||||||
using LightlessSync.LightlessConfiguration;
|
using LightlessSync.LightlessConfiguration;
|
||||||
using LightlessSync.Services;
|
using LightlessSync.Services;
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ using Dalamud.Bindings.ImGui;
|
|||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Utility;
|
|
||||||
using LightlessSync.API.Data.Enum;
|
|
||||||
using LightlessSync.API.Data.Extensions;
|
using LightlessSync.API.Data.Extensions;
|
||||||
using LightlessSync.API.Dto.Group;
|
using LightlessSync.API.Dto.Group;
|
||||||
using LightlessSync.Interop.Ipc;
|
using LightlessSync.Interop.Ipc;
|
||||||
@@ -24,11 +22,9 @@ using LightlessSync.WebAPI.Files;
|
|||||||
using LightlessSync.WebAPI.Files.Models;
|
using LightlessSync.WebAPI.Files.Models;
|
||||||
using LightlessSync.WebAPI.SignalR.Utils;
|
using LightlessSync.WebAPI.SignalR.Utils;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
|
|||||||
@@ -12,16 +12,14 @@ using LightlessSync.Services;
|
|||||||
using LightlessSync.Services.Mediator;
|
using LightlessSync.Services.Mediator;
|
||||||
using LightlessSync.Services.TextureCompression;
|
using LightlessSync.Services.TextureCompression;
|
||||||
using LightlessSync.Utils;
|
using LightlessSync.Utils;
|
||||||
using Penumbra.Api.Enums;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System;
|
using OtterTex;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Threading;
|
using SixLabors.ImageSharp;
|
||||||
using System.Threading.Tasks;
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
|
using SixLabors.ImageSharp.Processing;
|
||||||
|
using ImageSharpImage = SixLabors.ImageSharp.Image;
|
||||||
|
|
||||||
namespace LightlessSync.UI;
|
namespace LightlessSync.UI;
|
||||||
|
|
||||||
@@ -810,11 +808,11 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
var primaryGamePath = entry.GamePaths.FirstOrDefault() ?? string.Empty;
|
var primaryGamePath = entry.GamePaths.FirstOrDefault() ?? string.Empty;
|
||||||
var classificationPath = string.IsNullOrEmpty(primaryGamePath) ? primaryFile : primaryGamePath;
|
var classificationPath = string.IsNullOrEmpty(primaryGamePath) ? primaryFile : primaryGamePath;
|
||||||
var mapKind = _textureMetadataHelper.DetermineMapKind(primaryGamePath, primaryFile);
|
var mapKind = _textureMetadataHelper.DetermineMapKind(primaryGamePath, primaryFile);
|
||||||
var category = _textureMetadataHelper.DetermineCategory(classificationPath);
|
var category = TextureMetadataHelper.DetermineCategory(classificationPath);
|
||||||
var slot = _textureMetadataHelper.DetermineSlot(category, classificationPath);
|
var slot = TextureMetadataHelper.DetermineSlot(category, classificationPath);
|
||||||
var format = entry.Format.Value;
|
var format = entry.Format.Value;
|
||||||
var suggestion = _textureMetadataHelper.GetSuggestedTarget(format, mapKind);
|
var suggestion = TextureMetadataHelper.GetSuggestedTarget(format, mapKind);
|
||||||
TextureCompressionTarget? currentTarget = _textureMetadataHelper.TryMapFormatToTarget(format, out var mappedTarget)
|
TextureCompressionTarget? currentTarget = TextureMetadataHelper.TryMapFormatToTarget(format, out var mappedTarget)
|
||||||
? mappedTarget
|
? mappedTarget
|
||||||
: null;
|
: null;
|
||||||
var displayName = Path.GetFileName(primaryFile);
|
var displayName = Path.GetFileName(primaryFile);
|
||||||
@@ -2014,23 +2012,43 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
private async Task<IDalamudTextureWrap?> BuildPreviewAsync(TextureRow row, CancellationToken token)
|
private async Task<IDalamudTextureWrap?> BuildPreviewAsync(TextureRow row, CancellationToken token)
|
||||||
{
|
{
|
||||||
if (!_ipcManager.Penumbra.APIAvailable)
|
const int PreviewMaxDimension = 1024;
|
||||||
|
|
||||||
|
token.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
if (!File.Exists(row.PrimaryFilePath))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tempFile = Path.Combine(Path.GetTempPath(), $"lightless_preview_{Guid.NewGuid():N}.png");
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var job = new TextureConversionJob(row.PrimaryFilePath, tempFile, TextureType.Png, IncludeMipMaps: false);
|
using var scratch = TexFileHelper.Load(row.PrimaryFilePath);
|
||||||
await _ipcManager.Penumbra.ConvertTextureFiles(_logger, new[] { job }, null, token).ConfigureAwait(false);
|
using var rgbaScratch = scratch.GetRGBA(out var rgbaInfo).ThrowIfError(rgbaInfo);
|
||||||
if (!File.Exists(tempFile))
|
|
||||||
|
var meta = rgbaInfo.Meta;
|
||||||
|
var width = meta.Width;
|
||||||
|
var height = meta.Height;
|
||||||
|
var bytesPerPixel = meta.Format.BitsPerPixel() / 8;
|
||||||
|
var requiredLength = width * height * bytesPerPixel;
|
||||||
|
|
||||||
|
token.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
var rgbaPixels = rgbaScratch.Pixels[..requiredLength].ToArray();
|
||||||
|
using var image = ImageSharpImage.LoadPixelData<Rgba32>(rgbaPixels, width, height);
|
||||||
|
|
||||||
|
if (Math.Max(width, height) > PreviewMaxDimension)
|
||||||
{
|
{
|
||||||
return null;
|
var dominant = Math.Max(width, height);
|
||||||
|
var scale = PreviewMaxDimension / (float)dominant;
|
||||||
|
var targetWidth = Math.Max(1, (int)MathF.Round(width * scale));
|
||||||
|
var targetHeight = Math.Max(1, (int)MathF.Round(height * scale));
|
||||||
|
image.Mutate(ctx => ctx.Resize(targetWidth, targetHeight, KnownResamplers.Lanczos3));
|
||||||
}
|
}
|
||||||
|
|
||||||
var data = await File.ReadAllBytesAsync(tempFile, token).ConfigureAwait(false);
|
using var ms = new MemoryStream();
|
||||||
return _uiSharedService.LoadImage(data);
|
await image.SaveAsPngAsync(ms, cancellationToken: token).ConfigureAwait(false);
|
||||||
|
return _uiSharedService.LoadImage(ms.ToArray());
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
@@ -2041,20 +2059,6 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
_logger.LogDebug(ex, "Preview generation failed for {File}", row.PrimaryFilePath);
|
_logger.LogDebug(ex, "Preview generation failed for {File}", row.PrimaryFilePath);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
finally
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (File.Exists(tempFile))
|
|
||||||
{
|
|
||||||
File.Delete(tempFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogTrace(ex, "Failed to clean up preview temp file {File}", tempFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ResetPreview(string key)
|
private void ResetPreview(string key)
|
||||||
@@ -2291,7 +2295,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
_textureSelections[row.Key] = selectedTarget;
|
_textureSelections[row.Key] = selectedTarget;
|
||||||
}
|
}
|
||||||
var hasSelectedInfo = _textureMetadataHelper.TryGetRecommendationInfo(selectedTarget, out var selectedInfo);
|
var hasSelectedInfo = TextureMetadataHelper.TryGetRecommendationInfo(selectedTarget, out var selectedInfo);
|
||||||
|
|
||||||
using (ImRaii.Child("textureDetailInfo", new Vector2(-1, 0), true, ImGuiWindowFlags.AlwaysVerticalScrollbar))
|
using (ImRaii.Child("textureDetailInfo", new Vector2(-1, 0), true, ImGuiWindowFlags.AlwaysVerticalScrollbar))
|
||||||
{
|
{
|
||||||
@@ -2425,7 +2429,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
if (row.SuggestedTarget.HasValue)
|
if (row.SuggestedTarget.HasValue)
|
||||||
{
|
{
|
||||||
var recommendedTarget = row.SuggestedTarget.Value;
|
var recommendedTarget = row.SuggestedTarget.Value;
|
||||||
var hasRecommendationInfo = _textureMetadataHelper.TryGetRecommendationInfo(recommendedTarget, out var recommendedInfo);
|
var hasRecommendationInfo = TextureMetadataHelper.TryGetRecommendationInfo(recommendedTarget, out var recommendedInfo);
|
||||||
var recommendedTitle = hasRecommendationInfo ? recommendedInfo!.Title : recommendedTarget.ToString();
|
var recommendedTitle = hasRecommendationInfo ? recommendedInfo!.Title : recommendedTarget.ToString();
|
||||||
var recommendedDescription = hasRecommendationInfo
|
var recommendedDescription = hasRecommendationInfo
|
||||||
? recommendedInfo!.Description
|
? recommendedInfo!.Description
|
||||||
@@ -2634,4 +2638,4 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
namespace LightlessSync.UI
|
|
||||||
{
|
|
||||||
public enum ProfileTags
|
|
||||||
{
|
|
||||||
SFW = 0,
|
|
||||||
NSFW = 1,
|
|
||||||
|
|
||||||
RP = 2,
|
|
||||||
ERP = 3,
|
|
||||||
No_RP = 4,
|
|
||||||
No_ERP = 5,
|
|
||||||
|
|
||||||
Venues = 6,
|
|
||||||
Gpose = 7,
|
|
||||||
|
|
||||||
Limsa = 8,
|
|
||||||
Gridania = 9,
|
|
||||||
Ul_dah = 10,
|
|
||||||
|
|
||||||
WUT = 11,
|
|
||||||
|
|
||||||
PVP = 1001,
|
|
||||||
Ultimate = 1002,
|
|
||||||
Raids = 1003,
|
|
||||||
Roulette = 1004,
|
|
||||||
Crafting = 1005,
|
|
||||||
Casual = 1006,
|
|
||||||
Hardcore = 1007,
|
|
||||||
Glamour = 1008,
|
|
||||||
Mentor = 1009,
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -28,23 +28,16 @@ using LightlessSync.WebAPI;
|
|||||||
using LightlessSync.WebAPI.Files;
|
using LightlessSync.WebAPI.Files;
|
||||||
using LightlessSync.WebAPI.Files.Models;
|
using LightlessSync.WebAPI.Files.Models;
|
||||||
using LightlessSync.WebAPI.SignalR.Utils;
|
using LightlessSync.WebAPI.SignalR.Utils;
|
||||||
using DalamudObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
|
|
||||||
using Microsoft.AspNetCore.Http.Connections;
|
using Microsoft.AspNetCore.Http.Connections;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
|
||||||
using FfxivCharacter = FFXIVClientStructs.FFXIV.Client.Game.Character.Character;
|
|
||||||
using FfxivCharacterBase = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.CharacterBase;
|
|
||||||
|
|
||||||
namespace LightlessSync.UI;
|
namespace LightlessSync.UI;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
using Dalamud.Interface.ImGuiSeStringRenderer;
|
|
||||||
using Dalamud.Interface.Textures.TextureWraps;
|
using Dalamud.Interface.Textures.TextureWraps;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
using LightlessSync.API.Data;
|
using LightlessSync.API.Data;
|
||||||
@@ -13,9 +12,6 @@ using LightlessSync.UI.Services;
|
|||||||
using LightlessSync.UI.Tags;
|
using LightlessSync.UI.Tags;
|
||||||
using LightlessSync.Utils;
|
using LightlessSync.Utils;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
namespace LightlessSync.UI;
|
namespace LightlessSync.UI;
|
||||||
|
|||||||
@@ -4,21 +4,16 @@ using Dalamud.Interface.Colors;
|
|||||||
using Dalamud.Interface.ImGuiFileDialog;
|
using Dalamud.Interface.ImGuiFileDialog;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using LightlessSync.API.Data;
|
|
||||||
using LightlessSync.API.Data.Enum;
|
using LightlessSync.API.Data.Enum;
|
||||||
using LightlessSync.API.Data.Extensions;
|
using LightlessSync.API.Data.Extensions;
|
||||||
using LightlessSync.API.Dto.Group;
|
using LightlessSync.API.Dto.Group;
|
||||||
using LightlessSync.API.Dto.User;
|
|
||||||
using LightlessSync.Services;
|
using LightlessSync.Services;
|
||||||
using LightlessSync.Services.Mediator;
|
using LightlessSync.Services.Mediator;
|
||||||
using LightlessSync.UI.Handlers;
|
|
||||||
using LightlessSync.PlayerData.Pairs;
|
using LightlessSync.PlayerData.Pairs;
|
||||||
using LightlessSync.WebAPI;
|
using LightlessSync.WebAPI;
|
||||||
using LightlessSync.UI.Services;
|
using LightlessSync.UI.Services;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
|
||||||
|
|
||||||
namespace LightlessSync.UI;
|
namespace LightlessSync.UI;
|
||||||
|
|
||||||
|
|||||||
@@ -13,10 +13,7 @@ using LightlessSync.Utils;
|
|||||||
using LightlessSync.WebAPI;
|
using LightlessSync.WebAPI;
|
||||||
using LightlessSync.UI.Services;
|
using LightlessSync.UI.Services;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LightlessSync.UI;
|
namespace LightlessSync.UI;
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ using Dalamud.Interface.Textures.TextureWraps;
|
|||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
using LightlessSync.Utils;
|
using LightlessSync.Utils;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
namespace LightlessSync.UI.Tags;
|
namespace LightlessSync.UI.Tags;
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
using LightlessSync.UI;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
namespace LightlessSync.UI.Tags;
|
namespace LightlessSync.UI.Tags;
|
||||||
@@ -35,16 +32,16 @@ public sealed class ProfileTagService
|
|||||||
|
|
||||||
private static IReadOnlyDictionary<int, ProfileTagDefinition> CreateTagLibrary()
|
private static IReadOnlyDictionary<int, ProfileTagDefinition> CreateTagLibrary()
|
||||||
{
|
{
|
||||||
var dictionary = new Dictionary<int, ProfileTagDefinition>
|
return new Dictionary<int, ProfileTagDefinition>
|
||||||
{
|
{
|
||||||
[(int)ProfileTags.SFW] = ProfileTagDefinition.FromIconAndText(
|
[0] = ProfileTagDefinition.FromIconAndText(
|
||||||
230419,
|
230419,
|
||||||
"SFW",
|
"SFW",
|
||||||
background: new Vector4(0.16f, 0.24f, 0.18f, 0.95f),
|
background: new Vector4(0.16f, 0.24f, 0.18f, 0.95f),
|
||||||
border: new Vector4(0.32f, 0.52f, 0.34f, 0.85f),
|
border: new Vector4(0.32f, 0.52f, 0.34f, 0.85f),
|
||||||
textColor: new Vector4(0.78f, 0.94f, 0.80f, 1f)),
|
textColor: new Vector4(0.78f, 0.94f, 0.80f, 1f)),
|
||||||
|
|
||||||
[(int)ProfileTags.NSFW] = ProfileTagDefinition.FromIconAndText(
|
[1] = ProfileTagDefinition.FromIconAndText(
|
||||||
230419,
|
230419,
|
||||||
"NSFW",
|
"NSFW",
|
||||||
background: new Vector4(0.32f, 0.18f, 0.22f, 0.95f),
|
background: new Vector4(0.32f, 0.18f, 0.22f, 0.95f),
|
||||||
@@ -52,28 +49,28 @@ public sealed class ProfileTagService
|
|||||||
textColor: new Vector4(1f, 0.82f, 0.86f, 1f)),
|
textColor: new Vector4(1f, 0.82f, 0.86f, 1f)),
|
||||||
|
|
||||||
|
|
||||||
[(int)ProfileTags.RP] = ProfileTagDefinition.FromIconAndText(
|
[2] = ProfileTagDefinition.FromIconAndText(
|
||||||
61545,
|
61545,
|
||||||
"RP",
|
"RP",
|
||||||
background: new Vector4(0.20f, 0.20f, 0.30f, 0.95f),
|
background: new Vector4(0.20f, 0.20f, 0.30f, 0.95f),
|
||||||
border: new Vector4(0.42f, 0.42f, 0.66f, 0.85f),
|
border: new Vector4(0.42f, 0.42f, 0.66f, 0.85f),
|
||||||
textColor: new Vector4(0.80f, 0.84f, 1f, 1f)),
|
textColor: new Vector4(0.80f, 0.84f, 1f, 1f)),
|
||||||
|
|
||||||
[(int)ProfileTags.ERP] = ProfileTagDefinition.FromIconAndText(
|
[3] = ProfileTagDefinition.FromIconAndText(
|
||||||
61545,
|
61545,
|
||||||
"ERP",
|
"ERP",
|
||||||
background: new Vector4(0.20f, 0.20f, 0.30f, 0.95f),
|
background: new Vector4(0.20f, 0.20f, 0.30f, 0.95f),
|
||||||
border: new Vector4(0.42f, 0.42f, 0.66f, 0.85f),
|
border: new Vector4(0.42f, 0.42f, 0.66f, 0.85f),
|
||||||
textColor: new Vector4(0.80f, 0.84f, 1f, 1f)),
|
textColor: new Vector4(0.80f, 0.84f, 1f, 1f)),
|
||||||
|
|
||||||
[(int)ProfileTags.No_RP] = ProfileTagDefinition.FromIconAndText(
|
[4] = ProfileTagDefinition.FromIconAndText(
|
||||||
230420,
|
230420,
|
||||||
"No RP",
|
"No RP",
|
||||||
background: new Vector4(0.30f, 0.18f, 0.30f, 0.95f),
|
background: new Vector4(0.30f, 0.18f, 0.30f, 0.95f),
|
||||||
border: new Vector4(0.69f, 0.40f, 0.65f, 0.85f),
|
border: new Vector4(0.69f, 0.40f, 0.65f, 0.85f),
|
||||||
textColor: new Vector4(1f, 0.84f, 1f, 1f)),
|
textColor: new Vector4(1f, 0.84f, 1f, 1f)),
|
||||||
|
|
||||||
[(int)ProfileTags.No_ERP] = ProfileTagDefinition.FromIconAndText(
|
[5] = ProfileTagDefinition.FromIconAndText(
|
||||||
230420,
|
230420,
|
||||||
"No ERP",
|
"No ERP",
|
||||||
background: new Vector4(0.30f, 0.18f, 0.30f, 0.95f),
|
background: new Vector4(0.30f, 0.18f, 0.30f, 0.95f),
|
||||||
@@ -81,14 +78,14 @@ public sealed class ProfileTagService
|
|||||||
textColor: new Vector4(1f, 0.84f, 1f, 1f)),
|
textColor: new Vector4(1f, 0.84f, 1f, 1f)),
|
||||||
|
|
||||||
|
|
||||||
[(int)ProfileTags.Venues] = ProfileTagDefinition.FromIconAndText(
|
[6] = ProfileTagDefinition.FromIconAndText(
|
||||||
60756,
|
60756,
|
||||||
"Venues",
|
"Venues",
|
||||||
background: new Vector4(0.18f, 0.24f, 0.28f, 0.95f),
|
background: new Vector4(0.18f, 0.24f, 0.28f, 0.95f),
|
||||||
border: new Vector4(0.33f, 0.55f, 0.63f, 0.85f),
|
border: new Vector4(0.33f, 0.55f, 0.63f, 0.85f),
|
||||||
textColor: new Vector4(0.78f, 0.90f, 0.97f, 1f)),
|
textColor: new Vector4(0.78f, 0.90f, 0.97f, 1f)),
|
||||||
|
|
||||||
[(int)ProfileTags.Gpose] = ProfileTagDefinition.FromIconAndText(
|
[7] = ProfileTagDefinition.FromIconAndText(
|
||||||
61546,
|
61546,
|
||||||
"GPose",
|
"GPose",
|
||||||
background: new Vector4(0.18f, 0.18f, 0.26f, 0.95f),
|
background: new Vector4(0.18f, 0.18f, 0.26f, 0.95f),
|
||||||
@@ -96,36 +93,33 @@ public sealed class ProfileTagService
|
|||||||
textColor: new Vector4(0.80f, 0.82f, 0.96f, 1f)),
|
textColor: new Vector4(0.80f, 0.82f, 0.96f, 1f)),
|
||||||
|
|
||||||
|
|
||||||
[(int)ProfileTags.Limsa] = ProfileTagDefinition.FromIconAndText(
|
[8] = ProfileTagDefinition.FromIconAndText(
|
||||||
60572,
|
60572,
|
||||||
"Limsa"),
|
"Limsa"),
|
||||||
|
|
||||||
[(int)ProfileTags.Gridania] = ProfileTagDefinition.FromIconAndText(
|
[9] = ProfileTagDefinition.FromIconAndText(
|
||||||
60573,
|
60573,
|
||||||
"Gridania"),
|
"Gridania"),
|
||||||
|
|
||||||
[(int)ProfileTags.Ul_dah] = ProfileTagDefinition.FromIconAndText(
|
[10] = ProfileTagDefinition.FromIconAndText(
|
||||||
60574,
|
60574,
|
||||||
"Ul'dah"),
|
"Ul'dah"),
|
||||||
|
|
||||||
|
|
||||||
[(int)ProfileTags.WUT] = ProfileTagDefinition.FromIconAndText(
|
[11] = ProfileTagDefinition.FromIconAndText(
|
||||||
61397,
|
61397,
|
||||||
"WU/T"),
|
"WU/T"),
|
||||||
|
|
||||||
|
|
||||||
[(int)ProfileTags.PVP] = ProfileTagDefinition.FromIcon(61806),
|
[1001] = ProfileTagDefinition.FromIcon(61806), // PVP
|
||||||
[(int)ProfileTags.Ultimate] = ProfileTagDefinition.FromIcon(61832),
|
[1002] = ProfileTagDefinition.FromIcon(61832), // Ultimate
|
||||||
[(int)ProfileTags.Raids] = ProfileTagDefinition.FromIcon(61802),
|
[1003] = ProfileTagDefinition.FromIcon(61802), // Raids
|
||||||
[(int)ProfileTags.Roulette] = ProfileTagDefinition.FromIcon(61807),
|
[1004] = ProfileTagDefinition.FromIcon(61807), // Roulette
|
||||||
[(int)ProfileTags.Crafting] = ProfileTagDefinition.FromIcon(61816),
|
[1005] = ProfileTagDefinition.FromIcon(61816), // Crafting
|
||||||
[(int)ProfileTags.Casual] = ProfileTagDefinition.FromIcon(61753),
|
[1006] = ProfileTagDefinition.FromIcon(61753), // Casual
|
||||||
[(int)ProfileTags.Hardcore] = ProfileTagDefinition.FromIcon(61754),
|
[1007] = ProfileTagDefinition.FromIcon(61754), // Hardcore
|
||||||
[(int)ProfileTags.Glamour] = ProfileTagDefinition.FromIcon(61759),
|
[1008] = ProfileTagDefinition.FromIcon(61759), // Glamour
|
||||||
[(int)ProfileTags.Mentor] = ProfileTagDefinition.FromIcon(61760)
|
[1009] = ProfileTagDefinition.FromIcon(61760) // Mentor
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return dictionary;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using Dalamud.Utility;
|
|
||||||
using K4os.Compression.LZ4.Legacy;
|
using K4os.Compression.LZ4.Legacy;
|
||||||
using LightlessSync.API.Data;
|
using LightlessSync.API.Data;
|
||||||
using LightlessSync.API.Dto.Files;
|
using LightlessSync.API.Dto.Files;
|
||||||
@@ -10,13 +9,9 @@ using LightlessSync.Services.Mediator;
|
|||||||
using LightlessSync.Services.TextureCompression;
|
using LightlessSync.Services.TextureCompression;
|
||||||
using LightlessSync.WebAPI.Files.Models;
|
using LightlessSync.WebAPI.Files.Models;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.IO;
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using LightlessSync.LightlessConfiguration;
|
using LightlessSync.LightlessConfiguration;
|
||||||
|
|
||||||
namespace LightlessSync.WebAPI.Files;
|
namespace LightlessSync.WebAPI.Files;
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ using LightlessSync.WebAPI.Files.Models;
|
|||||||
using LightlessSync.WebAPI.SignalR;
|
using LightlessSync.WebAPI.SignalR;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Net.Http;
|
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ using Microsoft.Extensions.Logging;
|
|||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace LightlessSync.WebAPI.Files;
|
namespace LightlessSync.WebAPI.Files;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user