Rewrote quantizer

This commit is contained in:
2025-01-25 20:28:51 +01:00
parent 3f238c619e
commit 9ddafca929
10 changed files with 357 additions and 548 deletions

View File

@@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.IO;
using PSXSplash.RuntimeCode;
using UnityEditor;
@@ -9,13 +10,9 @@ public class QuantizedPreviewWindow : EditorWindow
private Texture2D originalTexture;
private Texture2D quantizedTexture;
private Texture2D vramTexture; // New VRAM Texture
private ushort[] clut; // Changed to 1D array
private List<VRAMPixel> clut; // Changed to 1D array
private ushort[] indexedPixelData; // New field for indexed pixel data
private int bpp = 4;
private int targetWidth = 128;
private int targetHeight = 128;
public bool dithering = true;
private int maxKMeans = 100;
private PSXBPP bpp;
private readonly int previewSize = 256;
[MenuItem("Window/Quantized Preview")]
@@ -31,13 +28,9 @@ public class QuantizedPreviewWindow : EditorWindow
originalTexture = (Texture2D)EditorGUILayout.ObjectField("Original Texture", originalTexture, typeof(Texture2D), false);
targetWidth = EditorGUILayout.IntField("Target Width", targetWidth);
targetHeight = EditorGUILayout.IntField("Target Height", targetHeight);
dithering = EditorGUILayout.Toggle("Dithering", dithering);
bpp = (PSXBPP)EditorGUILayout.EnumPopup("Bit Depth", bpp);
bpp = EditorGUILayout.IntPopup("Bits Per Pixel", bpp, new[] { "4 bpp", "8 bpp", "16 bpp" }, new[] { 4, 8, 16 });
maxKMeans = EditorGUILayout.IntField("Max K-Means", maxKMeans);
if (GUILayout.Button("Generate Quantized Preview") && originalTexture != null)
{
@@ -121,9 +114,9 @@ public class QuantizedPreviewWindow : EditorWindow
using (FileStream fileStream = new FileStream(path, FileMode.Create, FileAccess.Write))
using (BinaryWriter writer = new BinaryWriter(fileStream))
{
foreach (ushort value in clut)
foreach (VRAMPixel value in clut)
{
writer.Write(value);
writer.Write(value.Pack());
}
}
}
@@ -133,145 +126,25 @@ public class QuantizedPreviewWindow : EditorWindow
private void GenerateQuantizedPreview()
{
Texture2D resizedTexture = PSXTexture.ResizeTexture(originalTexture, targetWidth, targetHeight);
if(dithering) {
resizedTexture = PSXTexture.DitherTexture(resizedTexture);
}
PSXTexture2D psxTex = PSXTexture2D.CreateFromTexture2D(originalTexture, bpp, false);
if (bpp == 16)
{
quantizedTexture = null;
indexedPixelData = PSXTexture.ConvertTo16Bpp(resizedTexture);
clut = null;
vramTexture = ConvertTo16BppTexture2D(resizedTexture);
}
else
{
var (indexedPixels, generatedClut) = ImageQuantizer.Quantize(resizedTexture, bpp, maxKMeans);
quantizedTexture = psxTex.GeneratePreview();
vramTexture = psxTex.GenerateVramPreview();
clut = psxTex.ColorPalette;
indexedPixelData = indexedPixels;
clut = generatedClut;
int pixelSize = bpp == 4 ? 4 : bpp == 8 ? 2 : 1;
quantizedTexture = new Texture2D(resizedTexture.width, resizedTexture.height);
Color[] quantizedColors = new Color[resizedTexture.width * resizedTexture.height];
int pixelIndex = 0;
for (int y = 0; y < resizedTexture.height; y++)
{
for (int x = 0; x < resizedTexture.width; x++)
{
int index;
if (pixelSize == 4)
{
int packedValue = indexedPixelData[pixelIndex];
index = (packedValue >> ((x % 4) * 4)) & 0xF;
}
else if (pixelSize == 2)
{
int packedValue = indexedPixelData[pixelIndex];
index = (packedValue >> ((x % 2) * 8)) & 0xFF;
}
else
{
index = indexedPixelData[pixelIndex];
}
Vector3 color = new Vector3(
(clut[index] & 31) / 31.0f, // Red: bits 04
((clut[index] >> 5) & 31) / 31.0f, // Green: bits 59
((clut[index] >> 10) & 31) / 31.0f // Blue: bits 1014
);
quantizedColors[y * resizedTexture.width + x] = new Color(color.x, color.y, color.z);
if ((x % pixelSize) == (pixelSize - 1))
{
pixelIndex++;
}
}
}
quantizedTexture.SetPixels(quantizedColors);
quantizedTexture.Apply();
vramTexture = CreateVramTexture(resizedTexture.width, resizedTexture.height, indexedPixelData);
}
}
private Texture2D CreateVramTexture(int width, int height, ushort[] indexedData)
{
int adjustedWidth = width;
if (bpp == 4)
{
adjustedWidth = Mathf.CeilToInt(width / 4f);
}
else if (bpp == 8)
{
adjustedWidth = Mathf.CeilToInt(width / 2f);
}
Texture2D vramTexture = new Texture2D(adjustedWidth, height);
Color[] vramColors = new Color[adjustedWidth * height];
for (int i = 0; i < indexedData.Length; i++)
{
int index = indexedData[i];
float r = (index & 31) / 31.0f; // Red: bits 04
float g = ((index >> 5) & 31) / 31.0f; // Green: bits 59
float b = ((index >> 10) & 31) / 31.0f; // Blue: bits 1014
vramColors[i] = new Color(r, g, b);
}
vramTexture.SetPixels(vramColors);
vramTexture.Apply();
return vramTexture;
}
private void DrawTexturePreview(Texture2D texture, int size, bool flipY = true)
{
Rect rect = GUILayoutUtility.GetRect(size, size, GUILayout.ExpandWidth(false));
// Flip the texture on the Y-axis
Texture2D displayedTexture = flipY ? FlipTextureY(texture) : texture;
EditorGUI.DrawPreviewTexture(rect, displayedTexture, null, ScaleMode.ScaleToFit, 0, 0, ColorWriteMask.All);
EditorGUI.DrawPreviewTexture(rect, texture, null, ScaleMode.ScaleToFit, 0, 0, ColorWriteMask.All);
}
private Texture2D FlipTextureY(Texture2D texture)
{
Color[] originalPixels = texture.GetPixels();
Color[] flippedPixels = new Color[originalPixels.Length];
int width = texture.width;
int height = texture.height;
// Flip the pixels on the Y-axis
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
flippedPixels[(height - y - 1) * width + x] = originalPixels[y * width + x];
}
}
Texture2D flippedTexture = new Texture2D(width, height);
flippedTexture.SetPixels(flippedPixels);
flippedTexture.Apply();
return flippedTexture;
}
private void DrawCLUT()
{
@@ -282,7 +155,7 @@ public class QuantizedPreviewWindow : EditorWindow
GUILayout.Space(10);
int totalColors = clut.Length;
int totalColors = clut.Count;
int totalRows = Mathf.CeilToInt((float)totalColors / maxColorsPerRow);
for (int row = 0; row < totalRows; row++)
@@ -296,9 +169,9 @@ public class QuantizedPreviewWindow : EditorWindow
int index = row * maxColorsPerRow + col;
Vector3 color = new Vector3(
(clut[index] & 31) / 31.0f, // Red: bits 04
((clut[index] >> 5) & 31) / 31.0f, // Green: bits 59
((clut[index] >> 10) & 31) / 31.0f // Blue: bits 1014
clut[index].R / 31.0f, // Red: bits 04
clut[index].G / 31.0f, // Green: bits 59
clut[index].B / 31.0f // Blue: bits 1014
);
Rect rect = GUILayoutUtility.GetRect(swatchSize, swatchSize, GUILayout.ExpandWidth(false));
@@ -309,35 +182,4 @@ public class QuantizedPreviewWindow : EditorWindow
}
}
private Texture2D ConvertTo16BppTexture2D(Texture2D source)
{
int width = source.width;
int height = source.height;
Texture2D convertedTexture = new Texture2D(width, height);
Color[] originalPixels = source.GetPixels();
Color[] convertedPixels = new Color[originalPixels.Length];
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int flippedY = height - y - 1;
Color pixel = originalPixels[flippedY * width + x];
float r = Mathf.Floor(pixel.r * 31) / 31.0f; // 5 bits for red
float g = Mathf.Floor(pixel.g * 31) / 31.0f; // 5 bits for green
float b = Mathf.Floor(pixel.b * 31) / 31.0f; // 5 bits for blue
convertedPixels[y * width + x] = new Color(r, g, b, pixel.a);
}
}
convertedTexture.SetPixels(convertedPixels);
convertedTexture.Apply();
return convertedTexture;
}
}