diff --git a/TelltaleTextureTool/TelltaleTextureTool/App.axaml b/TelltaleTextureTool/TelltaleTextureTool/App.axaml
index 134f304..8379afa 100644
--- a/TelltaleTextureTool/TelltaleTextureTool/App.axaml
+++ b/TelltaleTextureTool/TelltaleTextureTool/App.axaml
@@ -17,15 +17,175 @@
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
(filePath);
-
- bool hasAlpha = ImageUtilities.IsImageOpaque(image);
- string hasAlphaString = hasAlpha ? "True" : "False";
-
- imageProperties = new ImageProperties()
+ // Load the image using SkiaSharp
+ using (var skBitmap = SKBitmap.Decode(filePath))
{
- Name = Path.GetFileNameWithoutExtension(filePath),
- Extension = Path.GetExtension(filePath),
- CompressionType = imageInfo.Metadata.DecodedImageFormat.Name,
- ChannelCount = (imageInfo.PixelType.BitsPerPixel / 8).ToString(),
- Height = imageInfo.Height.ToString(),
- Width = imageInfo.Width.ToString(),
- HasAlpha = hasAlphaString,
- MipMapCount = "N/A"
- };
+ // Create a memory stream to hold the image data
+ using (var ms = new MemoryStream())
+ {
+ // Encode the SKBitmap to a PNG and write to the memory stream
+ skBitmap.Encode(ms, SKEncodedImageFormat.Png, 100);
+ ms.Seek(0, SeekOrigin.Begin); // Rewind the stream to the beginning
+
+ // Create an Avalonia Bitmap from the memory stream
+ bitmap = new Bitmap(ms);
+ }
+
+ string hasAlphaString = skBitmap.Info.IsOpaque ? "True" : "False";
+
+ imageProperties = new ImageProperties()
+ {
+ Name = Path.GetFileNameWithoutExtension(filePath),
+ Extension = Path.GetExtension(filePath),
+ SurfaceFormat = skBitmap.ColorType.ToString(),
+ ChannelCount = (skBitmap.BytesPerPixel*8).ToString(),
+ Height = skBitmap.Height.ToString(),
+ Width = skBitmap.Width.ToString(),
+ HasAlpha = hasAlphaString,
+ MipMapCount = "N/A" // SkiaSharp does not provide mipmap count directly
+ };
+ }
}
private void GetImageDataFromD3DTX(string filePath, D3DTXVersion d3DTXVersion, out Bitmap bitmap, out ImageProperties imageProperties)
@@ -94,7 +103,7 @@ private void GetImageDataFromD3DTX(string filePath, D3DTXVersion d3DTXVersion, o
imageProperties = new ImageProperties()
{
Name = metadata.TextureName,
- CompressionType = d3dtx.GetStringFormat(),
+ SurfaceFormat = d3dtx.GetStringFormat(),
Width = metadata.Width.ToString(),
Height = metadata.Height.ToString(),
HasAlpha = d3dtx.GetHasAlpha(),
@@ -131,7 +140,7 @@ private void GetImageDataFromTIFF(string filePath, out Bitmap bitmap, out ImageP
{
Name = Path.GetFileNameWithoutExtension(filePath),
Extension = Path.GetExtension(filePath),
- CompressionType = imageInfo.Metadata.DecodedImageFormat.Name,
+ SurfaceFormat = imageInfo.Metadata.DecodedImageFormat.Name,
ChannelCount = (imageInfo.PixelType.BitsPerPixel / 8).ToString(),
Height = imageInfo.Height.ToString(),
Width = imageInfo.Width.ToString(),
@@ -147,7 +156,7 @@ private void GetImageDataFromInvalid(out Bitmap? bitmap, out ImageProperties ima
imageProperties = new ImageProperties()
{
Name = "",
- CompressionType = "",
+ SurfaceFormat = "",
ChannelCount = "",
Height = "",
Width = "",
@@ -303,7 +312,6 @@ private static Bitmap GetDDSBitmap(IImage image)
throw new ArgumentException($"Skia unable to interpret pfim format: {image.Format}");
}
-
// Converts the data into writeableBitmap. (TODO Insert a link to the code)
var imageInfo = new SKImageInfo(image.Width, image.Height, colorType);
var handle = GCHandle.Alloc(newData, GCHandleType.Pinned);
@@ -339,7 +347,7 @@ private static ImageProperties GetKtx2Properties(string ddsFilePath)
Extension = ".ktx2",
Height = texture.BaseHeight.ToString(),
Width = texture.BaseWidth.ToString(),
- CompressionType = texture.VkFormat.ToString(),
+ SurfaceFormat = texture.VkFormat.ToString(),
HasAlpha = KTX2_HELPER.HasAlpha(texture.VkFormat) ? "True" : "False",
//ChannelCount = Helper.GetDataFormatDescriptor(texture.VkFormat).DescriptorBlockSize.ToString(),
MipMapCount = texture.NumLevels.ToString()
@@ -351,7 +359,7 @@ public static ImageProperties GetImagePropertiesFromInvalid()
return new ImageProperties()
{
Name = "",
- CompressionType = "",
+ SurfaceFormat = "",
ChannelCount = "",
Height = "",
Width = "",
diff --git a/TelltaleTextureTool/TelltaleTextureTool/GUI/ImageProperties.cs b/TelltaleTextureTool/TelltaleTextureTool/GUI/ImageProperties.cs
index 3eaafbe..af1617b 100644
--- a/TelltaleTextureTool/TelltaleTextureTool/GUI/ImageProperties.cs
+++ b/TelltaleTextureTool/TelltaleTextureTool/GUI/ImageProperties.cs
@@ -11,7 +11,7 @@ public class ImageProperties : ObservableObject
public string? Extension { get; set; }
public string? Width { get; set; }
public string? Height { get; set; }
- public string? CompressionType { get; set; }
+ public string? SurfaceFormat { get; set; }
public string? HasAlpha { get; set; }
public string? BitsPerPixel { get; set; }
public string? ChannelCount { get; set; }
diff --git a/TelltaleTextureTool/TelltaleTextureTool/GUI/ViewModels/MainViewModel.cs b/TelltaleTextureTool/TelltaleTextureTool/GUI/ViewModels/MainViewModel.cs
index 68f26cf..0b9bf4f 100644
--- a/TelltaleTextureTool/TelltaleTextureTool/GUI/ViewModels/MainViewModel.cs
+++ b/TelltaleTextureTool/TelltaleTextureTool/GUI/ViewModels/MainViewModel.cs
@@ -85,6 +85,21 @@ public partial class MainViewModel : ViewModelBase
new FormatItemViewModel { Name = "Legacy Version 9", ItemStatus = true},
new FormatItemViewModel { Name = "Legacy Version 10", ItemStatus = true},
new FormatItemViewModel { Name = "Legacy Version 11", ItemStatus = true},
+ new FormatItemViewModel { Name = "Legacy Version 12", ItemStatus = true},
+ new FormatItemViewModel { Name = "Legacy Version 13", ItemStatus = true},
+ new FormatItemViewModel { Name = "Console Legacy Version 1", ItemStatus = true},
+ new FormatItemViewModel { Name = "Console Legacy Version 2", ItemStatus = true},
+ new FormatItemViewModel { Name = "Console Legacy Version 3", ItemStatus = true},
+ new FormatItemViewModel { Name = "Console Legacy Version 4", ItemStatus = true},
+ new FormatItemViewModel { Name = "Console Legacy Version 5", ItemStatus = true},
+ new FormatItemViewModel { Name = "Console Legacy Version 6", ItemStatus = true},
+ new FormatItemViewModel { Name = "Console Legacy Version 7", ItemStatus = true},
+ new FormatItemViewModel { Name = "Console Legacy Version 8", ItemStatus = true},
+ new FormatItemViewModel { Name = "Console Legacy Version 9", ItemStatus = true},
+ new FormatItemViewModel { Name = "Console Legacy Version 10", ItemStatus = true},
+ new FormatItemViewModel { Name = "Console Legacy Version 11", ItemStatus = true},
+ new FormatItemViewModel { Name = "Console Legacy Version 12", ItemStatus = true},
+ new FormatItemViewModel { Name = "Console Legacy Version 13", ItemStatus = true},
];
private readonly List _allTypes = [".png", ".jpg", ".jpeg", ".bmp", ".tif", ".tiff", ".d3dtx", ".dds", ".ktx", ".ktx2", ".tga"];
@@ -177,6 +192,7 @@ public MainViewModel()
#region MAIN MENU BUTTONS ACTIONS
// Open Directory Command
+ [RelayCommand]
public async Task OpenDirectoryButton_Click()
{
try
@@ -202,7 +218,8 @@ public async Task OpenDirectoryButton_Click()
}
}
- public async void SaveFileButton_Click()
+ [RelayCommand]
+ public async Task SaveFileButton_Click()
{
try
{
@@ -244,7 +261,8 @@ public async void SaveFileButton_Click()
}
}
- public async void AddFilesButton_Click()
+ [RelayCommand]
+ public async Task AddFilesButton_Click()
{
try
{
@@ -287,6 +305,7 @@ public async void AddFilesButton_Click()
}
// Delete Command
+ [RelayCommand]
public async Task DeleteFileButton_Click()
{
var workingDirectoryFile =
@@ -330,6 +349,7 @@ public async Task DeleteFileButton_Click()
}
}
+ [RelayCommand]
public void HelpButton_Click()
{
mainManager.OpenAppHelp();
@@ -352,7 +372,8 @@ public void AboutButton_Click()
#region CONTEXT MENU ACTIONS
- public async void ContextMenuAddFilesCommand()
+ [RelayCommand]
+ public async Task ContextMenuAddFilesCommand()
{
try
{
@@ -396,7 +417,8 @@ public async void ContextMenuAddFilesCommand()
await UpdateUiAsync();
}
- public async void ContextMenuOpenFileCommand()
+ [RelayCommand]
+ public async Task ContextMenuOpenFileCommand()
{
try
{
@@ -419,7 +441,8 @@ public async void ContextMenuOpenFileCommand()
}
}
- public async void ContextMenuOpenFolderCommand()
+ [RelayCommand]
+ public async Task ContextMenuOpenFolderCommand()
{
try
{
@@ -620,6 +643,21 @@ private D3DTXVersion GetD3DTXConversionType()
"Legacy Version 9" => D3DTXVersion.LV9,
"Legacy Version 10" => D3DTXVersion.LV10,
"Legacy Version 11" => D3DTXVersion.LV11,
+ "Legacy Version 12" => D3DTXVersion.LV12,
+ "Legacy Version 13" => D3DTXVersion.LV13,
+ "Console Legacy Version 1" => D3DTXVersion.CLV1,
+ "Console Legacy Version 2" => D3DTXVersion.CLV2,
+ "Console Legacy Version 3" => D3DTXVersion.CLV3,
+ "Console Legacy Version 4" => D3DTXVersion.CLV4,
+ "Console Legacy Version 5" => D3DTXVersion.CLV5,
+ "Console Legacy Version 6" => D3DTXVersion.CLV6,
+ "Console Legacy Version 7" => D3DTXVersion.CLV7,
+ "Console Legacy Version 8" => D3DTXVersion.CLV8,
+ "Console Legacy Version 9" => D3DTXVersion.CLV9,
+ "Console Legacy Version 10" => D3DTXVersion.CLV10,
+ "Console Legacy Version 11" => D3DTXVersion.CLV11,
+ "Console Legacy Version 12" => D3DTXVersion.CLV12,
+ "Console Legacy Version 13" => D3DTXVersion.CLV13,
_ => D3DTXVersion.DEFAULT
};
}
@@ -718,6 +756,7 @@ private async Task UpdateUiAsync()
#region SMALL MENU BUTTON ACTIONS
+ [RelayCommand]
public async Task ReturnDirectory_Click()
{
try
@@ -737,6 +776,7 @@ public async Task ReturnDirectory_Click()
}
}
+ [RelayCommand]
public async Task ContextMenuRefreshDirectoryCommand()
{
await RefreshDirectoryButton_Click();
diff --git a/TelltaleTextureTool/TelltaleTextureTool/GUI/Views/MainView.axaml b/TelltaleTextureTool/TelltaleTextureTool/GUI/Views/MainView.axaml
index 0d54c90..c089d1a 100644
--- a/TelltaleTextureTool/TelltaleTextureTool/GUI/Views/MainView.axaml
+++ b/TelltaleTextureTool/TelltaleTextureTool/GUI/Views/MainView.axaml
@@ -4,8 +4,8 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:TelltaleTextureTool.ViewModels"
mc:Ignorable="d"
- d:DesignWidth="1000"
- d:DesignHeight="550"
+ d:DesignWidth="3000"
+ d:DesignHeight="1000"
x:Class="TelltaleTextureTool.Views.MainView"
xmlns:i="clr-namespace:Avalonia.Xaml.Interactivity;assembly=Avalonia.Xaml.Interactivity"
xmlns:paz="using:Avalonia.Controls.PanAndZoom"
@@ -16,560 +16,571 @@
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ Width="28"
+ Height="28" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
\ No newline at end of file
diff --git a/TelltaleTextureTool/TelltaleTextureTool/GUI/Views/MainWindow.axaml b/TelltaleTextureTool/TelltaleTextureTool/GUI/Views/MainWindow.axaml
index 52fb396..56dfb16 100644
--- a/TelltaleTextureTool/TelltaleTextureTool/GUI/Views/MainWindow.axaml
+++ b/TelltaleTextureTool/TelltaleTextureTool/GUI/Views/MainWindow.axaml
@@ -4,13 +4,11 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:TelltaleTextureTool.Views"
mc:Ignorable="d"
- d:DesignWidth="1230"
- d:DesignHeight="700"
x:Class="TelltaleTextureTool.Views.MainWindow"
Icon="/Assets/main_icon.ico"
Title="Telltale Texture Tool"
Height="700"
- Width="1270"
+ Width="1570"
MinWidth="600"
MinHeight="250">
diff --git a/TelltaleTextureTool/TelltaleTextureTool/GUI/Views/MainWindow.axaml.cs b/TelltaleTextureTool/TelltaleTextureTool/GUI/Views/MainWindow.axaml.cs
index 08d52c7..2174d22 100644
--- a/TelltaleTextureTool/TelltaleTextureTool/GUI/Views/MainWindow.axaml.cs
+++ b/TelltaleTextureTool/TelltaleTextureTool/GUI/Views/MainWindow.axaml.cs
@@ -1,4 +1,5 @@
-using Avalonia.Controls;
+using Avalonia;
+using Avalonia.Controls;
namespace TelltaleTextureTool.Views;
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/DDS/DDS_DirectXTexNet.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/DDS/DDS_DirectXTexNet.cs
index f462802..81dedda 100644
--- a/TelltaleTextureTool/TelltaleTextureTool/Graphics/DDS/DDS_DirectXTexNet.cs
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/DDS/DDS_DirectXTexNet.cs
@@ -68,7 +68,7 @@ public static ImageProperties GetDDSProperties(string ddsFilePath, TexMetadata d
Extension = ".dds",
Height = ddsMetadata.Height.ToString(),
Width = ddsMetadata.Width.ToString(),
- CompressionType = dxgiFormat.ToString(),
+ SurfaceFormat = dxgiFormat.ToString(),
HasAlpha = hasAlpha,
ChannelCount = channelCount.ToString(),
MipMapCount = ddsMetadata.MipLevels.ToString()
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/DDS/DDS_HELPER.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/DDS/DDS_HELPER.cs
index 3df6ed7..c8923c7 100644
--- a/TelltaleTextureTool/TelltaleTextureTool/Graphics/DDS/DDS_HELPER.cs
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/DDS/DDS_HELPER.cs
@@ -254,7 +254,7 @@ public static DXGIFormat GetDXGIFormat(T3SurfaceFormat format, T3SurfaceGamma ga
_ => DXGIFormat.R8G8B8A8_UNORM, // Choose R8G8B8A8 if the format is not specified. (Raw data)
};
- if (platformType == T3PlatformType.ePlatform_iPhone)
+ if (platformType == T3PlatformType.ePlatform_iPhone || platformType == T3PlatformType.ePlatform_Android)
{
dxgiFormat = GetDXGIFormatWithSwappedChannels(dxgiFormat);
}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/BitHelper.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/BitHelper.cs
new file mode 100644
index 0000000..50f64f7
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/BitHelper.cs
@@ -0,0 +1,391 @@
+using System;
+using System.Runtime.CompilerServices;
+
+namespace LibWindPop.Utils
+{
+ ///
+ /// help to deal with bit operation
+ ///
+ internal static class BitHelper
+ {
+ ///
+ /// Scale 1 bit to 8 bits
+ ///
+ /// 1 bit value
+ /// 8 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int OneBitToEightBit(int value)
+ {
+ return (value & 0b1) == 0 ? 0 : 0xFF;
+ }
+
+ ///
+ /// Scale 8 bits to 1 bit
+ ///
+ /// 8 bits value
+ /// 1 bit value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int EightBitToOneBit(int value)
+ {
+ return (value & 0b10000000) >> 7;
+ }
+
+ ///
+ /// Scale 2 bits to 8 bits
+ ///
+ /// 2 bits value
+ /// 8 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int TwoBitToEightBit(int value)
+ {
+ value &= 0b11;
+ value = (value << 2) | value;
+ return (value << 4) | value;
+ }
+
+ ///
+ /// Scale 8 bits to 2 bits
+ ///
+ /// 8 bits value
+ /// 2 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int EightBitToTwoBit(int value)
+ {
+ return (value & 0b11000000) >> 6;
+ }
+
+ ///
+ /// Scale 3 bits to 8 bits
+ ///
+ /// 3 bits value
+ /// 8 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int ThreeBitToEightBit(int value)
+ {
+ value &= 0b111;
+ return (value << 5) | (value << 2) | (value >> 1);
+ }
+
+ ///
+ /// Scale 8 bits to 3 bits
+ ///
+ /// 8 bits value
+ /// 3 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int EightBitToThreeBit(int value)
+ {
+ return (value & 0b11100000) >> 5;
+ }
+
+ ///
+ /// Scale 4 bits to 8 bits
+ ///
+ /// 4 bits value
+ /// 8 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int FourBitToEightBit(int value)
+ {
+ value &= 0b1111;
+ return (value << 4) | value;
+ }
+
+ ///
+ /// Scale 8 bits to 4 bits
+ ///
+ /// 8 bits value
+ /// 4 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int EightBitToFourBit(int value)
+ {
+ return (value & 0b11110000) >> 4;
+ }
+
+ ///
+ /// Scale 5 bits to 8 bits
+ ///
+ /// 5 bits value
+ /// 8 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int FiveBitToEightBit(int value)
+ {
+ value &= 0b11111;
+ return (value << 3) | (value >> 2);
+ }
+ ///
+ /// Scale 8 bits to 5 bits
+ ///
+ /// 8 bits value
+ /// 5 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int EightBitToFiveBit(int value)
+ {
+ return (value & 0b11111000) >> 3;
+ }
+
+ ///
+ /// Scale 6 bits to 8 bits
+ ///
+ /// 6 bits value
+ /// 8 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int SixBitToEightBit(int value)
+ {
+ value &= 0b111111;
+ return (value << 2) | (value >> 4);
+ }
+
+ ///
+ /// Scale 8 bits to 6 bits
+ ///
+ /// 8 bits value
+ /// 6 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int EightBitToSixBit(int value)
+ {
+ return (value & 0b11111100) >> 2;
+ }
+
+ ///
+ /// Scale 7 bits to 8 bits
+ ///
+ /// 7 bits value
+ /// 8 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int SevenBitToEightBit(int value)
+ {
+ value &= 0b1111111;
+ return (value << 1) | (value >> 6);
+ }
+
+ ///
+ /// Scale 8 bits to 7 bits
+ ///
+ /// 8 bits value
+ /// 7 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int EightBitToSevenBit(int value)
+ {
+ return (value & 0b11111110) >> 1;
+ }
+
+ ///
+ /// Scale 8 bits to 8 bits
+ ///
+ /// 8 bits value
+ /// 8 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int EightBitToEightBit(int value)
+ {
+ return value & 0b11111111;
+ }
+
+ ///
+ /// Scale 1 bits to 8 bits
+ ///
+ /// 1 bits value
+ /// 8 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong OneBitToEightBit(ulong value)
+ {
+ return (value & 0b1) == 0 ? 0ul : 0xFFul;
+ }
+
+ ///
+ /// Scale 8 bits to 1 bits
+ ///
+ /// 8 bits value
+ /// 1 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong EightBitToOneBit(ulong value)
+ {
+ return (value & 0b10000000) >> 7;
+ }
+
+ ///
+ /// Scale 2 bits to 8 bits
+ ///
+ /// 2 bits value
+ /// 8 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong TwoBitToEightBit(ulong value)
+ {
+ value &= 0b11;
+ value = (value << 2) | value;
+ return (value << 4) | value;
+ }
+
+ ///
+ /// Scale 8 bits to 2 bits
+ ///
+ /// 8 bits value
+ /// 2 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong EightBitToTwoBit(ulong value)
+ {
+ return (value & 0b11000000) >> 6;
+ }
+
+ ///
+ /// Scale 3 bits to 8 bits
+ ///
+ /// 3 bits value
+ /// 8 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong ThreeBitToEightBit(ulong value)
+ {
+ value &= 0b111;
+ return (value << 5) | (value << 2) | (value >> 1);
+ }
+
+ ///
+ /// Scale 8 bits to 3 bits
+ ///
+ /// 8 bits value
+ /// 3 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong EightBitToThreeBit(ulong value)
+ {
+ return (value & 0b11100000) >> 5;
+ }
+
+ ///
+ /// Scale 4 bits to 8 bits
+ ///
+ /// 4 bits value
+ /// 8 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong FourBitToEightBit(ulong value)
+ {
+ value &= 0b1111;
+ return (value << 4) | value;
+ }
+
+ ///
+ /// Scale 8 bits to 4 bits
+ ///
+ /// 8 bits value
+ /// 4 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong EightBitToFourBit(ulong value)
+ {
+ return (value & 0b11110000) >> 4;
+ }
+
+ ///
+ /// Scale 5 bits to 8 bits
+ ///
+ /// 5 bits value
+ /// 8 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong FiveBitToEightBit(ulong value)
+ {
+ value &= 0b11111;
+ return (value << 3) | (value >> 2);
+ }
+
+ ///
+ /// Scale 8 bits to 5 bits
+ ///
+ /// 8 bits value
+ /// 5 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong EightBitToFiveBit(ulong value)
+ {
+ return (value & 0b11111000) >> 3;
+ }
+
+ ///
+ /// Scale 6 bits to 8 bits
+ ///
+ /// 6 bits value
+ /// 8 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong SixBitToEightBit(ulong value)
+ {
+ value &= 0b111111;
+ return (value << 2) | (value >> 4);
+ }
+
+ ///
+ /// Scale 8 bits to 6 bits
+ ///
+ /// 8 bits value
+ /// 6 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong EightBitToSixBit(ulong value)
+ {
+ return (value & 0b11111100) >> 2;
+ }
+
+ ///
+ /// Scale 7 bits to 8 bits
+ ///
+ /// 7 bits value
+ /// 8 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong SevenBitToEightBit(ulong value)
+ {
+ value &= 0b1111111;
+ return (value << 1) | (value >> 6);
+ }
+
+ ///
+ /// Scale 8 bits to 7 bits
+ ///
+ /// 8 bits value
+ /// 7 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong EightBitToSevenBit(ulong value)
+ {
+ return (value & 0b11111110) >> 1;
+ }
+
+ ///
+ /// Scale 8 bits to 8 bits
+ ///
+ /// 8 bits value
+ /// 8 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ulong EightBitToEightBit(ulong value)
+ {
+ return value & 0b11111111;
+ }
+
+ ///
+ /// Scale 8 bits to 8 bits
+ ///
+ /// 8 bits value
+ /// 8 bits value
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void Endian8In16(Span data)
+ {
+ int len = data.Length >> 1 << 1;
+ for (int i = 0; i < len; i += 2)
+ {
+ (data[i], data[i | 1]) = (data[i | 1], data[i]);
+ }
+ }
+
+ ///
+ /// check if the value is power of two
+ ///
+ /// checked value
+ /// if the value is power of two, return true; otherwise, return false
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsPowerOfTwo(int value)
+ {
+ return (value & (value - 1)) == 0;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int GetClosestPowerOfTwoAbove(int value)
+ {
+ int ans = 1;
+ while (ans < value)
+ {
+ ans <<= 1;
+ }
+ return ans;
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Bitmap/ArrayPoolBitmap.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Bitmap/ArrayPoolBitmap.cs
new file mode 100644
index 0000000..58e6bf6
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Bitmap/ArrayPoolBitmap.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Buffers;
+
+namespace LibWindPop.Utils.Graphics.Bitmap
+{
+ public class ArrayPoolBitmap : IDisposableBitmap
+ {
+ public int Width => width;
+ public int Height => height;
+ public int Area => area;
+
+ public Span AsSpan()
+ {
+ return data_ptr.AsSpan();
+ }
+
+ public RefBitmap AsRefBitmap()
+ {
+ return new RefBitmap(width, height, AsSpan());
+ }
+
+ private readonly int width;
+ private readonly int height;
+ private readonly int area;
+ private YFColor[] data_owner;
+ private ArraySegment data_ptr;
+ private bool disposedValue;
+
+ public ArrayPoolBitmap(int width, int height)
+ {
+ ThrowHelper.ThrowWhen(width < 0 || height < 0);
+ this.width = width;
+ this.height = height;
+ area = width * height;
+ data_owner = ArrayPool.Shared.Rent(area);
+ data_ptr = new ArraySegment(data_owner, 0, area);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!disposedValue)
+ {
+ ArrayPool.Shared.Return(data_owner);
+ disposedValue = true;
+ }
+ }
+
+ ~ArrayPoolBitmap()
+ {
+ Dispose(disposing: false);
+ }
+
+ public void Dispose()
+ {
+ Dispose(disposing: true);
+ GC.SuppressFinalize(this);
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Bitmap/ArraySegmentBitmap.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Bitmap/ArraySegmentBitmap.cs
new file mode 100644
index 0000000..ec49861
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Bitmap/ArraySegmentBitmap.cs
@@ -0,0 +1,44 @@
+using System;
+
+namespace LibWindPop.Utils.Graphics.Bitmap
+{
+ public struct ArraySegmentBitmap : IBitmap
+ {
+ public int Width => width;
+ public int Height => height;
+ public int Area => area;
+
+ public Span AsSpan()
+ {
+ return data_ptr.AsSpan();
+ }
+
+ public RefBitmap AsRefBitmap()
+ {
+ return new RefBitmap(width, height, AsSpan());
+ }
+
+ private readonly int width;
+ private readonly int height;
+ private readonly int area;
+ private ArraySegment data_ptr;
+
+ public ArraySegmentBitmap(int width, int height, YFColor[] data_ptr)
+ {
+ ThrowHelper.ThrowWhen(width < 0 || height < 0);
+ this.width = width;
+ this.height = height;
+ area = width * height;
+ this.data_ptr = new ArraySegment(data_ptr, 0, area);
+ }
+
+ public ArraySegmentBitmap(int width, int height, ArraySegment data_ptr)
+ {
+ ThrowHelper.ThrowWhen(width < 0 || height < 0);
+ this.width = width;
+ this.height = height;
+ area = width * height;
+ this.data_ptr = data_ptr[..area];
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Bitmap/IBitmap.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Bitmap/IBitmap.cs
new file mode 100644
index 0000000..3c543b5
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Bitmap/IBitmap.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace LibWindPop.Utils.Graphics.Bitmap
+{
+ public interface IBitmap
+ {
+ int Width { get; }
+ int Height { get; }
+ int Area { get; }
+
+ Span AsSpan();
+
+ RefBitmap AsRefBitmap();
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Bitmap/IDisposableBitmap.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Bitmap/IDisposableBitmap.cs
new file mode 100644
index 0000000..02afdb1
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Bitmap/IDisposableBitmap.cs
@@ -0,0 +1,8 @@
+using System;
+
+namespace LibWindPop.Utils.Graphics.Bitmap
+{
+ public interface IDisposableBitmap : IBitmap, IDisposable
+ {
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Bitmap/MemoryBitmap.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Bitmap/MemoryBitmap.cs
new file mode 100644
index 0000000..23a5cb9
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Bitmap/MemoryBitmap.cs
@@ -0,0 +1,35 @@
+using System;
+
+namespace LibWindPop.Utils.Graphics.Bitmap
+{
+ public struct MemoryBitmap : IBitmap
+ {
+ public int Width => width;
+ public int Height => height;
+ public int Area => area;
+
+ public Span AsSpan()
+ {
+ return data_ptr.Span;
+ }
+
+ public RefBitmap AsRefBitmap()
+ {
+ return new RefBitmap(width, height, AsSpan());
+ }
+
+ private readonly int width;
+ private readonly int height;
+ private readonly int area;
+ private Memory data_ptr;
+
+ public MemoryBitmap(int width, int height, Memory data_ptr)
+ {
+
+ this.width = width;
+ this.height = height;
+ area = width * height;
+ this.data_ptr = data_ptr[..area];
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Bitmap/MemoryPoolBitmap.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Bitmap/MemoryPoolBitmap.cs
new file mode 100644
index 0000000..5118c78
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Bitmap/MemoryPoolBitmap.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Buffers;
+
+namespace LibWindPop.Utils.Graphics.Bitmap
+{
+ public class MemoryPoolBitmap : IDisposableBitmap
+ {
+ public int Width => width;
+ public int Height => height;
+ public int Area => area;
+
+ public Span AsSpan()
+ {
+ return data_ptr.Span;
+ }
+
+ public RefBitmap AsRefBitmap()
+ {
+ return new RefBitmap(width, height, AsSpan());
+ }
+
+ private readonly int width;
+ private readonly int height;
+ private readonly int area;
+ private IMemoryOwner data_owner;
+ private Memory data_ptr;
+
+ public MemoryPoolBitmap(int width, int height)
+ {
+
+ this.width = width;
+ this.height = height;
+ area = width * height;
+ data_owner = MemoryPool.Shared.Rent(area);
+ data_ptr = data_owner.Memory[..area];
+ }
+
+ public void Dispose()
+ {
+ data_owner.Dispose();
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Bitmap/NativeBitmap.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Bitmap/NativeBitmap.cs
new file mode 100644
index 0000000..006a319
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Bitmap/NativeBitmap.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace LibWindPop.Utils.Graphics.Bitmap
+{
+ public unsafe class NativeBitmap : IDisposableBitmap
+ {
+ public int Width => width;
+ public int Height => height;
+ public int Area => area;
+
+ public Span AsSpan()
+ {
+ return new Span(data_ptr, area);
+ }
+
+ public RefBitmap AsRefBitmap()
+ {
+ return new RefBitmap(width, height, AsSpan());
+ }
+
+ private readonly int width;
+ private readonly int height;
+ private readonly int area;
+ private YFColor* data_ptr;
+ private bool disposedValue;
+
+ public NativeBitmap(int width, int height)
+ {
+ this.width = width;
+ this.height = height;
+ area = width * height;
+ data_ptr = (YFColor*)NativeMemory.Alloc((nuint)(area * sizeof(YFColor)));
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!disposedValue)
+ {
+ NativeMemory.Free(data_ptr);
+ data_ptr = null;
+ disposedValue = true;
+ }
+ }
+
+ ~NativeBitmap()
+ {
+ Dispose(disposing: false);
+ }
+
+ public void Dispose()
+ {
+ Dispose(disposing: true);
+ GC.SuppressFinalize(this);
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Bitmap/RefBitmap.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Bitmap/RefBitmap.cs
new file mode 100644
index 0000000..af23570
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Bitmap/RefBitmap.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Runtime.CompilerServices;
+
+namespace LibWindPop.Utils.Graphics.Bitmap
+{
+ public readonly ref struct RefBitmap
+ {
+ public readonly int Width;
+ public readonly int Height;
+ public readonly int Area;
+ public readonly Span Data;
+
+ public readonly Span this[int y]
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return Data.Slice(y * Width, Width);
+ }
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set
+ {
+ int data_len = Math.Min(Width, value.Length);
+ value[..data_len].CopyTo(Data.Slice(y * Width, data_len));
+ }
+ }
+
+ public readonly ref YFColor this[int x, int y]
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return ref Data[y * Width + x];
+ }
+ }
+
+ internal RefBitmap(int width, int height, Span data)
+ {
+ Width = width;
+ Height = height;
+ Area = width * height;
+ Data = data;
+ }
+
+ public readonly Span AsSpan()
+ {
+ return Data;
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/L8_UByte.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/L8_UByte.cs
new file mode 100644
index 0000000..b6b9a80
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/L8_UByte.cs
@@ -0,0 +1,60 @@
+using LibWindPop.Utils.Graphics.Bitmap;
+using LibWindPop.Utils.Graphics.Texture.Shared;
+using System;
+
+namespace LibWindPop.Utils.Graphics.Texture.Coder
+{
+ public readonly unsafe struct L8_UByte : ITextureCoder, IPitchableTextureCoder
+ {
+ public readonly void Decode(ReadOnlySpan srcData, int width, int height, RefBitmap dstBitmap)
+ {
+ Decode(srcData, width, height, width, dstBitmap);
+ }
+
+ public readonly void Encode(RefBitmap srcBitmap, Span dstData, int width, int height)
+ {
+ Encode(srcBitmap, dstData, width, height, width);
+ }
+
+ public readonly void Decode(ReadOnlySpan srcData, int width, int height, int pitch, RefBitmap dstBitmap)
+ {
+ ThrowHelper.ThrowWhen(width < 0 || height < 0 || pitch < 0);
+ int tempDataIndex;
+ YFColor tempColor;
+ Span row;
+ int srcDataIndex = 0;
+ for (int y = 0; y < height; y++)
+ {
+ tempDataIndex = srcDataIndex;
+ srcDataIndex += pitch;
+ row = dstBitmap[y];
+ for (int x = 0; x < width; x++)
+ {
+ (tempColor.Red, tempColor.Green, tempColor.Blue) = ChannelHelper.GetRGBFromL(srcData[tempDataIndex++]);
+ tempColor.Alpha = 0xFF;
+ row[x] = tempColor;
+ }
+ }
+ }
+
+ public readonly void Encode(RefBitmap srcBitmap, Span dstData, int width, int height, int pitch)
+ {
+ ThrowHelper.ThrowWhen(width < 0 || height < 0 || pitch < 0);
+ int tempDataIndex;
+ YFColor tempColor;
+ Span row;
+ int dstDataIndex = 0;
+ for (int y = 0; y < height; y++)
+ {
+ tempDataIndex = dstDataIndex;
+ dstDataIndex += pitch;
+ row = srcBitmap[y];
+ for (int x = 0; x < width; x++)
+ {
+ tempColor = row[x];
+ dstData[tempDataIndex++] = ChannelHelper.GetLFromRGB(tempColor.Red, tempColor.Green, tempColor.Blue);
+ }
+ }
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_ATC_Explicit_UByte.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_ATC_Explicit_UByte.cs
new file mode 100644
index 0000000..41559d2
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_ATC_Explicit_UByte.cs
@@ -0,0 +1,80 @@
+using LibWindPop.Utils.Graphics.Bitmap;
+using LibWindPop.Utils.Graphics.Texture.IGraphicsAPITexture.OpenGLES;
+using LibWindPop.Utils.Graphics.Texture.Shared;
+using System;
+
+namespace LibWindPop.Utils.Graphics.Texture.Coder
+{
+ public readonly unsafe struct RGBA_ATC_Explicit_UByte : ITextureCoder, IPitchableTextureCoder, IOpenGLES20CompressedTexture
+ {
+ public static int OpenGLES20InternalFormat => 0x8C93; // GL_ATC_RGBA_EXPLICIT_ALPHA_AMD
+
+ public readonly void Decode(ReadOnlySpan srcData, int width, int height, RefBitmap dstBitmap)
+ {
+ Decode(srcData, width, height, (width + 3) / 4 * 16, dstBitmap);
+ }
+
+ public readonly void Encode(RefBitmap srcBitmap, Span dstData, int width, int height)
+ {
+ Encode(srcBitmap, dstData, width, height, (width + 3) / 4 * 16);
+ }
+
+ public readonly void Decode(ReadOnlySpan srcData, int width, int height, int pitch, RefBitmap dstBitmap)
+ {
+ ThrowHelper.ThrowWhen(width < 0 || height < 0);
+ int tempDataIndex;
+ Span decodeBlockData = stackalloc YFColor[16];
+ int srcDataIndex = 0;
+ for (int yTile = 0; yTile < height; yTile += 4)
+ {
+ tempDataIndex = srcDataIndex;
+ srcDataIndex += pitch;
+ for (int xTile = 0; xTile < width; xTile += 4)
+ {
+ ATCCoder.DecodeATCBlockWithExplicitAlpha(srcData.Slice(tempDataIndex, 16), decodeBlockData);
+ tempDataIndex += 16;
+ for (int y = 0; y < 4; y++)
+ {
+ if ((yTile + y) >= height)
+ {
+ break;
+ }
+ for (int x = 0; x < 4; x++)
+ {
+ if ((xTile + x) >= width)
+ {
+ break;
+ }
+ dstBitmap[xTile | x, yTile | y] = decodeBlockData[(y << 2) | x];
+ }
+ }
+ }
+ }
+ }
+
+ public readonly void Encode(RefBitmap srcBitmap, Span dstData, int width, int height, int pitch)
+ {
+ ThrowHelper.ThrowWhen(width < 0 || height < 0);
+ int tempDataIndex;
+ Span encodeBlockData = stackalloc YFColor[16];
+ int srcDataIndex = 0;
+ for (int yTile = 0; yTile < height; yTile += 4)
+ {
+ tempDataIndex = srcDataIndex;
+ srcDataIndex += pitch;
+ for (int xTile = 0; xTile < width; xTile += 4)
+ {
+ for (int y = 0; y < 4; y++)
+ {
+ for (int x = 0; x < 4; x++)
+ {
+ encodeBlockData[(y << 2) | x] = ((yTile + y) >= height || (xTile + x) >= width) ? YFColor.Transparent : srcBitmap[xTile | x, yTile | y];
+ }
+ }
+ ATCCoder.EncodeATCBlockWithExplicitAlpha(dstData.Slice(tempDataIndex, 16), encodeBlockData);
+ tempDataIndex += 16;
+ }
+ }
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_ATC_Interpolated_UByte.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_ATC_Interpolated_UByte.cs
new file mode 100644
index 0000000..469d1b1
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_ATC_Interpolated_UByte.cs
@@ -0,0 +1,80 @@
+using LibWindPop.Utils.Graphics.Bitmap;
+using LibWindPop.Utils.Graphics.Texture.IGraphicsAPITexture.OpenGLES;
+using LibWindPop.Utils.Graphics.Texture.Shared;
+using System;
+
+namespace LibWindPop.Utils.Graphics.Texture.Coder
+{
+ public readonly unsafe struct RGBA_ATC_Interpolated_UByte : ITextureCoder, IPitchableTextureCoder, IOpenGLES20CompressedTexture
+ {
+ public static int OpenGLES20InternalFormat => 0x87EE; // GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD
+
+ public readonly void Decode(ReadOnlySpan srcData, int width, int height, RefBitmap dstBitmap)
+ {
+ Decode(srcData, width, height, (width + 3) / 4 * 16, dstBitmap);
+ }
+
+ public readonly void Encode(RefBitmap srcBitmap, Span dstData, int width, int height)
+ {
+ Encode(srcBitmap, dstData, width, height, (width + 3) / 4 * 16);
+ }
+
+ public readonly void Decode(ReadOnlySpan srcData, int width, int height, int pitch, RefBitmap dstBitmap)
+ {
+ ThrowHelper.ThrowWhen(width < 0 || height < 0);
+ int tempDataIndex;
+ Span decodeBlockData = stackalloc YFColor[16];
+ int srcDataIndex = 0;
+ for (int yTile = 0; yTile < height; yTile += 4)
+ {
+ tempDataIndex = srcDataIndex;
+ srcDataIndex += pitch;
+ for (int xTile = 0; xTile < width; xTile += 4)
+ {
+ ATCCoder.DecodeATCBlockWithInterpolatedAlpha(srcData.Slice(tempDataIndex, 16), decodeBlockData);
+ tempDataIndex += 16;
+ for (int y = 0; y < 4; y++)
+ {
+ if ((yTile + y) >= height)
+ {
+ break;
+ }
+ for (int x = 0; x < 4; x++)
+ {
+ if ((xTile + x) >= width)
+ {
+ break;
+ }
+ dstBitmap[xTile | x, yTile | y] = decodeBlockData[(y << 2) | x];
+ }
+ }
+ }
+ }
+ }
+
+ public readonly void Encode(RefBitmap srcBitmap, Span dstData, int width, int height, int pitch)
+ {
+ ThrowHelper.ThrowWhen(width < 0 || height < 0);
+ int tempDataIndex;
+ Span encodeBlockData = stackalloc YFColor[16];
+ int srcDataIndex = 0;
+ for (int yTile = 0; yTile < height; yTile += 4)
+ {
+ tempDataIndex = srcDataIndex;
+ srcDataIndex += pitch;
+ for (int xTile = 0; xTile < width; xTile += 4)
+ {
+ for (int y = 0; y < 4; y++)
+ {
+ for (int x = 0; x < 4; x++)
+ {
+ encodeBlockData[(y << 2) | x] = ((yTile + y) >= height || (xTile + x) >= width) ? YFColor.Transparent : srcBitmap[xTile | x, yTile | y];
+ }
+ }
+ ATCCoder.EncodeATCBlockWithInterpolatedAlpha(dstData.Slice(tempDataIndex, 16), encodeBlockData);
+ tempDataIndex += 16;
+ }
+ }
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_BC1_PS4_UByte.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_BC1_PS4_UByte.cs
new file mode 100644
index 0000000..482658d
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_BC1_PS4_UByte.cs
@@ -0,0 +1,102 @@
+using LibWindPop.Utils.Graphics.Bitmap;
+using LibWindPop.Utils.Graphics.Texture.Shared;
+using System;
+
+namespace LibWindPop.Utils.Graphics.Texture.Coder
+{
+ public readonly unsafe struct RGBA_BC1_PS4_UByte : ITextureCoder
+ {
+ public readonly void Decode(ReadOnlySpan srcData, int width, int height, RefBitmap dstBitmap)
+ {
+ ThrowHelper.ThrowWhen(width < 0 || height < 0);
+ Span decodeBlockData = stackalloc YFColor[16];
+ const int dataSizePerTexel = 8;
+ int texelWidth = (width + 3) / 4;
+ int texelHeight = (height + 3) / 4;
+ (int alignWidth, int alignHeight) = PS4TextureHelper.GetAlignSizeFromTexelSize(texelWidth, texelHeight);
+ int microTileWidth = Math.Min(8, texelWidth);
+ int microTileHeight = Math.Min(8, texelHeight);
+ for (int yMacroTile = 0; yMacroTile < alignHeight; yMacroTile += 8)
+ {
+ for (int xMacroTile = 0; xMacroTile < alignWidth; xMacroTile += 8)
+ {
+ int baseTexelIndex = yMacroTile * alignWidth + xMacroTile * microTileHeight;
+ for (int yMicroTile = 0; yMicroTile < microTileHeight; yMicroTile++)
+ {
+ for (int xMicroTile = 0; xMicroTile < microTileWidth; xMicroTile++)
+ {
+ int texelIndex = baseTexelIndex + PS4TextureHelper.GetMicroTexelIndexFromPosition(xMicroTile, yMicroTile);
+ int dataIndex = texelIndex * dataSizePerTexel;
+ BCCoder.DecodeBC1BlockWithAlpha(srcData.Slice(dataIndex, dataSizePerTexel), decodeBlockData);
+ int texelY = yMacroTile + yMicroTile;
+ int texelX = xMacroTile + xMicroTile;
+ for (int y = 0; y < 4; y++)
+ {
+ int textureY = (texelY << 2) | y;
+ if (textureY < height)
+ {
+ Span rowData = dstBitmap[textureY];
+ for (int x = 0; x < 4; x++)
+ {
+ int textureX = (texelX << 2) | x;
+ if (textureX < width)
+ {
+ rowData[textureX] = decodeBlockData[(y << 2) | x];
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public readonly void Encode(RefBitmap srcBitmap, Span dstData, int width, int height)
+ {
+ ThrowHelper.ThrowWhen(width < 0 || height < 0);
+ Span encodeBlockData = stackalloc YFColor[16];
+ const int dataSizePerTexel = 8;
+ int texelWidth = (width + 3) / 4;
+ int texelHeight = (height + 3) / 4;
+ (int alignWidth, int alignHeight) = PS4TextureHelper.GetAlignSizeFromTexelSize(texelWidth, texelHeight);
+ int microTileWidth = Math.Min(8, texelWidth);
+ int microTileHeight = Math.Min(8, texelHeight);
+ for (int yMacroTile = 0; yMacroTile < alignHeight; yMacroTile += 8)
+ {
+ for (int xMacroTile = 0; xMacroTile < alignWidth; xMacroTile += 8)
+ {
+ int baseTexelIndex = yMacroTile * alignWidth + xMacroTile * microTileHeight;
+ for (int yMicroTile = 0; yMicroTile < microTileHeight; yMicroTile++)
+ {
+ for (int xMicroTile = 0; xMicroTile < microTileWidth; xMicroTile++)
+ {
+ encodeBlockData.Fill(YFColor.Transparent);
+ int texelY = yMacroTile + yMicroTile;
+ int texelX = xMacroTile + xMicroTile;
+ for (int y = 0; y < 4; y++)
+ {
+ int textureY = (texelY << 2) | y;
+ if (textureY < height)
+ {
+ Span rowData = srcBitmap[textureY];
+ for (int x = 0; x < 4; x++)
+ {
+ int textureX = (texelX << 2) | x;
+ if (textureX < width)
+ {
+ encodeBlockData[(y << 2) | x] = rowData[textureX];
+ }
+ }
+ }
+ }
+ int texelIndex = baseTexelIndex + PS4TextureHelper.GetMicroTexelIndexFromPosition(xMicroTile, yMicroTile);
+ int dataIndex = texelIndex * dataSizePerTexel;
+ BCCoder.EncodeBC1BlockWithAlpha(dstData.Slice(dataIndex, dataSizePerTexel), encodeBlockData);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_ETC2_EAC_UByte.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_ETC2_EAC_UByte.cs
new file mode 100644
index 0000000..0373e05
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_ETC2_EAC_UByte.cs
@@ -0,0 +1,69 @@
+using LibWindPop.Utils.Graphics.Bitmap;
+using LibWindPop.Utils.Graphics.Texture.Shared;
+using System;
+
+namespace LibWindPop.Utils.Graphics.Texture.Coder
+{
+ public readonly unsafe struct RGBA_ETC2_EAC_UByte : ITextureCoder
+ {
+ public readonly void Decode(ReadOnlySpan srcData, int width, int height, RefBitmap dstBitmap)
+ {
+ // if (PVRTexLibHelper.Decode(srcData, PVRTexLib.PVRTexLibPixelFormat.ETC2_RGBA, dstBitmap))
+ // {
+ // return;
+ // }
+ ThrowHelper.ThrowWhen(width < 0 || height < 0);
+ int tempDataIndex = 0;
+ Span decodeBlockData = stackalloc YFColor[16];
+ for (int yTile = 0; yTile < height; yTile += 4)
+ {
+ for (int xTile = 0; xTile < width; xTile += 4)
+ {
+ ETCCoder.DecodeR8G8B8A8ETC2EACBlock(srcData.Slice(tempDataIndex, 16), decodeBlockData);
+ tempDataIndex += 16;
+ for (int y = 0; y < 4; y++)
+ {
+ if ((yTile + y) >= height)
+ {
+ break;
+ }
+ for (int x = 0; x < 4; x++)
+ {
+ if ((xTile + x) >= width)
+ {
+ break;
+ }
+ dstBitmap[xTile | x, yTile | y] = decodeBlockData[(y << 2) | x];
+ }
+ }
+ }
+ }
+ }
+
+ public readonly void Encode(RefBitmap srcBitmap, Span dstData, int width, int height)
+ {
+ // if (PVRTexLibHelper.Encode(srcBitmap, PVRTexLib.PVRTexLibPixelFormat.ETC2_RGBA, dstData))
+ // {
+ // return;
+ // }
+ ThrowHelper.ThrowWhen(width < 0 || height < 0);
+ int tempDataIndex = 0;
+ Span encodeBlockData = stackalloc YFColor[16];
+ for (int yTile = 0; yTile < height; yTile += 4)
+ {
+ for (int xTile = 0; xTile < width; xTile += 4)
+ {
+ for (int y = 0; y < 4; y++)
+ {
+ for (int x = 0; x < 4; x++)
+ {
+ encodeBlockData[(y << 2) | x] = ((yTile + y) >= height || (xTile + x) >= width) ? YFColor.Transparent : srcBitmap[xTile | x, yTile | y];
+ }
+ }
+ ETCCoder.EncodeR8G8B8A8ETC2EACBlock(dstData.Slice(tempDataIndex, 16), encodeBlockData);
+ tempDataIndex += 16;
+ }
+ }
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_ETC2_Punchthrough_UByte.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_ETC2_Punchthrough_UByte.cs
new file mode 100644
index 0000000..fe44ffc
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_ETC2_Punchthrough_UByte.cs
@@ -0,0 +1,69 @@
+using LibWindPop.Utils.Graphics.Bitmap;
+using LibWindPop.Utils.Graphics.Texture.Shared;
+using System;
+
+namespace LibWindPop.Utils.Graphics.Texture.Coder
+{
+ public readonly unsafe struct RGBA_ETC2_Punchthrough_UByte : ITextureCoder
+ {
+ public readonly void Decode(ReadOnlySpan srcData, int width, int height, RefBitmap dstBitmap)
+ {
+ // if (PVRTexLibHelper.Decode(srcData, PVRTexLib.PVRTexLibPixelFormat.ETC2_RGB_A1, dstBitmap))
+ // {
+ // return;
+ // }
+ ThrowHelper.ThrowWhen(width < 0 || height < 0);
+ int tempDataIndex = 0;
+ Span decodeBlockData = stackalloc YFColor[16];
+ for (int yTile = 0; yTile < height; yTile += 4)
+ {
+ for (int xTile = 0; xTile < width; xTile += 4)
+ {
+ ETCCoder.DecodeR8G8B8A1ETC2Block(srcData.Slice(tempDataIndex, 8), decodeBlockData);
+ tempDataIndex += 8;
+ for (int y = 0; y < 4; y++)
+ {
+ if ((yTile + y) >= height)
+ {
+ break;
+ }
+ for (int x = 0; x < 4; x++)
+ {
+ if ((xTile + x) >= width)
+ {
+ break;
+ }
+ dstBitmap[xTile | x, yTile | y] = decodeBlockData[(y << 2) | x];
+ }
+ }
+ }
+ }
+ }
+
+ public readonly void Encode(RefBitmap srcBitmap, Span dstData, int width, int height)
+ {
+ // if (PVRTexLibHelper.Encode(srcBitmap, PVRTexLib.PVRTexLibPixelFormat.ETC2_RGB_A1, dstData))
+ // {
+ // return;
+ // }
+ ThrowHelper.ThrowWhen(width < 0 || height < 0);
+ int tempDataIndex = 0;
+ Span encodeBlockData = stackalloc YFColor[16];
+ for (int yTile = 0; yTile < height; yTile += 4)
+ {
+ for (int xTile = 0; xTile < width; xTile += 4)
+ {
+ for (int y = 0; y < 4; y++)
+ {
+ for (int x = 0; x < 4; x++)
+ {
+ encodeBlockData[(y << 2) | x] = ((yTile + y) >= height || (xTile + x) >= width) ? YFColor.Transparent : srcBitmap[xTile | x, yTile | y];
+ }
+ }
+ ETCCoder.EncodeR8G8B8A1ETC2Block(dstData.Slice(tempDataIndex, 8), encodeBlockData);
+ tempDataIndex += 8;
+ }
+ }
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_PVRTCII_2BPP_UByte.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_PVRTCII_2BPP_UByte.cs
new file mode 100644
index 0000000..f69fc43
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_PVRTCII_2BPP_UByte.cs
@@ -0,0 +1,44 @@
+using LibWindPop.Utils.Graphics.Bitmap;
+using LibWindPop.Utils.Graphics.Texture.IGraphicsAPITexture.OpenGLES;
+using LibWindPop.Utils.Graphics.Texture.Shared;
+using System;
+
+namespace LibWindPop.Utils.Graphics.Texture.Coder
+{
+ public readonly unsafe struct RGBA_PVRTCII_2BPP_UByte : ITextureCoder, IOpenGLES20CompressedTexture
+ {
+ public static int OpenGLES20InternalFormat => 0x9137; // GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG
+
+ public readonly void Decode(ReadOnlySpan srcData, int width, int height, RefBitmap dstBitmap)
+ {
+ // if (PVRTexLibHelper.Decode(srcData, PVRTexLib.PVRTexLibPixelFormat.PVRTCII_2bpp, dstBitmap))
+ // {
+ // return;
+ // }
+ ThrowHelper.ThrowWhen(width < 0 || height < 0 || !BitHelper.IsPowerOfTwo(width) || !BitHelper.IsPowerOfTwo(height));
+ fixed (YFColor* color = dstBitmap.Data)
+ {
+ fixed (byte* data = srcData)
+ {
+ PVRTCCoder.DecodeTexture_RGBA_PVRTCII_2BPP(data, color, (uint)width, (uint)height);
+ }
+ }
+ }
+
+ public readonly void Encode(RefBitmap srcBitmap, Span dstData, int width, int height)
+ {
+ // if (PVRTexLibHelper.Encode(srcBitmap, PVRTexLib.PVRTexLibPixelFormat.PVRTCII_2bpp, dstData))
+ // {
+ // return;
+ // }
+ ThrowHelper.ThrowWhen(width < 0 || height < 0 || !BitHelper.IsPowerOfTwo(width) || !BitHelper.IsPowerOfTwo(height));
+ fixed (YFColor* color = srcBitmap.Data)
+ {
+ fixed (byte* data = dstData)
+ {
+ PVRTCCoder.EncodeTexture_RGBA_PVRTCII_2BPP(data, color, (uint)width, (uint)height);
+ }
+ }
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_PVRTCII_4BPP_UByte.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_PVRTCII_4BPP_UByte.cs
new file mode 100644
index 0000000..54293b4
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_PVRTCII_4BPP_UByte.cs
@@ -0,0 +1,36 @@
+using LibWindPop.Utils.Graphics.Bitmap;
+using LibWindPop.Utils.Graphics.Texture.IGraphicsAPITexture.OpenGLES;
+using LibWindPop.Utils.Graphics.Texture.Shared;
+using System;
+
+namespace LibWindPop.Utils.Graphics.Texture.Coder
+{
+ public readonly unsafe struct RGBA_PVRTCII_4BPP_UByte : ITextureCoder, IOpenGLES20CompressedTexture
+ {
+ public static int OpenGLES20InternalFormat => 0x9138; // GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG
+
+ public readonly void Decode(ReadOnlySpan srcData, int width, int height, RefBitmap dstBitmap)
+ {
+ ThrowHelper.ThrowWhen(width < 0 || height < 0 || !BitHelper.IsPowerOfTwo(width) || !BitHelper.IsPowerOfTwo(height));
+ fixed (YFColor* color = dstBitmap.Data)
+ {
+ fixed (byte* data = srcData)
+ {
+ PVRTCCoder.DecodeTexture_RGBA_PVRTCII_4BPP(data, color, (uint)width, (uint)height);
+ }
+ }
+ }
+
+ public readonly void Encode(RefBitmap srcBitmap, Span dstData, int width, int height)
+ {
+ ThrowHelper.ThrowWhen(width < 0 || height < 0 || !BitHelper.IsPowerOfTwo(width) || !BitHelper.IsPowerOfTwo(height));
+ fixed (YFColor* color = srcBitmap.Data)
+ {
+ fixed (byte* data = dstData)
+ {
+ PVRTCCoder.EncodeTexture_RGBA_PVRTCII_4BPP(data, color, (uint)width, (uint)height);
+ }
+ }
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_PVRTCI_2BPP_UByte.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_PVRTCI_2BPP_UByte.cs
new file mode 100644
index 0000000..cd6c372
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_PVRTCI_2BPP_UByte.cs
@@ -0,0 +1,45 @@
+using LibWindPop.Utils.Graphics.Bitmap;
+using LibWindPop.Utils.Graphics.Texture.IGraphicsAPITexture.OpenGLES;
+using LibWindPop.Utils.Graphics.Texture.Shared;
+using System;
+
+namespace LibWindPop.Utils.Graphics.Texture.Coder
+{
+ public readonly unsafe struct RGBA_PVRTCI_2BPP_UByte : ITextureCoder, IOpenGLES20CompressedTexture
+ {
+ public static int OpenGLES20InternalFormat => 0x8C03; // GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG
+
+ public readonly void Decode(ReadOnlySpan srcData, int width, int height, RefBitmap dstBitmap)
+ {
+ // if (PVRTexLibHelper.Decode(srcData, PVRTexLib.PVRTexLibPixelFormat.PVRTCI_2bpp_RGBA, dstBitmap))
+ // {
+ // return;
+ // }
+ ThrowHelper.ThrowWhen(width < 0 || height < 0 || !BitHelper.IsPowerOfTwo(width) || !BitHelper.IsPowerOfTwo(height));
+ fixed (YFColor* color = dstBitmap.Data)
+ {
+ fixed (byte* data = srcData)
+ {
+ PVRTCCoder.DecodeTexture_RGBA_PVRTCI_2BPP(data, color, (uint)width, (uint)height);
+
+ }
+ }
+ }
+
+ public readonly void Encode(RefBitmap srcBitmap, Span dstData, int width, int height)
+ {
+ // if (PVRTexLibHelper.Encode(srcBitmap, PVRTexLib.PVRTexLibPixelFormat.PVRTCI_2bpp_RGBA, dstData))
+ // {
+ // return;
+ // }
+ ThrowHelper.ThrowWhen(width < 0 || height < 0 || !BitHelper.IsPowerOfTwo(width) || !BitHelper.IsPowerOfTwo(height));
+ fixed (YFColor* color = srcBitmap.Data)
+ {
+ fixed (byte* data = dstData)
+ {
+ PVRTCCoder.EncodeTexture_RGBA_PVRTCI_2BPP(data, color, (uint)width, (uint)height);
+ }
+ }
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_PVRTCI_4BPP_UByte.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_PVRTCI_4BPP_UByte.cs
new file mode 100644
index 0000000..0dd5e7c
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGBA_PVRTCI_4BPP_UByte.cs
@@ -0,0 +1,45 @@
+using LibWindPop.Utils.Graphics.Bitmap;
+using LibWindPop.Utils.Graphics.Texture.IGraphicsAPITexture.OpenGLES;
+using LibWindPop.Utils.Graphics.Texture.Shared;
+using System;
+
+namespace LibWindPop.Utils.Graphics.Texture.Coder
+{
+ public readonly unsafe struct RGBA_PVRTCI_4BPP_UByte : ITextureCoder, IOpenGLES20CompressedTexture
+ {
+ public static int OpenGLES20InternalFormat => 0x8C02; // GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG
+
+ public readonly void Decode(ReadOnlySpan srcData, int width, int height, RefBitmap dstBitmap)
+ {
+ // if (PVRTexLibHelper.Decode(srcData, PVRTexLib.PVRTexLibPixelFormat.PVRTCI_4bpp_RGBA, dstBitmap))
+ // {
+ // return;
+ // }
+ ThrowHelper.ThrowWhen(width < 0 || height < 0 || !BitHelper.IsPowerOfTwo(width) || !BitHelper.IsPowerOfTwo(height));
+ fixed (YFColor* color = dstBitmap.Data)
+ {
+ fixed (byte* data = srcData)
+ {
+ PVRTCCoder.DecodeTexture_RGBA_PVRTCI_4BPP(data, color, (uint)width, (uint)height);
+
+ }
+ }
+ }
+
+ public readonly void Encode(RefBitmap srcBitmap, Span dstData, int width, int height)
+ {
+ // if (PVRTexLibHelper.Encode(srcBitmap, PVRTexLib.PVRTexLibPixelFormat.PVRTCI_4bpp_RGBA, dstData))
+ // {
+ // return;
+ // }
+ ThrowHelper.ThrowWhen(width < 0 || height < 0 || !BitHelper.IsPowerOfTwo(width) || !BitHelper.IsPowerOfTwo(height));
+ fixed (YFColor* color = srcBitmap.Data)
+ {
+ fixed (byte* data = dstData)
+ {
+ PVRTCCoder.EncodeTexture_RGBA_PVRTCI_4BPP(data, color, (uint)width, (uint)height);
+ }
+ }
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGB_ATC_UByte.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGB_ATC_UByte.cs
new file mode 100644
index 0000000..ea7824b
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGB_ATC_UByte.cs
@@ -0,0 +1,80 @@
+using LibWindPop.Utils.Graphics.Bitmap;
+using LibWindPop.Utils.Graphics.Texture.IGraphicsAPITexture.OpenGLES;
+using LibWindPop.Utils.Graphics.Texture.Shared;
+using System;
+
+namespace LibWindPop.Utils.Graphics.Texture.Coder
+{
+ public readonly unsafe struct RGB_ATC_UByte : ITextureCoder, IPitchableTextureCoder, IOpenGLES20CompressedTexture
+ {
+ public static int OpenGLES20InternalFormat => 0x8C92; // GL_ATC_RGB_AMD
+
+ public readonly void Decode(ReadOnlySpan srcData, int width, int height, RefBitmap dstBitmap)
+ {
+ Decode(srcData, width, height, (width + 3) / 4 * 8, dstBitmap);
+ }
+
+ public readonly void Encode(RefBitmap srcBitmap, Span dstData, int width, int height)
+ {
+ Encode(srcBitmap, dstData, width, height, (width + 3) / 4 * 8);
+ }
+
+ public readonly void Decode(ReadOnlySpan srcData, int width, int height, int pitch, RefBitmap dstBitmap)
+ {
+ ThrowHelper.ThrowWhen(width < 0 || height < 0);
+ int tempDataIndex;
+ Span decodeBlockData = stackalloc YFColor[16];
+ int srcDataIndex = 0;
+ for (int yTile = 0; yTile < height; yTile += 4)
+ {
+ tempDataIndex = srcDataIndex;
+ srcDataIndex += pitch;
+ for (int xTile = 0; xTile < width; xTile += 4)
+ {
+ ATCCoder.DecodeATCBlock(srcData.Slice(tempDataIndex, 8), decodeBlockData);
+ tempDataIndex += 8;
+ for (int y = 0; y < 4; y++)
+ {
+ if ((yTile + y) >= height)
+ {
+ break;
+ }
+ for (int x = 0; x < 4; x++)
+ {
+ if ((xTile + x) >= width)
+ {
+ break;
+ }
+ dstBitmap[xTile | x, yTile | y] = decodeBlockData[(y << 2) | x];
+ }
+ }
+ }
+ }
+ }
+
+ public readonly void Encode(RefBitmap srcBitmap, Span dstData, int width, int height, int pitch)
+ {
+ ThrowHelper.ThrowWhen(width < 0 || height < 0);
+ int tempDataIndex;
+ Span encodeBlockData = stackalloc YFColor[16];
+ int srcDataIndex = 0;
+ for (int yTile = 0; yTile < height; yTile += 4)
+ {
+ tempDataIndex = srcDataIndex;
+ srcDataIndex += pitch;
+ for (int xTile = 0; xTile < width; xTile += 4)
+ {
+ for (int y = 0; y < 4; y++)
+ {
+ for (int x = 0; x < 4; x++)
+ {
+ encodeBlockData[(y << 2) | x] = ((yTile + y) >= height || (xTile + x) >= width) ? YFColor.Transparent : srcBitmap[xTile | x, yTile | y];
+ }
+ }
+ ATCCoder.EncodeATCBlock(dstData.Slice(tempDataIndex, 8), encodeBlockData);
+ tempDataIndex += 8;
+ }
+ }
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGB_ETC1_UByte.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGB_ETC1_UByte.cs
new file mode 100644
index 0000000..3a688b6
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGB_ETC1_UByte.cs
@@ -0,0 +1,72 @@
+using LibWindPop.Utils.Graphics.Bitmap;
+using LibWindPop.Utils.Graphics.Texture.IGraphicsAPITexture.OpenGLES;
+using LibWindPop.Utils.Graphics.Texture.Shared;
+using System;
+
+namespace LibWindPop.Utils.Graphics.Texture.Coder
+{
+ public readonly unsafe struct RGB_ETC1_UByte : ITextureCoder, IOpenGLES20CompressedTexture
+ {
+ public static int OpenGLES20InternalFormat => 0x8D64; // GL_ETC1_RGB8_OES
+
+ public readonly void Decode(ReadOnlySpan srcData, int width, int height, RefBitmap dstBitmap)
+ {
+ // if (PVRTexLibHelper.Decode(srcData, PVRTexLib.PVRTexLibPixelFormat.ETC1, dstBitmap))
+ // {
+ // return;
+ // }
+ ThrowHelper.ThrowWhen(width < 0 || height < 0);
+ int tempDataIndex = 0;
+ Span decodeBlockData = stackalloc YFColor[16];
+ for (int yTile = 0; yTile < height; yTile += 4)
+ {
+ for (int xTile = 0; xTile < width; xTile += 4)
+ {
+ ETCCoder.DecodeR8G8B8ETC1Block(srcData.Slice(tempDataIndex, 8), decodeBlockData);
+ tempDataIndex += 8;
+ for (int y = 0; y < 4; y++)
+ {
+ if ((yTile + y) >= height)
+ {
+ break;
+ }
+ for (int x = 0; x < 4; x++)
+ {
+ if ((xTile + x) >= width)
+ {
+ break;
+ }
+ dstBitmap[xTile | x, yTile | y] = decodeBlockData[(y << 2) | x];
+ }
+ }
+ }
+ }
+ }
+
+ public readonly void Encode(RefBitmap srcBitmap, Span dstData, int width, int height)
+ {
+ // if (PVRTexLibHelper.Encode(srcBitmap, PVRTexLib.PVRTexLibPixelFormat.ETC1, dstData))
+ // {
+ // return;
+ // }
+ ThrowHelper.ThrowWhen(width < 0 || height < 0);
+ int tempDataIndex = 0;
+ Span encodeBlockData = stackalloc YFColor[16];
+ for (int yTile = 0; yTile < height; yTile += 4)
+ {
+ for (int xTile = 0; xTile < width; xTile += 4)
+ {
+ for (int y = 0; y < 4; y++)
+ {
+ for (int x = 0; x < 4; x++)
+ {
+ encodeBlockData[(y << 2) | x] = ((yTile + y) >= height || (xTile + x) >= width) ? YFColor.Transparent : srcBitmap[xTile | x, yTile | y];
+ }
+ }
+ ETCCoder.EncodeR8G8B8ETC1Block(dstData.Slice(tempDataIndex, 8), encodeBlockData);
+ tempDataIndex += 8;
+ }
+ }
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGB_ETC2_UByte.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGB_ETC2_UByte.cs
new file mode 100644
index 0000000..fd9e19f
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGB_ETC2_UByte.cs
@@ -0,0 +1,69 @@
+using LibWindPop.Utils.Graphics.Bitmap;
+using LibWindPop.Utils.Graphics.Texture.Shared;
+using System;
+
+namespace LibWindPop.Utils.Graphics.Texture.Coder
+{
+ public readonly unsafe struct RGB_ETC2_UByte : ITextureCoder
+ {
+ public readonly void Decode(ReadOnlySpan srcData, int width, int height, RefBitmap dstBitmap)
+ {
+ // if (PVRTexLibHelper.Decode(srcData, PVRTexLib.PVRTexLibPixelFormat.ETC2_RGB, dstBitmap))
+ // {
+ // return;
+ // }
+ ThrowHelper.ThrowWhen(width < 0 || height < 0);
+ int tempDataIndex = 0;
+ Span decodeBlockData = stackalloc YFColor[16];
+ for (int yTile = 0; yTile < height; yTile += 4)
+ {
+ for (int xTile = 0; xTile < width; xTile += 4)
+ {
+ ETCCoder.DecodeR8G8B8ETC2Block(srcData.Slice(tempDataIndex, 8), decodeBlockData);
+ tempDataIndex += 8;
+ for (int y = 0; y < 4; y++)
+ {
+ if ((yTile + y) >= height)
+ {
+ break;
+ }
+ for (int x = 0; x < 4; x++)
+ {
+ if ((xTile + x) >= width)
+ {
+ break;
+ }
+ dstBitmap[xTile | x, yTile | y] = decodeBlockData[(y << 2) | x];
+ }
+ }
+ }
+ }
+ }
+
+ public readonly void Encode(RefBitmap srcBitmap, Span dstData, int width, int height)
+ {
+ // if (PVRTexLibHelper.Encode(srcBitmap, PVRTexLib.PVRTexLibPixelFormat.ETC2_RGB, dstData))
+ // {
+ // return;
+ // }
+ ThrowHelper.ThrowWhen(width < 0 || height < 0);
+ int tempDataIndex = 0;
+ Span encodeBlockData = stackalloc YFColor[16];
+ for (int yTile = 0; yTile < height; yTile += 4)
+ {
+ for (int x_tile = 0; x_tile < width; x_tile += 4)
+ {
+ for (int y = 0; y < 4; y++)
+ {
+ for (int x = 0; x < 4; x++)
+ {
+ encodeBlockData[(y << 2) | x] = ((yTile + y) >= height || (x_tile + x) >= width) ? YFColor.Transparent : srcBitmap[x_tile | x, yTile | y];
+ }
+ }
+ ETCCoder.EncodeR8G8B8ETC2Block(dstData.Slice(tempDataIndex, 8), encodeBlockData);
+ tempDataIndex += 8;
+ }
+ }
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGB_PVRTCI_2BPP_UByte.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGB_PVRTCI_2BPP_UByte.cs
new file mode 100644
index 0000000..a06b158
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGB_PVRTCI_2BPP_UByte.cs
@@ -0,0 +1,44 @@
+using LibWindPop.Utils.Graphics.Bitmap;
+using LibWindPop.Utils.Graphics.Texture.IGraphicsAPITexture.OpenGLES;
+using LibWindPop.Utils.Graphics.Texture.Shared;
+using System;
+
+namespace LibWindPop.Utils.Graphics.Texture.Coder
+{
+ public readonly unsafe struct RGB_PVRTCI_2BPP_UByte : ITextureCoder, IOpenGLES20CompressedTexture
+ {
+ public static int OpenGLES20InternalFormat => 0x8C01; // GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG
+
+ public readonly void Decode(ReadOnlySpan srcData, int width, int height, RefBitmap dstBitmap)
+ {
+ // if (PVRTexLibHelper.Decode(srcData, PVRTexLib.PVRTexLibPixelFormat.PVRTCI_2bpp_RGB, dstBitmap))
+ // {
+ // return;
+ // }
+ ThrowHelper.ThrowWhen(width < 0 || height < 0 || !BitHelper.IsPowerOfTwo(width) || !BitHelper.IsPowerOfTwo(height));
+ fixed (YFColor* color = dstBitmap.Data)
+ {
+ fixed (byte* data = srcData)
+ {
+ PVRTCCoder.DecodeTexture_RGB_PVRTCI_2BPP(data, color, (uint)width, (uint)height);
+ }
+ }
+ }
+
+ public readonly void Encode(RefBitmap srcBitmap, Span dstData, int width, int height)
+ {
+ // if (PVRTexLibHelper.Encode(srcBitmap, PVRTexLib.PVRTexLibPixelFormat.PVRTCI_2bpp_RGB, dstData))
+ // {
+ // return;
+ // }
+ ThrowHelper.ThrowWhen(width < 0 || height < 0 || !BitHelper.IsPowerOfTwo(width) || !BitHelper.IsPowerOfTwo(height));
+ fixed (YFColor* color = srcBitmap.Data)
+ {
+ fixed (byte* data = dstData)
+ {
+ PVRTCCoder.EncodeTexture_RGB_PVRTCI_2BPP(data, color, (uint)width, (uint)height);
+ }
+ }
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGB_PVRTCI_4BPP_UByte.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGB_PVRTCI_4BPP_UByte.cs
new file mode 100644
index 0000000..abc6e5e
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RGB_PVRTCI_4BPP_UByte.cs
@@ -0,0 +1,44 @@
+using LibWindPop.Utils.Graphics.Bitmap;
+using LibWindPop.Utils.Graphics.Texture.IGraphicsAPITexture.OpenGLES;
+using LibWindPop.Utils.Graphics.Texture.Shared;
+using System;
+
+namespace LibWindPop.Utils.Graphics.Texture.Coder
+{
+ public readonly unsafe struct RGB_PVRTCI_4BPP_UByte : ITextureCoder, IOpenGLES20CompressedTexture
+ {
+ public static int OpenGLES20InternalFormat => 0x8C00; // GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG
+
+ public readonly void Decode(ReadOnlySpan srcData, int width, int height, RefBitmap dstBitmap)
+ {
+ // if (PVRTexLibHelper.Decode(srcData, PVRTexLib.PVRTexLibPixelFormat.PVRTCI_4bpp_RGB, dstBitmap))
+ // {
+ // return;
+ // }
+ ThrowHelper.ThrowWhen(width < 0 || height < 0 || !BitHelper.IsPowerOfTwo(width) || !BitHelper.IsPowerOfTwo(height));
+ fixed (YFColor* color = dstBitmap.Data)
+ {
+ fixed (byte* data = srcData)
+ {
+ PVRTCCoder.DecodeTexture_RGB_PVRTCI_4BPP(data, color, (uint)width, (uint)height);
+ }
+ }
+ }
+
+ public readonly void Encode(RefBitmap srcBitmap, Span dstData, int width, int height)
+ {
+ // if (PVRTexLibHelper.Encode(srcBitmap, PVRTexLib.PVRTexLibPixelFormat.PVRTCI_4bpp_RGB, dstData))
+ // {
+ // return;
+ // }
+ ThrowHelper.ThrowWhen(width < 0 || height < 0 || !BitHelper.IsPowerOfTwo(width) || !BitHelper.IsPowerOfTwo(height));
+ fixed (YFColor* color = srcBitmap.Data)
+ {
+ fixed (byte* data = dstData)
+ {
+ PVRTCCoder.EncodeTexture_RGB_PVRTCI_4BPP(data, color, (uint)width, (uint)height);
+ }
+ }
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RG_EAC_UByte.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RG_EAC_UByte.cs
new file mode 100644
index 0000000..37280a4
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/RG_EAC_UByte.cs
@@ -0,0 +1,70 @@
+using LibWindPop.Utils.Graphics.Bitmap;
+using LibWindPop.Utils.Graphics.Texture.Shared;
+using System;
+
+namespace LibWindPop.Utils.Graphics.Texture.Coder
+{
+ public readonly unsafe struct RG_EAC_UByte : ITextureCoder
+ {
+ public readonly void Decode(ReadOnlySpan srcData, int width, int height, RefBitmap dstBitmap)
+ {
+ // if (PVRTexLibHelper.Decode(srcData, PVRTexLib.PVRTexLibPixelFormat.EAC_RG11, dstBitmap))
+ // {
+ // return;
+ // }
+ ThrowHelper.ThrowWhen(width < 0 || height < 0);
+ int tempDataIndex = 0;
+ Span decodeBlockData = stackalloc YFColor[16];
+ decodeBlockData.Fill(YFColor.Black);
+ for (int yTile = 0; yTile < height; yTile += 4)
+ {
+ for (int xTile = 0; xTile < width; xTile += 4)
+ {
+ EACCoder.DecodeR11G11EACBlock(srcData.Slice(tempDataIndex, 16), decodeBlockData);
+ tempDataIndex += 16;
+ for (int y = 0; y < 4; y++)
+ {
+ if ((yTile + y) >= height)
+ {
+ break;
+ }
+ for (int x = 0; x < 4; x++)
+ {
+ if ((xTile + x) >= width)
+ {
+ break;
+ }
+ dstBitmap[xTile | x, yTile | y] = decodeBlockData[(y << 2) | x];
+ }
+ }
+ }
+ }
+ }
+
+ public readonly void Encode(RefBitmap srcBitmap, Span dstData, int width, int height)
+ {
+ // if (PVRTexLibHelper.Encode(srcBitmap, PVRTexLib.PVRTexLibPixelFormat.EAC_RG11, dstData))
+ // {
+ // return;
+ // }
+ ThrowHelper.ThrowWhen(width < 0 || height < 0);
+ int tempDataIndex = 0;
+ Span encodeBlockData = stackalloc YFColor[16];
+ for (int yTile = 0; yTile < height; yTile += 4)
+ {
+ for (int xTile = 0; xTile < width; xTile += 4)
+ {
+ for (int y = 0; y < 4; y++)
+ {
+ for (int x = 0; x < 4; x++)
+ {
+ encodeBlockData[(y << 2) | x] = ((yTile + y) >= height || (xTile + x) >= width) ? YFColor.Transparent : srcBitmap[xTile | x, yTile | y];
+ }
+ }
+ EACCoder.EncodeR11G11EACBlock(dstData.Slice(tempDataIndex, 16), encodeBlockData);
+ tempDataIndex += 16;
+ }
+ }
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/R_EAC_UByte.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/R_EAC_UByte.cs
new file mode 100644
index 0000000..5e795cf
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Coder/R_EAC_UByte.cs
@@ -0,0 +1,70 @@
+using LibWindPop.Utils.Graphics.Bitmap;
+using LibWindPop.Utils.Graphics.Texture.Shared;
+using System;
+
+namespace LibWindPop.Utils.Graphics.Texture.Coder
+{
+ public readonly unsafe struct R_EAC_UByte : ITextureCoder
+ {
+ public readonly void Decode(ReadOnlySpan srcData, int width, int height, RefBitmap dstBitmap)
+ {
+ // if (PVRTexLibHelper.Decode(srcData, PVRTexLib.PVRTexLibPixelFormat.EAC_R11, dstBitmap))
+ // {
+ // return;
+ // }
+ ThrowHelper.ThrowWhen(width < 0 || height < 0);
+ int tempDataIndex = 0;
+ Span decodeBlockData = stackalloc YFColor[16];
+ decodeBlockData.Fill(YFColor.Black);
+ for (int yTile = 0; yTile < height; yTile += 4)
+ {
+ for (int xTile = 0; xTile < width; xTile += 4)
+ {
+ EACCoder.DecodeR11EACBlock(srcData.Slice(tempDataIndex, 8), decodeBlockData);
+ tempDataIndex += 8;
+ for (int y = 0; y < 4; y++)
+ {
+ if ((yTile + y) >= height)
+ {
+ break;
+ }
+ for (int x = 0; x < 4; x++)
+ {
+ if ((xTile + x) >= width)
+ {
+ break;
+ }
+ dstBitmap[xTile | x, yTile | y] = decodeBlockData[(y << 2) | x];
+ }
+ }
+ }
+ }
+ }
+
+ public readonly void Encode(RefBitmap srcBitmap, Span dstData, int width, int height)
+ {
+ // if (PVRTexLibHelper.Encode(srcBitmap, PVRTexLib.PVRTexLibPixelFormat.EAC_R11, dstData))
+ // {
+ // return;
+ // }
+ ThrowHelper.ThrowWhen(width < 0 || height < 0);
+ int tempDataIndex = 0;
+ Span encodeBlockData = stackalloc YFColor[16];
+ for (int yTile = 0; yTile < height; yTile += 4)
+ {
+ for (int xTile = 0; xTile < width; xTile += 4)
+ {
+ for (int y = 0; y < 4; y++)
+ {
+ for (int x = 0; x < 4; x++)
+ {
+ encodeBlockData[(y << 2) | x] = ((yTile + y) >= height || (xTile + x) >= width) ? YFColor.Transparent : srcBitmap[xTile | x, yTile | y];
+ }
+ }
+ EACCoder.EncodeR11EACBlock(dstData.Slice(tempDataIndex, 8), encodeBlockData);
+ tempDataIndex += 8;
+ }
+ }
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/OpenGLES/IOpenGLES20CompressedTexture.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/OpenGLES/IOpenGLES20CompressedTexture.cs
new file mode 100644
index 0000000..f6b0e17
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/OpenGLES/IOpenGLES20CompressedTexture.cs
@@ -0,0 +1,11 @@
+namespace LibWindPop.Utils.Graphics.Texture.IGraphicsAPITexture.OpenGLES
+{
+ ///
+ /// glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data);
+ /// glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data);
+ ///
+ public interface IOpenGLES20CompressedTexture
+ {
+ static abstract int OpenGLES20InternalFormat { get; }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/OpenGLES/IOpenGLES20Texture.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/OpenGLES/IOpenGLES20Texture.cs
new file mode 100644
index 0000000..e846505
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/OpenGLES/IOpenGLES20Texture.cs
@@ -0,0 +1,15 @@
+namespace LibWindPop.Utils.Graphics.Texture.IGraphicsAPITexture.OpenGLES
+{
+ ///
+ /// glTexImage2D(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
+ /// glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels);
+ ///
+ public interface IOpenGLES20Texture
+ {
+ static abstract int OpenGLES20InternalFormat { get; }
+
+ static abstract int OpenGLES20Format { get; }
+
+ static abstract int OpenGLES20Type { get; }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/Xbox360D3D9/D3DFORMAT.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/Xbox360D3D9/D3DFORMAT.cs
new file mode 100644
index 0000000..e2d4437
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/Xbox360D3D9/D3DFORMAT.cs
@@ -0,0 +1,472 @@
+using System;
+
+namespace LibWindPop.Utils.Graphics.Texture.IGraphicsAPITexture.Xbox360D3D9
+{
+ ///
+ /// Defines the various types of surface formats.
+ ///
+ public readonly struct D3DFORMAT : IEquatable
+ {
+ ///
+ /// inner value
+ ///
+ private readonly uint value;
+
+ ///
+ /// Describes the texture format.
+ ///
+ public GPUTEXTUREFORMAT TextureFormat => (GPUTEXTUREFORMAT)((value >> 0) & 0x3Fu);
+
+ ///
+ /// Describes whether the format is big endian or little endian.
+ ///
+ public GPUENDIAN Endian => (GPUENDIAN)((value >> 6) & 0x3u);
+
+ ///
+ /// Describes whether the format is tiled or linear.
+ ///
+ public bool Tiled => ((value >> 8) & 0x1u) != 0u;
+
+ ///
+ /// Describes whether the sign of individual texel elements is unsigned, signed, unsigned biased, or unsigned gamma corrected (for select data formats).
+ ///
+ public GPUSIGN TextureSignX => (GPUSIGN)((value >> 9) & 0x3u);
+
+ ///
+ /// Describes whether the sign of individual texel elements is unsigned, signed, unsigned biased, or unsigned gamma corrected (for select data formats).
+ ///
+ public GPUSIGN TextureSignY => (GPUSIGN)((value >> 11) & 0x3u);
+
+ ///
+ /// Describes whether the sign of individual texel elements is unsigned, signed, unsigned biased, or unsigned gamma corrected (for select data formats).
+ ///
+ public GPUSIGN TextureSignZ => (GPUSIGN)((value >> 13) & 0x3u);
+
+ ///
+ /// Describes whether the sign of individual texel elements is unsigned, signed, unsigned biased, or unsigned gamma corrected (for select data formats).
+ ///
+ public GPUSIGN TextureSignW => (GPUSIGN)((value >> 15) & 0x3u);
+
+ ///
+ /// Describes whether the number format is a fraction or an integer.
+ ///
+ public GPUNUMFORMAT NumFormat => (GPUNUMFORMAT)((value >> 17) & 0x1u);
+
+ ///
+ /// Describes how components of a texel are mapped to the elements of a register into which that texel is fetched.
+ ///
+ public GPUSWIZZLE SwizzleX => (GPUSWIZZLE)((value >> 18) & 0x7u);
+
+ ///
+ /// Describes how components of a texel are mapped to the elements of a register into which that texel is fetched.
+ ///
+ public GPUSWIZZLE SwizzleY => (GPUSWIZZLE)((value >> 21) & 0x7u);
+
+ ///
+ /// Describes how components of a texel are mapped to the elements of a register into which that texel is fetched.
+ ///
+ public GPUSWIZZLE SwizzleZ => (GPUSWIZZLE)((value >> 24) & 0x7u);
+
+ ///
+ /// Describes how components of a texel are mapped to the elements of a register into which that texel is fetched.
+ ///
+ public GPUSWIZZLE SwizzleW => (GPUSWIZZLE)((value >> 27) & 0x7u);
+
+ private D3DFORMAT(uint value)
+ {
+ this.value = value;
+ }
+
+ ///
+ /// MAKED3DFMT(TextureFormat, Endian, Tiled, TextureSign, NumFormat, Swizzle)
+ ///
+ /// Member of the GPUTEXTUREFORMAT enumeration.
+ /// Member of the GPUENDIAN enumeration.
+ /// Boolean. Pass TRUE for a tiled format; otherwise, pass FALSE.
+ /// Member of the GPUSIGN enumeration.
+ /// Member of the GPUNUMFORMAT enumeration.
+ /// Bit mask that describes how texture channels are mapped during a texture fetch. The bit mask consists of four members of the GPUSWIZZLE enumeration.
+ public D3DFORMAT(GPUTEXTUREFORMAT TextureFormat, GPUENDIAN Endian, bool Tiled, GPUSIGN TextureSign, GPUNUMFORMAT NumFormat, GPUSWIZZLE Swizzle)
+ {
+ value = ((((uint)TextureFormat & 0x3Fu) << 0)
+ | (((uint)Endian & 0x3u) << 6)
+ | ((Tiled ? 1u : 0u) << 8)
+ | (((uint)TextureSign & 0xFFu) << 9)
+ | (((uint)NumFormat & 0x1u) << 17)
+ | (((uint)Swizzle & 0xFFFu) << 18));
+ }
+
+ ///
+ /// MAKED3DFMT2(TextureFormat, Endian, Tiled, TextureSignX, TextureSignY, TextureSignZ, TextureSignW, NumFormat, SwizzleX, SwizzleY, SwizzleZ, SwizzleW)
+ ///
+ /// A member of the GPUTEXTUREFORMAT enumeration.
+ /// A member of the GPUENDIAN enumeration.
+ /// Pass TRUE for a tiled format; otherwise pass FALSE.
+ /// A member of the GPUSIGN enumeration.
+ /// A member of the GPUSIGN enumeration.
+ /// A member of the GPUSIGN enumeration.
+ /// A member of the GPUSIGN enumeration.
+ /// A member of the GPUNUMFORMAT enumeration.
+ /// A member of the GPUSWIZZLE enumeration.
+ /// A member of the GPUSWIZZLE enumeration.
+ /// A member of the GPUSWIZZLE enumeration.
+ /// A member of the GPUSWIZZLE enumeration.
+ public D3DFORMAT(GPUTEXTUREFORMAT TextureFormat, GPUENDIAN Endian, bool Tiled, GPUSIGN TextureSignX, GPUSIGN TextureSignY, GPUSIGN TextureSignZ, GPUSIGN TextureSignW, GPUNUMFORMAT NumFormat, GPUSWIZZLE SwizzleX, GPUSWIZZLE SwizzleY, GPUSWIZZLE SwizzleZ, GPUSWIZZLE SwizzleW)
+ {
+ value = (((uint)TextureFormat & 0x3Fu) << 0)
+ | (((uint)Endian & 0x3u) << 6)
+ | ((Tiled ? 1u : 0u) << 8)
+ | (((uint)TextureSignX & 0x3u) << 9)
+ | (((uint)TextureSignY & 0x3u) << 11)
+ | (((uint)TextureSignZ & 0x3u) << 13)
+ | (((uint)TextureSignW & 0x3u) << 15)
+ | (((uint)NumFormat & 0x1u) << 17)
+ | (((uint)SwizzleX & 0x7u) << 18)
+ | (((uint)SwizzleY & 0x7u) << 21)
+ | (((uint)SwizzleZ & 0x7u) << 24)
+ | (((uint)SwizzleW & 0x7u) << 27);
+ }
+
+ ///
+ /// MAKEINDEXFMT(Is32Bits, Endian)
+ ///
+ ///
+ ///
+ public D3DFORMAT(bool Is32Bits, GPUENDIAN Endian)
+ {
+ value = (Is32Bits ? 1u : 0u) << 2 | ((uint)Endian) << 0;
+ }
+
+ ///
+ /// MAKELEFMT(D3dFmt) (((D3dFmt) & ~D3DFORMAT_ENDIAN_MASK) | (GPUENDIAN_NONE << D3DFORMAT_ENDIAN_SHIFT))
+ ///
+ /// Little endian format
+ public readonly D3DFORMAT GetLEFormat()
+ {
+ return new D3DFORMAT(((value) & ~0x000000C0u) | ((uint)GPUENDIAN.GPUENDIAN_NONE << 6));
+ }
+
+ ///
+ /// MAKELINFMT(D3dFmt) ((D3dFmt) & ~D3DFORMAT_TILED_MASK)
+ ///
+ /// Linear format
+ public readonly D3DFORMAT GetLinearFormat()
+ {
+ return new D3DFORMAT((value) & ~0x00000100u);
+ }
+
+ ///
+ /// MAKESRGBFMT(D3dFmt)
+ ///
+ /// sRGB format
+ public readonly D3DFORMAT GetSrgbFormat()
+ {
+ return new D3DFORMAT(((value) & ~(0x00000600u | 0x00001800u | 0x00006000u))
+ | ((uint)GPUSIGN.GPUSIGN_GAMMA << 9)
+ | ((uint)GPUSIGN.GPUSIGN_GAMMA << 11)
+ | ((uint)GPUSIGN.GPUSIGN_GAMMA << 13));
+ }
+
+ public readonly override bool Equals(object? obj)
+ {
+ return (obj is D3DFORMAT fmt) && value == fmt.value;
+ }
+
+ public readonly bool Equals(D3DFORMAT other)
+ {
+ return value == other.value;
+ }
+
+ public readonly override int GetHashCode()
+ {
+ return value.GetHashCode();
+ }
+
+ public readonly override string ToString()
+ {
+ return value.ToString();
+ }
+
+ public static bool operator ==(D3DFORMAT fmt1, D3DFORMAT fmt2)
+ {
+ return fmt1.value == fmt2.value;
+ }
+
+ public static bool operator !=(D3DFORMAT fmt1, D3DFORMAT fmt2)
+ {
+ return fmt1.value != fmt2.value;
+ }
+
+ public static implicit operator uint(D3DFORMAT fmt)
+ {
+ return fmt.value;
+ }
+
+ public static implicit operator D3DFORMAT(uint fmt)
+ {
+ return new D3DFORMAT(fmt);
+ }
+
+ // DXT:
+
+ public static readonly D3DFORMAT D3DFMT_DXT1 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_DXT1, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_DXT1 = D3DFMT_DXT1.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_DXT2 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_DXT2_3, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_DXT2 = D3DFMT_DXT2.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_DXT3 = D3DFMT_DXT2;
+ public static readonly D3DFORMAT D3DFMT_LIN_DXT3 = D3DFMT_DXT3.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_DXT3A = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_DXT3A, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_DXT3A = D3DFMT_DXT3A.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_DXT3A_1111 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_DXT3A_AS_1_1_1_1, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_DXT3A_1111 = D3DFMT_DXT3A_1111.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_DXT4 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_DXT4_5, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_DXT4 = D3DFMT_DXT4.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_DXT5 = D3DFMT_DXT4;
+ public static readonly D3DFORMAT D3DFMT_LIN_DXT5 = D3DFMT_DXT5.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_DXT5A = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_DXT5A, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_DXT5A = D3DFMT_DXT5A.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_DXN = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_DXN, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_DXN = D3DFMT_DXN.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_CTX1 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_CTX1, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_CTX1 = D3DFMT_CTX1.GetLinearFormat();
+
+ // 8bpp:
+
+ public static readonly D3DFORMAT D3DFMT_A8 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_8, GPUENDIAN.GPUENDIAN_NONE, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_RZZZ);
+ public static readonly D3DFORMAT D3DFMT_LIN_A8 = D3DFMT_A8.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_L8 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_8, GPUENDIAN.GPUENDIAN_NONE, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ORRR);
+ public static readonly D3DFORMAT D3DFMT_LIN_L8 = D3DFMT_L8.GetLinearFormat();
+
+ // 16bpp:
+
+ public static readonly D3DFORMAT D3DFMT_R5G6B5 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_5_6_5, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ORGB);
+ public static readonly D3DFORMAT D3DFMT_LIN_R5G6B5 = D3DFMT_R5G6B5.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_R6G5B5 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_6_5_5, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ORGB);
+ public static readonly D3DFORMAT D3DFMT_LIN_R6G5B5 = D3DFMT_R6G5B5.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_L6V5U5 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_6_5_5, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_SIGNED, GPUSIGN.GPUSIGN_SIGNED, GPUSIGN.GPUSIGN_UNSIGNED, GPUSIGN.GPUSIGN_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_X, GPUSWIZZLE.GPUSWIZZLE_Y, GPUSWIZZLE.GPUSWIZZLE_Z, GPUSWIZZLE.GPUSWIZZLE_1);
+ public static readonly D3DFORMAT D3DFMT_LIN_L6V5U5 = D3DFMT_L6V5U5.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_X1R5G5B5 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_1_5_5_5, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ORGB);
+ public static readonly D3DFORMAT D3DFMT_LIN_X1R5G5B5 = D3DFMT_X1R5G5B5.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_A1R5G5B5 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_1_5_5_5, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ARGB);
+ public static readonly D3DFORMAT D3DFMT_LIN_A1R5G5B5 = D3DFMT_A1R5G5B5.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_A4R4G4B4 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_4_4_4_4, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ARGB);
+ public static readonly D3DFORMAT D3DFMT_LIN_A4R4G4B4 = D3DFMT_A4R4G4B4.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_X4R4G4B4 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_4_4_4_4, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ORGB);
+ public static readonly D3DFORMAT D3DFMT_LIN_X4R4G4B4 = D3DFMT_X4R4G4B4.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_Q4W4V4U4 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_4_4_4_4, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_SIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_Q4W4V4U4 = D3DFMT_Q4W4V4U4.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_A8L8 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_8_8, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_GRRR);
+ public static readonly D3DFORMAT D3DFMT_LIN_A8L8 = D3DFMT_A8L8.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_G8R8 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_8_8, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_OOGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_G8R8 = D3DFMT_G8R8.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_V8U8 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_8_8, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_SIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_OOGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_V8U8 = D3DFMT_V8U8.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_D16 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_16, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_INTEGER, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_D16 = D3DFMT_D16.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_L16 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_16, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ORRR);
+ public static readonly D3DFORMAT D3DFMT_LIN_L16 = D3DFMT_L16.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_R16F = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_16_FLOAT, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_SIGNED, GPUNUMFORMAT.GPUNUMFORMAT_INTEGER, GPUSWIZZLE.GPUSWIZZLE_OOOR);
+ public static readonly D3DFORMAT D3DFMT_LIN_R16F = D3DFMT_R16F.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_R16F_EXPAND = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_16_EXPAND, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_SIGNED, GPUNUMFORMAT.GPUNUMFORMAT_INTEGER, GPUSWIZZLE.GPUSWIZZLE_OOOR);
+ public static readonly D3DFORMAT D3DFMT_LIN_R16F_EXPAND = D3DFMT_R16F_EXPAND.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_UYVY = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_Y1_Cr_Y0_Cb_REP, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_UYVY = D3DFMT_UYVY.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_LE_UYVY = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_Y1_Cr_Y0_Cb_REP, GPUENDIAN.GPUENDIAN_NONE, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LE_LIN_UYVY = D3DFMT_LE_UYVY.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_G8R8_G8B8 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_Y1_Cr_Y0_Cb_REP, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ARGB);
+ public static readonly D3DFORMAT D3DFMT_LIN_G8R8_G8B8 = D3DFMT_G8R8_G8B8.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_R8G8_B8G8 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_Cr_Y1_Cb_Y0_REP, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ARGB);
+ public static readonly D3DFORMAT D3DFMT_LIN_R8G8_B8G8 = D3DFMT_R8G8_B8G8.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_YUY2 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_Cr_Y1_Cb_Y0_REP, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_YUY2 = D3DFMT_YUY2.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_LE_YUY2 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_Cr_Y1_Cb_Y0_REP, GPUENDIAN.GPUENDIAN_NONE, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LE_LIN_YUY2 = D3DFMT_LE_YUY2.GetLinearFormat();
+
+ // 32bpp:
+
+ public static readonly D3DFORMAT D3DFMT_A8R8G8B8 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_8_8_8_8, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ARGB);
+ public static readonly D3DFORMAT D3DFMT_LIN_A8R8G8B8 = D3DFMT_A8R8G8B8.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_X8R8G8B8 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_8_8_8_8, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ORGB);
+ public static readonly D3DFORMAT D3DFMT_LIN_X8R8G8B8 = D3DFMT_X8R8G8B8.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_A8B8G8R8 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_8_8_8_8, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_A8B8G8R8 = D3DFMT_A8B8G8R8.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_X8B8G8R8 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_8_8_8_8, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_OBGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_X8B8G8R8 = D3DFMT_X8B8G8R8.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_X8L8V8U8 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_8_8_8_8, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_SIGNED, GPUSIGN.GPUSIGN_SIGNED, GPUSIGN.GPUSIGN_UNSIGNED, GPUSIGN.GPUSIGN_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_X, GPUSWIZZLE.GPUSWIZZLE_Y, GPUSWIZZLE.GPUSWIZZLE_Z, GPUSWIZZLE.GPUSWIZZLE_1);
+ public static readonly D3DFORMAT D3DFMT_LIN_X8L8V8U8 = D3DFMT_X8L8V8U8.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_Q8W8V8U8 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_8_8_8_8, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_SIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_Q8W8V8U8 = D3DFMT_Q8W8V8U8.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_A2R10G10B10 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_2_10_10_10_AS_16_16_16_16, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ARGB);
+ public static readonly D3DFORMAT D3DFMT_LIN_A2R10G10B10 = D3DFMT_A2R10G10B10.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_X2R10G10B10 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_2_10_10_10_AS_16_16_16_16, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ORGB);
+ public static readonly D3DFORMAT D3DFMT_LIN_X2R10G10B10 = D3DFMT_X2R10G10B10.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_A2B10G10R10 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_2_10_10_10_AS_16_16_16_16, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_A2B10G10R10 = D3DFMT_A2B10G10R10.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_A2W10V10U10 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_2_10_10_10_AS_16_16_16_16, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_SIGNED, GPUSIGN.GPUSIGN_SIGNED, GPUSIGN.GPUSIGN_SIGNED, GPUSIGN.GPUSIGN_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_X, GPUSWIZZLE.GPUSWIZZLE_Y, GPUSWIZZLE.GPUSWIZZLE_Z, GPUSWIZZLE.GPUSWIZZLE_W);
+ public static readonly D3DFORMAT D3DFMT_LIN_A2W10V10U10 = D3DFMT_A2W10V10U10.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_A16L16 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_16_16, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_GRRR);
+ public static readonly D3DFORMAT D3DFMT_LIN_A16L16 = D3DFMT_A16L16.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_G16R16 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_16_16, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_OOGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_G16R16 = D3DFMT_G16R16.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_V16U16 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_16_16, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_SIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_OOGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_V16U16 = D3DFMT_V16U16.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_R10G11B11 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_10_11_11_AS_16_16_16_16, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ORGB);
+ public static readonly D3DFORMAT D3DFMT_LIN_R10G11B11 = D3DFMT_R10G11B11.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_R11G11B10 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_11_11_10_AS_16_16_16_16, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ORGB);
+ public static readonly D3DFORMAT D3DFMT_LIN_R11G11B10 = D3DFMT_R11G11B10.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_W10V11U11 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_10_11_11_AS_16_16_16_16, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_SIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_OBGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_W10V11U11 = D3DFMT_W10V11U11.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_W11V11U10 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_11_11_10_AS_16_16_16_16, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_SIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_OBGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_W11V11U10 = D3DFMT_W11V11U10.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_G16R16F = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_16_16_FLOAT, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_SIGNED, GPUNUMFORMAT.GPUNUMFORMAT_INTEGER, GPUSWIZZLE.GPUSWIZZLE_OOGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_G16R16F = D3DFMT_G16R16F.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_G16R16F_EXPAND = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_16_16_EXPAND, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_SIGNED, GPUNUMFORMAT.GPUNUMFORMAT_INTEGER, GPUSWIZZLE.GPUSWIZZLE_OOGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_G16R16F_EXPAND = D3DFMT_G16R16F_EXPAND.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_L32 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_32, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ORRR);
+ public static readonly D3DFORMAT D3DFMT_LIN_L32 = D3DFMT_L32.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_R32F = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_32_FLOAT, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_SIGNED, GPUNUMFORMAT.GPUNUMFORMAT_INTEGER, GPUSWIZZLE.GPUSWIZZLE_OOOR);
+ public static readonly D3DFORMAT D3DFMT_LIN_R32F = D3DFMT_R32F.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_D24S8 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_24_8, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_OOGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_D24S8 = D3DFMT_D24S8.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_D24X8 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_24_8, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_OOOR);
+ public static readonly D3DFORMAT D3DFMT_LIN_D24X8 = D3DFMT_D24X8.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_D24FS8 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_24_8_FLOAT, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_INTEGER, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_D24FS8 = D3DFMT_D24FS8.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_D32 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_32, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_INTEGER, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_D32 = D3DFMT_D32.GetLinearFormat();
+
+ // 64bpp:
+
+ public static readonly D3DFORMAT D3DFMT_A16B16G16R16 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_16_16_16_16, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_A16B16G16R16 = D3DFMT_A16B16G16R16.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_Q16W16V16U16 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_16_16_16_16, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_SIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_Q16W16V16U16 = D3DFMT_Q16W16V16U16.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_A16B16G16R16F = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_16_16_16_16_FLOAT, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_SIGNED, GPUNUMFORMAT.GPUNUMFORMAT_INTEGER, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_A16B16G16R16F = D3DFMT_A16B16G16R16F.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_A16B16G16R16F_EXPAND = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_16_16_16_16_EXPAND, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_SIGNED, GPUNUMFORMAT.GPUNUMFORMAT_INTEGER, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_A16B16G16R16F_EXPAND = D3DFMT_A16B16G16R16F_EXPAND.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_A32L32 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_32_32, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_GRRR);
+ public static readonly D3DFORMAT D3DFMT_LIN_A32L32 = D3DFMT_A32L32.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_G32R32 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_32_32, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_OOGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_G32R32 = D3DFMT_G32R32.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_V32U32 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_32_32, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_SIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_OOGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_V32U32 = D3DFMT_V32U32.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_G32R32F = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_32_32_FLOAT, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_SIGNED, GPUNUMFORMAT.GPUNUMFORMAT_INTEGER, GPUSWIZZLE.GPUSWIZZLE_OOGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_G32R32F = D3DFMT_G32R32F.GetLinearFormat();
+
+ // 128bpp:
+
+ public static readonly D3DFORMAT D3DFMT_A32B32G32R32 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_32_32_32_32, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_A32B32G32R32 = D3DFMT_A32B32G32R32.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_Q32W32V32U32 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_32_32_32_32, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_SIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_Q32W32V32U32 = D3DFMT_Q32W32V32U32.GetLinearFormat();
+
+ public static readonly D3DFORMAT D3DFMT_A32B32G32R32F = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_32_32_32_32_FLOAT, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_SIGNED, GPUNUMFORMAT.GPUNUMFORMAT_INTEGER, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_LIN_A32B32G32R32F = D3DFMT_A32B32G32R32F.GetLinearFormat();
+
+ // EDRAM only:
+
+ public static readonly D3DFORMAT D3DFMT_A2B10G10R10F_EDRAM = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_2_10_10_10_FLOAT_EDRAM, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_INTEGER, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+ public static readonly D3DFORMAT D3DFMT_G16R16_EDRAM = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_16_16_EDRAM, GPUENDIAN.GPUENDIAN_8IN32, true, GPUSIGN.GPUSIGN_ALL_SIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_OOGR);
+ public static readonly D3DFORMAT D3DFMT_A16B16G16R16_EDRAM = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_16_16_16_16_EDRAM, GPUENDIAN.GPUENDIAN_8IN16, true, GPUSIGN.GPUSIGN_ALL_SIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ABGR);
+
+ // Front buffer formats have to be little endian:
+
+ public static readonly D3DFORMAT D3DFMT_LE_X8R8G8B8 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_8_8_8_8, GPUENDIAN.GPUENDIAN_NONE, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ORGB);
+ public static readonly D3DFORMAT D3DFMT_LE_A8R8G8B8 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_8_8_8_8, GPUENDIAN.GPUENDIAN_NONE, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ARGB);
+ public static readonly D3DFORMAT D3DFMT_LE_X2R10G10B10 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_2_10_10_10_AS_16_16_16_16, GPUENDIAN.GPUENDIAN_NONE, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ORGB);
+ public static readonly D3DFORMAT D3DFMT_LE_A2R10G10B10 = new D3DFORMAT(GPUTEXTUREFORMAT.GPUTEXTUREFORMAT_2_10_10_10_AS_16_16_16_16, GPUENDIAN.GPUENDIAN_NONE, true, GPUSIGN.GPUSIGN_ALL_UNSIGNED, GPUNUMFORMAT.GPUNUMFORMAT_FRACTION, GPUSWIZZLE.GPUSWIZZLE_ARGB);
+
+ // Other:
+
+ public static readonly D3DFORMAT D3DFMT_INDEX16 = new D3DFORMAT(false, GPUENDIAN.GPUENDIAN_8IN16);
+ public static readonly D3DFORMAT D3DFMT_INDEX32 = new D3DFORMAT(true, GPUENDIAN.GPUENDIAN_8IN32);
+ public static readonly D3DFORMAT D3DFMT_LE_INDEX16 = new D3DFORMAT(false, GPUENDIAN.GPUENDIAN_NONE);
+ public static readonly D3DFORMAT D3DFMT_LE_INDEX32 = new D3DFORMAT(true, GPUENDIAN.GPUENDIAN_NONE);
+
+ public static readonly D3DFORMAT D3DFMT_VERTEXDATA = new D3DFORMAT(8u);
+
+ public static readonly D3DFORMAT D3DFMT_UNKNOWN = new D3DFORMAT(0xFFFFFFFFu);
+
+ // The following are not supported on Xbox 360:
+ //
+ // D3DFMT_A8R3G3B2
+ // D3DFMT_R3G3B2
+ // D3DFMT_S1D15
+ // D3DFMT_S8D24
+ // D3DFMT_X8D24
+ // D3DFMT_X4S4D24
+ // D3DFMT_A8P8
+ // D3DFMT_P8
+ // D3DFMT_A4L4
+ // D3DFMT_R8G8B8
+ // D3DFMT_D16_LOCKABLE
+ // D3DFMT_D15S1
+ // D3DFMT_D24X4S4
+ // D3DFMT_D32F_LOCKABLE
+
+ public static readonly D3DFORMAT D3DFMT_FORCE_DWORD = new D3DFORMAT(0x7FFFFFFFu);
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/Xbox360D3D9/GPUENDIAN.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/Xbox360D3D9/GPUENDIAN.cs
new file mode 100644
index 0000000..56717fb
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/Xbox360D3D9/GPUENDIAN.cs
@@ -0,0 +1,25 @@
+namespace LibWindPop.Utils.Graphics.Texture.IGraphicsAPITexture.Xbox360D3D9
+{
+ ///
+ /// Describes whether or not to do byte or word swapping to support different endian modes.
+ ///
+ public enum GPUENDIAN
+ {
+ ///
+ /// No swapping.
+ ///
+ GPUENDIAN_NONE = 0,
+ ///
+ /// Every 8 bits in a 16-bit word are swapped. For example, 0xAABBCCDD would be changed to 0xBBAADDCC.
+ ///
+ GPUENDIAN_8IN16 = 1,
+ ///
+ /// Every 8 bits in a 32-bit word are swapped. For example, 0xAABBCCDD would be changed to 0xDDCCBBAA.
+ ///
+ GPUENDIAN_8IN32 = 2,
+ ///
+ /// Every 16 bits in a 32-bit word are swapped. For example, 0xAABBCCDD would be changed to 0xCCDDAABB.
+ ///
+ GPUENDIAN_16IN32 = 3,
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/Xbox360D3D9/GPUNUMFORMAT.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/Xbox360D3D9/GPUNUMFORMAT.cs
new file mode 100644
index 0000000..3cc3776
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/Xbox360D3D9/GPUNUMFORMAT.cs
@@ -0,0 +1,17 @@
+namespace LibWindPop.Utils.Graphics.Texture.IGraphicsAPITexture.Xbox360D3D9
+{
+ ///
+ /// Describes whether a value is an integer or a fraction.
+ ///
+ public enum GPUNUMFORMAT
+ {
+ ///
+ /// Indicates that a value is a fraction.
+ ///
+ GPUNUMFORMAT_FRACTION = 0,
+ ///
+ /// Indicates that a value is an integer.
+ ///
+ GPUNUMFORMAT_INTEGER = 1,
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/Xbox360D3D9/GPUSIGN.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/Xbox360D3D9/GPUSIGN.cs
new file mode 100644
index 0000000..0636321
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/Xbox360D3D9/GPUSIGN.cs
@@ -0,0 +1,28 @@
+namespace LibWindPop.Utils.Graphics.Texture.IGraphicsAPITexture.Xbox360D3D9
+{
+ ///
+ /// Describes the sign of a value.
+ ///
+ public enum GPUSIGN
+ {
+ ///
+ /// Indicates that a value is unsigned.
+ ///
+ GPUSIGN_UNSIGNED = 0,
+ ///
+ /// Indicates that a value is signed.
+ ///
+ GPUSIGN_SIGNED = 1,
+ ///
+ /// Indicates that a value is unsigned biased. GPUSIGN_BIAS maps an UNSIGNED value with a range of 0 to 1 into a range of -1 to 1. The equation used to perform the mapping is 2 * x - 1. For example, the UNSIGNED values 0, 0.5 and 1 would map to -1, 0 and 1.
+ ///
+ GPUSIGN_BIAS = 2,
+ ///
+ /// Indicates that a value is unsigned gamma corrected.
+ ///
+ GPUSIGN_GAMMA = 3,
+
+ GPUSIGN_ALL_UNSIGNED = GPUSIGN_UNSIGNED | GPUSIGN_UNSIGNED << 2 | GPUSIGN_UNSIGNED << 4 | GPUSIGN_UNSIGNED << 6,
+ GPUSIGN_ALL_SIGNED = GPUSIGN_SIGNED | GPUSIGN_SIGNED << 2 | GPUSIGN_SIGNED << 4 | GPUSIGN_SIGNED << 6,
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/Xbox360D3D9/GPUSWIZZLE.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/Xbox360D3D9/GPUSWIZZLE.cs
new file mode 100644
index 0000000..a85f0c3
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/Xbox360D3D9/GPUSWIZZLE.cs
@@ -0,0 +1,49 @@
+namespace LibWindPop.Utils.Graphics.Texture.IGraphicsAPITexture.Xbox360D3D9
+{
+ ///
+ /// Describes how components of a texel are mapped to the elements of a register into which that texel is fetched.
+ ///
+ public enum GPUSWIZZLE
+ {
+ ///
+ /// Describes the X component of a register.
+ ///
+ GPUSWIZZLE_X = 0,
+ ///
+ /// Describes the Y component of a register.
+ ///
+ GPUSWIZZLE_Y = 1,
+ ///
+ /// Describes the Z component of a register.
+ ///
+ GPUSWIZZLE_Z = 2,
+ ///
+ /// Describes the W component of a register.
+ ///
+ GPUSWIZZLE_W = 3,
+ ///
+ /// Describes the 0 component of a register.
+ ///
+ GPUSWIZZLE_0 = 4,
+ ///
+ /// Describes the 1 component of a register.
+ ///
+ GPUSWIZZLE_1 = 5,
+ ///
+ /// NULL
+ ///
+ GPUSWIZZLE_KEEP = 7, // Fetch instructions only
+
+ GPUSWIZZLE_ARGB = GPUSWIZZLE_Z | GPUSWIZZLE_Y << 3 | GPUSWIZZLE_X << 6 | GPUSWIZZLE_W << 9,
+ GPUSWIZZLE_ORGB = GPUSWIZZLE_Z | GPUSWIZZLE_Y << 3 | GPUSWIZZLE_X << 6 | GPUSWIZZLE_1 << 9,
+ GPUSWIZZLE_ABGR = GPUSWIZZLE_X | GPUSWIZZLE_Y << 3 | GPUSWIZZLE_Z << 6 | GPUSWIZZLE_W << 9,
+ GPUSWIZZLE_OBGR = GPUSWIZZLE_X | GPUSWIZZLE_Y << 3 | GPUSWIZZLE_Z << 6 | GPUSWIZZLE_1 << 9,
+ GPUSWIZZLE_OOGR = GPUSWIZZLE_X | GPUSWIZZLE_Y << 3 | GPUSWIZZLE_1 << 6 | GPUSWIZZLE_1 << 9,
+ GPUSWIZZLE_OZGR = GPUSWIZZLE_X | GPUSWIZZLE_Y << 3 | GPUSWIZZLE_0 << 6 | GPUSWIZZLE_1 << 9,
+ GPUSWIZZLE_RZZZ = GPUSWIZZLE_0 | GPUSWIZZLE_0 << 3 | GPUSWIZZLE_0 << 6 | GPUSWIZZLE_X << 9,
+ GPUSWIZZLE_OOOR = GPUSWIZZLE_X | GPUSWIZZLE_1 << 3 | GPUSWIZZLE_1 << 6 | GPUSWIZZLE_1 << 9,
+ GPUSWIZZLE_ORRR = GPUSWIZZLE_X | GPUSWIZZLE_X << 3 | GPUSWIZZLE_X << 6 | GPUSWIZZLE_1 << 9,
+ GPUSWIZZLE_GRRR = GPUSWIZZLE_X | GPUSWIZZLE_X << 3 | GPUSWIZZLE_X << 6 | GPUSWIZZLE_Y << 9,
+ GPUSWIZZLE_RGBA = GPUSWIZZLE_W | GPUSWIZZLE_Z << 3 | GPUSWIZZLE_Y << 6 | GPUSWIZZLE_X << 9,
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/Xbox360D3D9/GPUTEXTUREFORMAT.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/Xbox360D3D9/GPUTEXTUREFORMAT.cs
new file mode 100644
index 0000000..9a4c32a
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/Xbox360D3D9/GPUTEXTUREFORMAT.cs
@@ -0,0 +1,265 @@
+namespace LibWindPop.Utils.Graphics.Texture.IGraphicsAPITexture.Xbox360D3D9
+{
+ ///
+ /// Describes the format of a texture surface.
+ ///
+ public enum GPUTEXTUREFORMAT
+ {
+ ///
+ /// Texture format with a single 1-bit channel.
+ ///
+ GPUTEXTUREFORMAT_1_REVERSE = 0,
+ ///
+ /// Texture format with a single 1-bit channel.
+ ///
+ GPUTEXTUREFORMAT_1 = 1,
+ ///
+ /// Texture format with a single 8 bit channel.
+ ///
+ GPUTEXTUREFORMAT_8 = 2,
+ ///
+ /// Texture format with a single 1-bit channel and three 5-bit channels.
+ ///
+ GPUTEXTUREFORMAT_1_5_5_5 = 3,
+ ///
+ /// Texture format with one 6-bit channel and two 5-bit channels.
+ ///
+ GPUTEXTUREFORMAT_5_6_5 = 4,
+ ///
+ /// Texture format with one 6-bit channel and two 5-bit channels.
+ ///
+ GPUTEXTUREFORMAT_6_5_5 = 5,
+ ///
+ /// Texture format with four 8-bit channels.
+ ///
+ GPUTEXTUREFORMAT_8_8_8_8 = 6,
+ ///
+ /// Texture format with a single 2-bit channel and three 10-bit channels.
+ ///
+ GPUTEXTUREFORMAT_2_10_10_10 = 7,
+ ///
+ /// Texture format with a single 8-bit channel.
+ ///
+ GPUTEXTUREFORMAT_8_A = 8,
+ ///
+ /// Texture format with a single 8-bit channel.
+ ///
+ GPUTEXTUREFORMAT_8_B = 9,
+ ///
+ /// Texture format with two 8-bit channels.
+ ///
+ GPUTEXTUREFORMAT_8_8 = 10,
+ ///
+ /// Texture format with two texels stored in each word. Each data value (Cr, Y1, Cb, Y0) is 8 bits. The Cr and Cb specify the chrominance and are shared by both texels. The Y0 and Y1 values specify the luminance of the respective texels.
+ ///
+ GPUTEXTUREFORMAT_Cr_Y1_Cb_Y0_REP = 11,
+ ///
+ /// Texture format with two texels stored in each word. Each data value (Y1, Cr, Y0, Cb) is 8 bits. The Cr and Cb specify the chrominance and are shared by both texels. The Y0 and Y1 values specify the luminance of the respective texels.
+ ///
+ GPUTEXTUREFORMAT_Y1_Cr_Y0_Cb_REP = 12,
+ ///
+ /// Format with two 16-bit channels. Format is not applicable to textures and only applies to render targets in EDRAM.
+ ///
+ GPUTEXTUREFORMAT_16_16_EDRAM = 13, // EDRAM render target only
+ ///
+ /// Texture format with four 8-bit channels.
+ ///
+ GPUTEXTUREFORMAT_8_8_8_8_A = 14,
+ ///
+ /// Texture format with four 4-bit channels.
+ ///
+ GPUTEXTUREFORMAT_4_4_4_4 = 15,
+ ///
+ /// Texture format with a single 10-bit channel and two 11-bit channels.
+ ///
+ GPUTEXTUREFORMAT_10_11_11 = 16,
+ ///
+ /// Texture format with a single 10-bit channel and two 11-bit channels.
+ ///
+ GPUTEXTUREFORMAT_11_11_10 = 17,
+ ///
+ /// DXT1 compression texture format with 4 bits per texel.
+ ///
+ GPUTEXTUREFORMAT_DXT1 = 18,
+ ///
+ /// DXT2 compression texture format with 8 bits per texel.
+ ///
+ GPUTEXTUREFORMAT_DXT2_3 = 19,
+ ///
+ /// DXT4 compression texture format with 8 bits per texel.
+ ///
+ GPUTEXTUREFORMAT_DXT4_5 = 20,
+ ///
+ /// Format with four 16-bit channels. Format is not applicable to textures and only applies to render targets in EDRAM.
+ ///
+ GPUTEXTUREFORMAT_16_16_16_16_EDRAM = 21, // EDRAM render target only
+ ///
+ /// Texture format with a 24-bit channel and an 8-bit channel. When applied to textures the 8-bit channel is ignored. When applied to depth-stencil targets the 8-bit channel maps to stencil.
+ ///
+ GPUTEXTUREFORMAT_24_8 = 22,
+ ///
+ /// Texture format with a 24-bit float channel and an 8-bit integer channel. When applied to textures the 8-bit channel is ignored. When applied to depth-stencil targets the 8-bit channel maps to stencil.
+ ///
+ GPUTEXTUREFORMAT_24_8_FLOAT = 23,
+ ///
+ /// Texture format with a single 16-bit channel.
+ ///
+ GPUTEXTUREFORMAT_16 = 24,
+ ///
+ /// Texture format with two 16-bit channels.
+ ///
+ GPUTEXTUREFORMAT_16_16 = 25,
+ ///
+ /// Texture format with four 16-bit channels.
+ ///
+ GPUTEXTUREFORMAT_16_16_16_16 = 26,
+ ///
+ /// 16-bit float texture format that is expanded to 32-bit (16.16) fixed point during sampling to allow filtering. This format samples at half the rate of GPUTEXTUREFORMAT_16.
+ ///
+ GPUTEXTUREFORMAT_16_EXPAND = 27,
+ ///
+ /// 32-bit float texture format that is expanded to 64-bit (16.16, 16.16) fixed point during sampling to allow filtering. This format samples at half the rate of GPUTEXTUREFORMAT_16_16.
+ ///
+ GPUTEXTUREFORMAT_16_16_EXPAND = 28,
+ ///
+ /// 64-bit float texture format that is expanded to 128-bit (16.16, 16.16, 16.16, 16.16) fixed point during sampling to allow filtering. This format samples at half the rate of GPUTEXTUREFORMAT_16_16_16_16.
+ ///
+ GPUTEXTUREFORMAT_16_16_16_16_EXPAND = 29,
+ ///
+ /// Texture format with a single 16-bit float channel.
+ ///
+ GPUTEXTUREFORMAT_16_FLOAT = 30,
+ ///
+ /// Texture format with two 16-bit float channels.
+ ///
+ GPUTEXTUREFORMAT_16_16_FLOAT = 31,
+ ///
+ /// Texture format with four 16-bit float channels.
+ ///
+ GPUTEXTUREFORMAT_16_16_16_16_FLOAT = 32,
+ ///
+ /// Texture format with a single 32-bit channel.
+ ///
+ GPUTEXTUREFORMAT_32 = 33,
+ ///
+ /// Texture format with two 32-bit channels.
+ ///
+ GPUTEXTUREFORMAT_32_32 = 34,
+ ///
+ /// Texture format with four 32-bit channels.
+ ///
+ GPUTEXTUREFORMAT_32_32_32_32 = 35,
+ ///
+ /// Texture format with a single 32-bit float channel.
+ ///
+ GPUTEXTUREFORMAT_32_FLOAT = 36,
+ ///
+ /// Texture format with two 32-bit float channels.
+ ///
+ GPUTEXTUREFORMAT_32_32_FLOAT = 37,
+ ///
+ /// Texture format with four 32-bit float channels.
+ ///
+ GPUTEXTUREFORMAT_32_32_32_32_FLOAT = 38,
+ ///
+ /// Texture format with a single 32-bit channel that is stored as an 8-bit channel in the texture cache.
+ ///
+ GPUTEXTUREFORMAT_32_AS_8 = 39,
+ ///
+ /// Texture format with a single 32-bit channel that is stored as two 8-bit channels in the texture cache.
+ ///
+ GPUTEXTUREFORMAT_32_AS_8_8 = 40,
+ ///
+ /// Texture format with a single 16-bit channel.
+ ///
+ GPUTEXTUREFORMAT_16_MPEG = 41,
+ ///
+ /// Texture format with two 16-bit channels.
+ ///
+ GPUTEXTUREFORMAT_16_16_MPEG = 42,
+ ///
+ /// Texture format with a single 8-bit channel.
+ ///
+ GPUTEXTUREFORMAT_8_INTERLACED = 43,
+ ///
+ /// Texture format with a single 32-bit channel that is stored as an 8-bit channel in the texture cache.
+ ///
+ GPUTEXTUREFORMAT_32_AS_8_INTERLACED = 44,
+ ///
+ /// Texture format with a single 32-bit channel that is stored as two 8-bit channels in the texture cache.
+ ///
+ GPUTEXTUREFORMAT_32_AS_8_8_INTERLACED = 45,
+ ///
+ /// Texture format with a single 16-bit channel.
+ ///
+ GPUTEXTUREFORMAT_16_INTERLACED = 46,
+ ///
+ /// Texture format with a single 16-bit channel.
+ ///
+ GPUTEXTUREFORMAT_16_MPEG_INTERLACED = 47,
+ ///
+ /// Texture format with two 16-bit channels.
+ ///
+ GPUTEXTUREFORMAT_16_16_MPEG_INTERLACED = 48,
+ ///
+ /// Two-component 8-bit per texel format made up of two DXT4/5 alpha blocks.
+ ///
+ GPUTEXTUREFORMAT_DXN = 49,
+ ///
+ /// GPUTEXTUREFORMAT_8_8_8_8 texture format that is stored as four 16-bit channels in the texture cache.
+ ///
+ GPUTEXTUREFORMAT_8_8_8_8_AS_16_16_16_16 = 50,
+ ///
+ /// GPUTEXTUREFORMAT_DXT1 texture format that is stored as four 16-bit channels in the texture cache.
+ ///
+ GPUTEXTUREFORMAT_DXT1_AS_16_16_16_16 = 51,
+ ///
+ /// GPUTEXTUREFORMAT_DXT2_3 texture format that is stored as four 16-bit channels in the texture cache.
+ ///
+ GPUTEXTUREFORMAT_DXT2_3_AS_16_16_16_16 = 52,
+ ///
+ /// GPUTEXTUREFORMAT_DXT4_5 texture format that is stored as four 16-bit channels in the texture cache.
+ ///
+ GPUTEXTUREFORMAT_DXT4_5_AS_16_16_16_16 = 53,
+ ///
+ /// GPUTEXTUREFORMAT_2_10_10_10 texture format that is stored as four 16-bit channels in the texture cache.
+ ///
+ GPUTEXTUREFORMAT_2_10_10_10_AS_16_16_16_16 = 54,
+ ///
+ /// GPUTEXTUREFORMAT_10_11_11 texture format that is stored as four 16-bit channels in the texture cache.
+ ///
+ GPUTEXTUREFORMAT_10_11_11_AS_16_16_16_16 = 55,
+ ///
+ /// GPUTEXTUREFORMAT_11_11_10 texture format that is stored as four 16-bit channels in the texture cache.
+ ///
+ GPUTEXTUREFORMAT_11_11_10_AS_16_16_16_16 = 56,
+ ///
+ /// Texture format with three 32-bit float channels.
+ ///
+ GPUTEXTUREFORMAT_32_32_32_FLOAT = 57,
+ ///
+ /// Single-component 4-bit per texel format made up of a DXT2/3 alpha block.
+ ///
+ GPUTEXTUREFORMAT_DXT3A = 58,
+ ///
+ /// Single-component 4-bit per texel format made up of a DXT4/5 alpha block.
+ ///
+ GPUTEXTUREFORMAT_DXT5A = 59,
+ ///
+ /// Two-component 4-bit per texel format similar to DXT1 but with 8:8 colors instead of 5:6:5 colors.
+ ///
+ GPUTEXTUREFORMAT_CTX1 = 60,
+ ///
+ /// Four-component format encoded in a DXT2/3 alpha block where each bit is expanded into a separate channel.
+ ///
+ GPUTEXTUREFORMAT_DXT3A_AS_1_1_1_1 = 61,
+ ///
+ /// Format with four 8-bit channels. Format is not applicable to textures and only applies to render targets in EDRAM.
+ ///
+ GPUTEXTUREFORMAT_8_8_8_8_GAMMA_EDRAM = 62, // EDRAM render target only
+ ///
+ /// Format with a single 2-bit float channel and three 10-bit float channels. Format is not applicable to textures and only applies to render targets in EDRAM.
+ ///
+ GPUTEXTUREFORMAT_2_10_10_10_FLOAT_EDRAM = 63, // EDRAM render target only
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/Xbox360D3D9/IXbox360D3D9Texture.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/Xbox360D3D9/IXbox360D3D9Texture.cs
new file mode 100644
index 0000000..d466cf9
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IGraphicsAPITexture/Xbox360D3D9/IXbox360D3D9Texture.cs
@@ -0,0 +1,10 @@
+namespace LibWindPop.Utils.Graphics.Texture.IGraphicsAPITexture.Xbox360D3D9
+{
+ ///
+ /// HRESULT CreateTexture(UINT Width, UINT Height, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL UnusedPool, IDirect3DTexture9 **ppTexture, HANDLE *pUnusedSharedHandle)
+ ///
+ public interface IXbox360D3D9Texture
+ {
+ static abstract D3DFORMAT Xbox360D3D9Format { get; }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IPitchableTextureCoder.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IPitchableTextureCoder.cs
new file mode 100644
index 0000000..18e6784
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/IPitchableTextureCoder.cs
@@ -0,0 +1,31 @@
+using LibWindPop.Utils.Graphics.Bitmap;
+using System;
+
+namespace LibWindPop.Utils.Graphics.Texture
+{
+ ///
+ /// Describe a texture decoder/encoder
+ ///
+ public interface IPitchableTextureCoder : ITextureCoder
+ {
+ ///
+ /// Decode Texture Data as BGRA8888 format
+ ///
+ /// Texture Data
+ /// Texture Width
+ /// Texture Height
+ /// Texture Pitch
+ /// Bitmap to save BGRA8888 data
+ void Decode(ReadOnlySpan src_data, int width, int height, int pitch, RefBitmap dst_bitmap);
+
+ ///
+ /// Encode BGRA8888 data as Texture format
+ ///
+ /// Bitmap with BGRA8888 data
+ /// Texture Data
+ /// Texture Width
+ /// Texture Height
+ /// Texture Pitch
+ void Encode(RefBitmap src_bitmap, Span dst_data, int width, int height, int pitch);
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/ITextureCoder.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/ITextureCoder.cs
new file mode 100644
index 0000000..a61bcc0
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/ITextureCoder.cs
@@ -0,0 +1,29 @@
+using LibWindPop.Utils.Graphics.Bitmap;
+using System;
+
+namespace LibWindPop.Utils.Graphics.Texture
+{
+ ///
+ /// Describe a texture decoder/encoder
+ ///
+ public interface ITextureCoder
+ {
+ ///
+ /// Decode Texture Data as BGRA8888 format
+ ///
+ /// Texture Data
+ /// Texture Width
+ /// Texture Height
+ /// Bitmap to save BGRA8888 data
+ void Decode(ReadOnlySpan src_data, int width, int height, RefBitmap dst_bitmap);
+
+ ///
+ /// Encode BGRA8888 data as Texture format
+ ///
+ /// Bitmap with BGRA8888 data
+ /// Texture Data
+ /// Texture Width
+ /// Texture Height
+ void Encode(RefBitmap src_bitmap, Span dst_data, int width, int height);
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Shared/ATCCoder.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Shared/ATCCoder.cs
new file mode 100644
index 0000000..e105613
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Shared/ATCCoder.cs
@@ -0,0 +1,146 @@
+using System;
+using System.Buffers.Binary;
+
+namespace LibWindPop.Utils.Graphics.Texture.Shared
+{
+ internal static unsafe class ATCCoder
+ {
+ public static void DecodeATCBlock(ReadOnlySpan atcword, Span decode_data)
+ {
+ ATCDecoder.DecodeColorWord(atcword, decode_data);
+ }
+
+ public static void EncodeATCBlock(Span atcword, ReadOnlySpan encode_data)
+ {
+ // ATCDecoder.DecodeColorWord(atcword.Slice(8, 8), decode_data, false);
+ }
+
+ public static void DecodeATCBlockWithExplicitAlpha(ReadOnlySpan atcword, Span decode_data)
+ {
+ ATCDecoder.DecodeColorWord(atcword.Slice(8, 8), decode_data);
+ ATCDecoder.ModifyExplicitAlphaWord(atcword[..8], decode_data);
+ }
+
+ public static void EncodeATCBlockWithExplicitAlpha(Span atcword, ReadOnlySpan encode_data)
+ {
+ // ATCDecoder.DecodeColorWord(atcword.Slice(8, 8), decode_data, false);
+ }
+
+ public static void DecodeATCBlockWithInterpolatedAlpha(ReadOnlySpan atcword, Span decode_data)
+ {
+ ATCDecoder.DecodeColorWord(atcword.Slice(8, 8), decode_data);
+ ATCDecoder.ModifyInterpolatedAlphaWord(atcword[..8], decode_data);
+ }
+
+ public static void EncodeATCBlockWithInterpolatedAlpha(Span atcword, ReadOnlySpan encode_data)
+ {
+ // ATCDecoder.DecodeColorWord(atcword.Slice(8, 8), decode_data, false);
+ }
+
+ private static class ATCDecoder
+ {
+ public static void ModifyExplicitAlphaWord(ReadOnlySpan texPtr, Span color)
+ {
+ ulong buf = BinaryPrimitives.ReadUInt64BigEndian(texPtr);
+ ulong a;
+ for (int i = 0; i < 16; i++)
+ {
+ a = buf & 0xF;
+ buf >>= 4;
+ color[i].Alpha = (byte)BitHelper.FourBitToEightBit(a);
+ }
+ }
+
+ public static void ModifyInterpolatedAlphaWord(ReadOnlySpan texPtr, Span color)
+ {
+ byte* alpha_buffer = stackalloc byte[8];
+ ulong alpha_flags = BinaryPrimitives.ReadUInt64LittleEndian(texPtr[..8]) >> 16;
+ alpha_buffer[0] = texPtr[0];
+ alpha_buffer[1] = texPtr[1];
+ if (alpha_buffer[0] > alpha_buffer[1])
+ {
+ alpha_buffer[2] = (byte)((6 * alpha_buffer[0] + 1 * alpha_buffer[1] + 3) / 7);
+ alpha_buffer[3] = (byte)((5 * alpha_buffer[0] + 2 * alpha_buffer[1] + 3) / 7);
+ alpha_buffer[4] = (byte)((4 * alpha_buffer[0] + 3 * alpha_buffer[1] + 3) / 7);
+ alpha_buffer[5] = (byte)((3 * alpha_buffer[0] + 4 * alpha_buffer[1] + 3) / 7);
+ alpha_buffer[6] = (byte)((2 * alpha_buffer[0] + 5 * alpha_buffer[1] + 3) / 7);
+ alpha_buffer[7] = (byte)((1 * alpha_buffer[0] + 6 * alpha_buffer[1] + 3) / 7);
+ }
+ else
+ {
+ alpha_buffer[2] = (byte)((4 * alpha_buffer[0] + 1 * alpha_buffer[1] + 2) / 5);
+ alpha_buffer[3] = (byte)((3 * alpha_buffer[0] + 2 * alpha_buffer[1] + 2) / 5);
+ alpha_buffer[4] = (byte)((2 * alpha_buffer[0] + 3 * alpha_buffer[1] + 2) / 5);
+ alpha_buffer[5] = (byte)((1 * alpha_buffer[0] + 4 * alpha_buffer[1] + 2) / 5);
+ alpha_buffer[6] = 0x0;
+ alpha_buffer[7] = 0xFF;
+ }
+ for (int i = 0; i < 16; i++)
+ {
+ color[i].Alpha = alpha_buffer[alpha_flags & 0x7];
+ alpha_flags >>= 3;
+ }
+ }
+
+ public static void DecodeColorWord(ReadOnlySpan texPtr, Span color)
+ {
+ YFColor* color_buffer = stackalloc YFColor[4];
+ ushort c0 = BinaryPrimitives.ReadUInt16LittleEndian(texPtr[..2]);
+ ushort c1 = BinaryPrimitives.ReadUInt16LittleEndian(texPtr.Slice(2, 2));
+ uint color_flags = BinaryPrimitives.ReadUInt32LittleEndian(texPtr.Slice(4, 4));
+
+ if ((c0 & 0x8000) != 0)
+ {
+ // color2 = c0
+ color_buffer[2].Red = (byte)BitHelper.FiveBitToEightBit(c0 >> 10);
+ color_buffer[2].Green = (byte)BitHelper.FiveBitToEightBit(c0 >> 5);
+ color_buffer[2].Blue = (byte)BitHelper.FiveBitToEightBit(c0);
+ color_buffer[2].Alpha = 0xFF;
+ // color3 = c1
+ color_buffer[3].Red = (byte)BitHelper.FiveBitToEightBit(c1 >> 11);
+ color_buffer[3].Green = (byte)BitHelper.SixBitToEightBit(c1 >> 5);
+ color_buffer[3].Blue = (byte)BitHelper.FiveBitToEightBit(c1);
+ color_buffer[3].Alpha = 0xFF;
+ // color0 = 0
+ color_buffer[0].Red = 0x0;
+ color_buffer[0].Green = 0x0;
+ color_buffer[0].Blue = 0x0;
+ color_buffer[0].Alpha = 0xFF;
+ // color1 = ?
+ color_buffer[1].Red = (byte)(Math.Abs((color_buffer[2].Red << 2) - color_buffer[3].Red) >> 2);
+ color_buffer[1].Green = (byte)(Math.Abs((color_buffer[2].Green << 2) - color_buffer[3].Green) >> 2);
+ color_buffer[1].Blue = (byte)(Math.Abs((color_buffer[2].Blue << 2) - color_buffer[3].Blue) >> 2);
+ color_buffer[1].Alpha = 0xFF;
+ }
+ else
+ {
+ // color0 = c0
+ color_buffer[0].Red = (byte)BitHelper.FiveBitToEightBit(c0 >> 10);
+ color_buffer[0].Green = (byte)BitHelper.FiveBitToEightBit(c0 >> 5);
+ color_buffer[0].Blue = (byte)BitHelper.FiveBitToEightBit(c0);
+ color_buffer[0].Alpha = 0xFF;
+ // color3 = c1
+ color_buffer[3].Red = (byte)BitHelper.FiveBitToEightBit(c1 >> 11);
+ color_buffer[3].Green = (byte)BitHelper.SixBitToEightBit(c1 >> 5);
+ color_buffer[3].Blue = (byte)BitHelper.FiveBitToEightBit(c1);
+ color_buffer[3].Alpha = 0xFF;
+ // color1 = (c0 * 5 + c1 * 3) / 8
+ color_buffer[1].Red = (byte)((color_buffer[0].Red * 5 + color_buffer[3].Red * 3 + 4) >> 3);
+ color_buffer[1].Green = (byte)((color_buffer[0].Green * 5 + color_buffer[3].Green * 3 + 4) >> 3);
+ color_buffer[1].Blue = (byte)((color_buffer[0].Blue * 5 + color_buffer[3].Blue * 3 + 4) >> 3);
+ color_buffer[1].Alpha = 0xFF;
+ // color2 = (c0 * 3 + c1 * 5) / 8
+ color_buffer[2].Red = (byte)((color_buffer[0].Red * 3 + color_buffer[3].Red * 5 + 4) >> 3);
+ color_buffer[2].Green = (byte)((color_buffer[0].Green * 3 + color_buffer[3].Green * 5 + 4) >> 3);
+ color_buffer[2].Blue = (byte)((color_buffer[0].Blue * 3 + color_buffer[3].Blue * 5 + 4) >> 3);
+ color_buffer[2].Alpha = 0xFF;
+ }
+ for (int i = 0; i < 16; i++)
+ {
+ color[i] = color_buffer[color_flags & 0x3];
+ color_flags >>= 2;
+ }
+ }
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Shared/BCCoder.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Shared/BCCoder.cs
new file mode 100644
index 0000000..626a89a
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Shared/BCCoder.cs
@@ -0,0 +1,1462 @@
+using System;
+using System.Buffers.Binary;
+using System.Runtime.InteropServices;
+
+namespace LibWindPop.Utils.Graphics.Texture.Shared
+{
+ internal static unsafe class BCCoder
+ {
+ public static void DecodeBC7Block(ReadOnlySpan bc7Word, Span decodeData)
+ {
+ fixed (byte* texData = bc7Word)
+ {
+ fixed (YFColor* colorData = decodeData)
+ {
+ BC6To7Decoder.DecodeBC7Block(texData, colorData);
+ }
+ }
+ }
+
+ public static void EncodeBC7Block(ReadOnlySpan bc7Word, Span decodeData)
+ {
+ throw new NotImplementedException();
+ }
+
+ public static void DecodeBC6HUnsignedBlock(ReadOnlySpan bc6hWord, Span decodeData)
+ {
+ fixed (byte* texData = bc6hWord)
+ {
+ fixed (YFColor* colorData = decodeData)
+ {
+ BC6To7Decoder.DecodeBC6Block(texData, colorData, false);
+ }
+ }
+ }
+
+ public static void EncodeBC6HUnsignedBlock(ReadOnlySpan bc6hWord, Span decodeData)
+ {
+ throw new NotImplementedException();
+ }
+
+ public static void DecodeBC6HSignedBlock(ReadOnlySpan bc6hWord, Span decodeData)
+ {
+ fixed (byte* texData = bc6hWord)
+ {
+ fixed (YFColor* colorData = decodeData)
+ {
+ BC6To7Decoder.DecodeBC6Block(texData, colorData, true);
+ }
+ }
+ }
+
+ public static void EncodeBC6HSignedBlock(ReadOnlySpan bc6hWord, Span decodeData)
+ {
+ throw new NotImplementedException();
+ }
+
+ public static void DecodeBC3Block(ReadOnlySpan dxt5word, Span decode_data)
+ {
+ BC1To5Decoder.DecodeColorWord(dxt5word.Slice(8, 8), decode_data, false);
+ BC1To5Decoder.ModifyInterpolatedAlphaWord(dxt5word[..8], decode_data);
+ }
+
+ public static void EncodeBC3Block(Span dxt5word, ReadOnlySpan encode_data)
+ {
+ BC1To5Encoder.EncodeColorWord(dxt5word.Slice(8, 8), encode_data, false);
+ BC1To5Encoder.EncodeInterpolatedAlphaWord(dxt5word[..8], encode_data);
+ }
+
+ public static void DecodeBC2Block(ReadOnlySpan dxt3word, Span decode_data)
+ {
+ BC1To5Decoder.DecodeColorWord(dxt3word.Slice(8, 8), decode_data, false);
+ BC1To5Decoder.ModifyExplicitAlphaWord(dxt3word[..8], decode_data);
+ }
+
+ public static void EncodeBC2Block(Span dxt3word, ReadOnlySpan encode_data)
+ {
+ BC1To5Encoder.EncodeColorWord(dxt3word.Slice(8, 8), encode_data, false);
+ BC1To5Encoder.EncodeExplicitAlphaWord(dxt3word[..8], encode_data);
+ }
+
+ public static void DecodeBC1Block(ReadOnlySpan dxt1word, Span decode_data)
+ {
+ BC1To5Decoder.DecodeColorWord(dxt1word, decode_data, false);
+ }
+
+ public static void EncodeBC1Block(Span dxt1word, ReadOnlySpan encode_data)
+ {
+ BC1To5Encoder.EncodeColorWord(dxt1word, encode_data, false);
+ }
+
+ public static void DecodeBC1BlockWithAlpha(ReadOnlySpan dxt1word, Span decode_data)
+ {
+ BC1To5Decoder.DecodeColorWord(dxt1word, decode_data, true);
+ }
+
+ public static void EncodeBC1BlockWithAlpha(Span dxt1word, ReadOnlySpan encode_data)
+ {
+ BC1To5Encoder.EncodeColorWord(dxt1word, encode_data, true);
+ }
+
+ ///
+ /// 用于快速编码DXT的类
+ ///
+ private static class BC1To5Encoder
+ {
+ ///
+ /// 编码DXT4/5中线性插值的Alpha通道
+ ///
+ ///
+ ///
+ public static void EncodeInterpolatedAlphaWord(Span texPtr, ReadOnlySpan color)
+ {
+ int maxAlpha = 0x0;
+ int minAlpha = 0xFF;
+ for (int i = 0; i < 16; i++)
+ {
+ if (color[i].Alpha < minAlpha) minAlpha = color[i].Alpha;
+ if (color[i].Alpha > maxAlpha) maxAlpha = color[i].Alpha;
+ }
+ int inset = (maxAlpha - minAlpha) >> 4;
+ minAlpha += inset;
+ maxAlpha -= inset;
+ if (minAlpha == maxAlpha)
+ {
+ BinaryPrimitives.WriteUInt64LittleEndian(texPtr, (ulong)(maxAlpha | (minAlpha << 8)));
+ }
+ else
+ {
+ byte* alphas = stackalloc byte[8];
+ alphas[0] = (byte)maxAlpha;
+ alphas[1] = (byte)minAlpha;
+ alphas[2] = (byte)((6 * maxAlpha + minAlpha) / 7);
+ alphas[3] = (byte)((5 * maxAlpha + (minAlpha << 1)) / 7);
+ alphas[4] = (byte)(((maxAlpha << 2) + 3 * minAlpha) / 7);
+ alphas[5] = (byte)((3 * maxAlpha + (minAlpha << 2)) / 7);
+ alphas[6] = (byte)(((maxAlpha << 1) + 5 * minAlpha) / 7);
+ alphas[7] = (byte)((maxAlpha + 6 * minAlpha) / 7);
+ ulong indices = 0;
+ byte buffer = 0;
+ for (int i = 15; i >= 0; i--)
+ {
+ int minDistance = int.MaxValue;
+ byte a = color[i].Alpha;
+ for (byte j = 0; j < 8; j++)
+ {
+ int dist = a - alphas[j];
+ if (dist < 0) dist = -dist;
+ if (dist < minDistance)
+ {
+ minDistance = dist;
+ buffer = j;
+ }
+ }
+ indices <<= 3;
+ indices |= buffer;
+ }
+ indices <<= 16;
+ indices |= (uint)maxAlpha | ((uint)minAlpha << 8);
+ BinaryPrimitives.WriteUInt64LittleEndian(texPtr, indices);
+ }
+ }
+
+ ///
+ /// 给DXT2/3用的编码A4
+ ///
+ ///
+ ///
+ public static void EncodeExplicitAlphaWord(Span texPtr, ReadOnlySpan color)
+ {
+ uint buf = 0u;
+ for (int i = 7; i >= 0; i--)
+ {
+ buf <<= 4;
+ buf |= (uint)BitHelper.EightBitToFourBit(color[i].Alpha);
+ }
+ BinaryPrimitives.WriteUInt32LittleEndian(texPtr[..4], buf);
+ buf = 0u;
+ for (int i = 15; i >= 8; i--)
+ {
+ buf <<= 4;
+ buf |= (uint)BitHelper.EightBitToFourBit(color[i].Alpha);
+ }
+ BinaryPrimitives.WriteUInt32LittleEndian(texPtr.Slice(4, 4), buf);
+ }
+
+ public static void EncodeColorWord(Span texPtr, ReadOnlySpan color, bool alpha)
+ {
+ // check alpha
+ if (alpha)
+ {
+ alpha = false;
+ for (int i = 0; i < 16; i++)
+ {
+ if ((color[i].Alpha & 0x80) == 0)
+ {
+ alpha = true;
+ break;
+ }
+ }
+ }
+ // Get min max color
+ YFColor minColor, maxColor;
+ YFColor inset;
+ minColor.Red = 255;
+ minColor.Green = 255;
+ minColor.Blue = 255;
+ minColor.Alpha = 255;
+ maxColor.Red = 0;
+ maxColor.Green = 0;
+ maxColor.Blue = 0;
+ maxColor.Alpha = 255;
+ for (int i = 0; i < 16; i++)
+ {
+ if (color[i].Red < minColor.Red) minColor.Red = color[i].Red;
+ if (color[i].Green < minColor.Green) minColor.Green = color[i].Green;
+ if (color[i].Blue < minColor.Blue) minColor.Blue = color[i].Blue;
+ if (color[i].Red > maxColor.Red) maxColor.Red = color[i].Red;
+ if (color[i].Green > maxColor.Green) maxColor.Green = color[i].Green;
+ if (color[i].Blue > maxColor.Blue) maxColor.Blue = color[i].Blue;
+ }
+ inset.Red = (byte)((maxColor.Red - minColor.Red) >> 4);
+ inset.Green = (byte)((maxColor.Green - minColor.Green) >> 4);
+ inset.Blue = (byte)((maxColor.Blue - minColor.Blue) >> 4);
+ minColor.Red += inset.Red;
+ minColor.Green += inset.Green;
+ minColor.Blue += inset.Blue;
+ maxColor.Red -= inset.Red;
+ maxColor.Green -= inset.Green;
+ maxColor.Blue -= inset.Blue;
+ // get rgb565-like rgb888 color
+ minColor.Red = (byte)((minColor.Red & 0xF8) | (minColor.Red >> 5));
+ minColor.Green = (byte)((minColor.Green & 0xFC) | (minColor.Green >> 6));
+ minColor.Blue = (byte)((minColor.Blue & 0xF8) | (minColor.Blue >> 5));
+ maxColor.Red = (byte)((maxColor.Red & 0xF8) | (maxColor.Red >> 5));
+ maxColor.Green = (byte)((maxColor.Green & 0xFC) | (maxColor.Green >> 6));
+ maxColor.Blue = (byte)((maxColor.Blue & 0xF8) | (maxColor.Blue >> 5));
+ // Get index
+ uint color_flags = 0;
+ ushort c0, c1;
+ if (alpha)
+ {
+ c0 = (ushort)((minColor.Red >> 3 << 11) | (minColor.Green >> 2 << 5) | (minColor.Blue >> 3));
+ c1 = (ushort)((maxColor.Red >> 3 << 11) | (maxColor.Green >> 2 << 5) | (maxColor.Blue >> 3));
+ Span color_buffer = stackalloc YFColor[3];
+ color_buffer[0].Red = minColor.Red;
+ color_buffer[0].Green = minColor.Green;
+ color_buffer[0].Blue = minColor.Blue;
+ color_buffer[0].Alpha = 0xFF;
+ color_buffer[1].Red = maxColor.Red;
+ color_buffer[1].Green = maxColor.Green;
+ color_buffer[1].Blue = maxColor.Blue;
+ color_buffer[1].Alpha = 0xFF;
+ color_buffer[2].Red = (byte)((minColor.Red + maxColor.Red) >> 1);
+ color_buffer[2].Green = (byte)((minColor.Green + maxColor.Green) >> 1);
+ color_buffer[2].Blue = (byte)((minColor.Blue + maxColor.Blue) >> 1);
+ color_buffer[2].Alpha = 0xFF;
+ for (int i = 15; i >= 0; i--)
+ {
+ int index = 0;
+ if ((color[i].Alpha & 0x80) == 0)
+ {
+ index = 3;
+ }
+ else
+ {
+ int minDiff = int.MaxValue;
+ for (int j = 0; j < 3; j++)
+ {
+ int delta_red = color_buffer[j].Red - color[i].Red;
+ if (delta_red < 0)
+ {
+ delta_red = -delta_red;
+ }
+ int delta_green = color_buffer[j].Green - color[i].Green;
+ if (delta_green < 0)
+ {
+ delta_green = -delta_green;
+ }
+ int delta_blue = color_buffer[j].Blue - color[i].Blue;
+ if (delta_blue < 0)
+ {
+ delta_blue = -delta_blue;
+ }
+ int diff = delta_red + delta_green + delta_blue;
+ if (diff < minDiff)
+ {
+ minDiff = diff;
+ index = j;
+ }
+ }
+ }
+ color_flags <<= 2;
+ color_flags |= (uint)index;
+ }
+ }
+ else
+ {
+ c0 = (ushort)((maxColor.Red >> 3 << 11) | (maxColor.Green >> 2 << 5) | (maxColor.Blue >> 3));
+ c1 = (ushort)((minColor.Red >> 3 << 11) | (minColor.Green >> 2 << 5) | (minColor.Blue >> 3));
+ Span color_buffer = stackalloc YFColor[4];
+ color_buffer[0].Red = maxColor.Red;
+ color_buffer[0].Green = maxColor.Green;
+ color_buffer[0].Blue = maxColor.Blue;
+ color_buffer[0].Alpha = 0xFF;
+ color_buffer[1].Red = minColor.Red;
+ color_buffer[1].Green = minColor.Green;
+ color_buffer[1].Blue = minColor.Blue;
+ color_buffer[1].Alpha = 0xFF;
+ color_buffer[2].Red = (byte)(((maxColor.Red << 1) + minColor.Red) / 3);
+ color_buffer[2].Green = (byte)(((maxColor.Green << 1) + minColor.Green) / 3);
+ color_buffer[2].Blue = (byte)(((maxColor.Blue << 1) + minColor.Blue) / 3);
+ color_buffer[2].Alpha = 0xFF;
+ color_buffer[3].Red = (byte)((maxColor.Red + (minColor.Red << 1)) / 3);
+ color_buffer[3].Green = (byte)((maxColor.Green + (minColor.Green << 1)) / 3);
+ color_buffer[3].Blue = (byte)((maxColor.Blue + (minColor.Blue << 1)) / 3);
+ color_buffer[3].Alpha = 0xFF;
+ for (int i = 15; i >= 0; i--)
+ {
+ int index = 0;
+ int minDiff = int.MaxValue;
+ for (int j = 0; j < 4; j++)
+ {
+ int delta_red = color_buffer[j].Red - color[i].Red;
+ if (delta_red < 0)
+ {
+ delta_red = -delta_red;
+ }
+ int delta_green = color_buffer[j].Green - color[i].Green;
+ if (delta_green < 0)
+ {
+ delta_green = -delta_green;
+ }
+ int delta_blue = color_buffer[j].Blue - color[i].Blue;
+ if (delta_blue < 0)
+ {
+ delta_blue = -delta_blue;
+ }
+ int diff = delta_red + delta_green + delta_blue;
+ if (diff < minDiff)
+ {
+ minDiff = diff;
+ index = j;
+ }
+ }
+ color_flags <<= 2;
+ color_flags |= (uint)index;
+ }
+ }
+ BinaryPrimitives.WriteUInt16LittleEndian(texPtr[..2], c0);
+ BinaryPrimitives.WriteUInt16LittleEndian(texPtr.Slice(2, 2), c1);
+ BinaryPrimitives.WriteUInt32LittleEndian(texPtr.Slice(4, 4), color_flags);
+ }
+ }
+
+ private static class BC1To5Decoder
+ {
+ public static void ModifyExplicitAlphaWord(ReadOnlySpan texPtr, Span color)
+ {
+ ulong buf = BinaryPrimitives.ReadUInt64BigEndian(texPtr);
+ for (int i = 0; i < 16; i++)
+ {
+ color[i].Alpha = (byte)BitHelper.FourBitToEightBit(buf);
+ buf >>= 4;
+ }
+ }
+
+ public static void ModifyInterpolatedAlphaWord(ReadOnlySpan texPtr, Span color)
+ {
+ Span alpha_buffer = stackalloc byte[8];
+ ulong alpha_flags = BinaryPrimitives.ReadUInt64LittleEndian(texPtr[..8]) >> 16;
+ alpha_buffer[0] = texPtr[0];
+ alpha_buffer[1] = texPtr[1];
+ if (alpha_buffer[0] > alpha_buffer[1])
+ {
+ alpha_buffer[2] = (byte)((6 * alpha_buffer[0] + alpha_buffer[1]) / 7);
+ alpha_buffer[3] = (byte)((5 * alpha_buffer[0] + (alpha_buffer[1] << 1)) / 7);
+ alpha_buffer[4] = (byte)(((alpha_buffer[0] << 2) + 3 * alpha_buffer[1]) / 7);
+ alpha_buffer[5] = (byte)((3 * alpha_buffer[0] + (alpha_buffer[1] << 2)) / 7);
+ alpha_buffer[6] = (byte)(((alpha_buffer[0] << 1) + 5 * alpha_buffer[1]) / 7);
+ alpha_buffer[7] = (byte)((alpha_buffer[0] + 6 * alpha_buffer[1]) / 7);
+ }
+ else
+ {
+ alpha_buffer[2] = (byte)(((alpha_buffer[0] << 2) + alpha_buffer[1]) / 5);
+ alpha_buffer[3] = (byte)((3 * alpha_buffer[0] + (alpha_buffer[1] << 1)) / 5);
+ alpha_buffer[4] = (byte)(((alpha_buffer[0] << 1) + 3 * alpha_buffer[1]) / 5);
+ alpha_buffer[5] = (byte)((alpha_buffer[0] + (alpha_buffer[1] << 2)) / 5);
+ alpha_buffer[6] = 0x0;
+ alpha_buffer[7] = 0xFF;
+ }
+ for (int i = 0; i < 16; i++)
+ {
+ color[i].Alpha = alpha_buffer[(int)(alpha_flags & 7ul)];
+ alpha_flags >>= 3;
+ }
+ }
+
+ public static void DecodeColorWord(ReadOnlySpan texPtr, Span color, bool alpha)
+ {
+ Span color_buffer = stackalloc YFColor[4];
+ ushort c0 = BinaryPrimitives.ReadUInt16LittleEndian(texPtr[..2]);
+ ushort c1 = BinaryPrimitives.ReadUInt16LittleEndian(texPtr.Slice(2, 2));
+ uint color_flags = BinaryPrimitives.ReadUInt32LittleEndian(texPtr.Slice(4, 4));
+ // color0 = c0
+ color_buffer[0].Red = (byte)BitHelper.FiveBitToEightBit(c0 >> 11);
+ color_buffer[0].Green = (byte)BitHelper.SixBitToEightBit(c0 >> 5);
+ color_buffer[0].Blue = (byte)BitHelper.FiveBitToEightBit(c0);
+ color_buffer[0].Alpha = 0xFF;
+ // color1 = c1
+ color_buffer[1].Red = (byte)BitHelper.FiveBitToEightBit(c1 >> 11);
+ color_buffer[1].Green = (byte)BitHelper.SixBitToEightBit(c1 >> 5);
+ color_buffer[1].Blue = (byte)BitHelper.FiveBitToEightBit(c1);
+ color_buffer[1].Alpha = 0xFF;
+ if (c0 > c1)
+ {
+ // color2 = 2 / 3 * c0 + 1 / 3 * c1
+ color_buffer[2].Red = (byte)(((color_buffer[0].Red << 1) + color_buffer[1].Red + 1) / 3);
+ color_buffer[2].Green = (byte)(((color_buffer[0].Green << 1) + color_buffer[1].Green + 1) / 3);
+ color_buffer[2].Blue = (byte)(((color_buffer[0].Blue << 1) + color_buffer[1].Blue + 1) / 3);
+ color_buffer[2].Alpha = 0xFF;
+ // color3 = 1 / 3 * c0 + 2 / 3 * c1
+ color_buffer[3].Red = (byte)((color_buffer[0].Red + (color_buffer[1].Red << 1) + 1) / 3);
+ color_buffer[3].Green = (byte)((color_buffer[0].Green + (color_buffer[1].Green << 1) + 1) / 3);
+ color_buffer[3].Blue = (byte)((color_buffer[0].Blue + (color_buffer[1].Blue << 1) + 1) / 3);
+ color_buffer[3].Alpha = 0xFF;
+ }
+ else
+ {
+ // color2 = 1 / 2 * c0 + 1 / 2 * c1
+ color_buffer[2].Red = (byte)((color_buffer[0].Red + color_buffer[1].Red + 1) >> 1);
+ color_buffer[2].Green = (byte)((color_buffer[0].Green + color_buffer[1].Green + 1) >> 1);
+ color_buffer[2].Blue = (byte)((color_buffer[0].Blue + color_buffer[1].Blue + 1) >> 1);
+ color_buffer[2].Alpha = 0xFF;
+ // color3 = 0
+ color_buffer[3].Red = 0x0;
+ color_buffer[3].Green = 0x0;
+ color_buffer[3].Blue = 0x0;
+ color_buffer[3].Alpha = alpha ? (byte)0x0 : (byte)0xFF;
+ }
+ for (int i = 0; i < 16; i++)
+ {
+ color[i] = color_buffer[(int)(color_flags & 0x3u)];
+ color_flags >>= 2;
+ }
+ }
+ }
+
+ private static class BC6To7Decoder
+ {
+ private struct BitReader
+ {
+ byte* m_data;
+ ushort m_bitPos;
+
+ public BitReader(byte* data)
+ {
+ m_data = data;
+ }
+
+ static void memcpy(void* d1, void* d2, int num)
+ {
+ NativeMemory.Copy(d2, d1, (nuint)num);
+ }
+
+ public ushort read(byte _numBits)
+ {
+ ushort pos = (ushort)(m_bitPos / 8);
+ ushort shift = (ushort)(m_bitPos & 7);
+ uint data = 0;
+ memcpy(&data, &m_data[pos], Math.Min(4, 16 - pos));
+ m_bitPos += _numBits;
+ return (ushort)((data >> shift) & ((1 << _numBits) - 1));
+ }
+
+ public ushort peek(ushort _offset, byte _numBits)
+ {
+ ushort bitPos = (ushort)(m_bitPos + _offset);
+ ushort shift = (ushort)(bitPos & 7);
+ ushort pos = (ushort)(bitPos / 8);
+ uint data = 0;
+ memcpy(&data, &m_data[pos], Math.Min(4, 16 - pos));
+ return (byte)((data >> shift) & ((1 << _numBits) - 1));
+ }
+ }
+
+ public static void DecodeBC6Block(byte* texPtr, YFColor* color, bool _signed)
+ {
+
+ BitReader bit = new BitReader(texPtr);
+
+ byte mode = (byte)(bit.read(2));
+
+ ushort* epR = stackalloc ushort[4];
+ ushort* epG = stackalloc ushort[4];
+ ushort* epB = stackalloc ushort[4];
+
+ if ((mode & 2) != 0)
+ {
+ // 5-bit mode
+ mode |= (byte)(bit.read(3) << 2);
+
+ if (0 == s_bc6hModeInfo[mode].endpointBits)
+ {
+ for (int i = 0; i < 16; i++)
+ {
+ color->Red = 0;
+ color->Green = 0;
+ color->Blue = 0;
+ color->Alpha = 0;
+ color++;
+ }
+ return;
+ }
+
+ switch (mode)
+ {
+ case 2:
+ epR[0] |= (ushort)(bit.read(10) << 0);
+ epG[0] |= (ushort)(bit.read(10) << 0);
+ epB[0] |= (ushort)(bit.read(10) << 0);
+ epR[1] |= (ushort)(bit.read(5) << 0);
+ epR[0] |= (ushort)(bit.read(1) << 10);
+ epG[2] |= (ushort)(bit.read(4) << 0);
+ epG[1] |= (ushort)(bit.read(4) << 0);
+ epG[0] |= (ushort)(bit.read(1) << 10);
+ epB[3] |= (ushort)(bit.read(1) << 0);
+ epG[3] |= (ushort)(bit.read(4) << 0);
+ epB[1] |= (ushort)(bit.read(4) << 0);
+ epB[0] |= (ushort)(bit.read(1) << 10);
+ epB[3] |= (ushort)(bit.read(1) << 1);
+ epB[2] |= (ushort)(bit.read(4) << 0);
+ epR[2] |= (ushort)(bit.read(5) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 2);
+ epR[3] |= (ushort)(bit.read(5) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 3);
+ break;
+
+ case 3:
+ epR[0] |= (ushort)(bit.read(10) << 0);
+ epG[0] |= (ushort)(bit.read(10) << 0);
+ epB[0] |= (ushort)(bit.read(10) << 0);
+ epR[1] |= (ushort)(bit.read(10) << 0);
+ epG[1] |= (ushort)(bit.read(10) << 0);
+ epB[1] |= (ushort)(bit.read(10) << 0);
+ break;
+
+ case 6:
+ epR[0] |= (ushort)(bit.read(10) << 0);
+ epG[0] |= (ushort)(bit.read(10) << 0);
+ epB[0] |= (ushort)(bit.read(10) << 0);
+ epR[1] |= (ushort)(bit.read(4) << 0);
+ epR[0] |= (ushort)(bit.read(1) << 10);
+ epG[3] |= (ushort)(bit.read(1) << 4);
+ epG[2] |= (ushort)(bit.read(4) << 0);
+ epG[1] |= (ushort)(bit.read(5) << 0);
+ epG[0] |= (ushort)(bit.read(1) << 10);
+ epG[3] |= (ushort)(bit.read(4) << 0);
+ epB[1] |= (ushort)(bit.read(4) << 0);
+ epB[0] |= (ushort)(bit.read(1) << 10);
+ epB[3] |= (ushort)(bit.read(1) << 1);
+ epB[2] |= (ushort)(bit.read(4) << 0);
+ epR[2] |= (ushort)(bit.read(4) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 2);
+ epR[3] |= (ushort)(bit.read(4) << 0);
+ epG[2] |= (ushort)(bit.read(1) << 4);
+ epB[3] |= (ushort)(bit.read(1) << 3);
+ break;
+
+ case 7:
+ epR[0] |= (ushort)(bit.read(10) << 0);
+ epG[0] |= (ushort)(bit.read(10) << 0);
+ epB[0] |= (ushort)(bit.read(10) << 0);
+ epR[1] |= (ushort)(bit.read(9) << 0);
+ epR[0] |= (ushort)(bit.read(1) << 10);
+ epG[1] |= (ushort)(bit.read(9) << 0);
+ epG[0] |= (ushort)(bit.read(1) << 10);
+ epB[1] |= (ushort)(bit.read(9) << 0);
+ epB[0] |= (ushort)(bit.read(1) << 10);
+ break;
+
+ case 10:
+ epR[0] |= (ushort)(bit.read(10) << 0);
+ epG[0] |= (ushort)(bit.read(10) << 0);
+ epB[0] |= (ushort)(bit.read(10) << 0);
+ epR[1] |= (ushort)(bit.read(4) << 0);
+ epR[0] |= (ushort)(bit.read(1) << 10);
+ epB[2] |= (ushort)(bit.read(1) << 4);
+ epG[2] |= (ushort)(bit.read(4) << 0);
+ epG[1] |= (ushort)(bit.read(4) << 0);
+ epG[0] |= (ushort)(bit.read(1) << 10);
+ epB[3] |= (ushort)(bit.read(1) << 0);
+ epG[3] |= (ushort)(bit.read(4) << 0);
+ epB[1] |= (ushort)(bit.read(5) << 0);
+ epB[0] |= (ushort)(bit.read(1) << 10);
+ epB[2] |= (ushort)(bit.read(4) << 0);
+ epR[2] |= (ushort)(bit.read(4) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 1);
+ epB[3] |= (ushort)(bit.read(1) << 2);
+ epR[3] |= (ushort)(bit.read(4) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 4);
+ epB[3] |= (ushort)(bit.read(1) << 3);
+ break;
+
+ case 11:
+ epR[0] |= (ushort)(bit.read(10) << 0);
+ epG[0] |= (ushort)(bit.read(10) << 0);
+ epB[0] |= (ushort)(bit.read(10) << 0);
+ epR[1] |= (ushort)(bit.read(8) << 0);
+ epR[0] |= (ushort)(bit.read(1) << 11);
+ epR[0] |= (ushort)(bit.read(1) << 10);
+ epG[1] |= (ushort)(bit.read(8) << 0);
+ epG[0] |= (ushort)(bit.read(1) << 11);
+ epG[0] |= (ushort)(bit.read(1) << 10);
+ epB[1] |= (ushort)(bit.read(8) << 0);
+ epB[0] |= (ushort)(bit.read(1) << 11);
+ epB[0] |= (ushort)(bit.read(1) << 10);
+ break;
+
+ case 14:
+ epR[0] |= (ushort)(bit.read(9) << 0);
+ epB[2] |= (ushort)(bit.read(1) << 4);
+ epG[0] |= (ushort)(bit.read(9) << 0);
+ epG[2] |= (ushort)(bit.read(1) << 4);
+ epB[0] |= (ushort)(bit.read(9) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 4);
+ epR[1] |= (ushort)(bit.read(5) << 0);
+ epG[3] |= (ushort)(bit.read(1) << 4);
+ epG[2] |= (ushort)(bit.read(4) << 0);
+ epG[1] |= (ushort)(bit.read(5) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 0);
+ epG[3] |= (ushort)(bit.read(4) << 0);
+ epB[1] |= (ushort)(bit.read(5) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 1);
+ epB[2] |= (ushort)(bit.read(4) << 0);
+ epR[2] |= (ushort)(bit.read(5) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 2);
+ epR[3] |= (ushort)(bit.read(5) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 3);
+ break;
+
+ case 15:
+ epR[0] |= (ushort)(bit.read(10) << 0);
+ epG[0] |= (ushort)(bit.read(10) << 0);
+ epB[0] |= (ushort)(bit.read(10) << 0);
+ epR[1] |= (ushort)(bit.read(4) << 0);
+ epR[0] |= (ushort)(bit.read(1) << 15);
+ epR[0] |= (ushort)(bit.read(1) << 14);
+ epR[0] |= (ushort)(bit.read(1) << 13);
+ epR[0] |= (ushort)(bit.read(1) << 12);
+ epR[0] |= (ushort)(bit.read(1) << 11);
+ epR[0] |= (ushort)(bit.read(1) << 10);
+ epG[1] |= (ushort)(bit.read(4) << 0);
+ epG[0] |= (ushort)(bit.read(1) << 15);
+ epG[0] |= (ushort)(bit.read(1) << 14);
+ epG[0] |= (ushort)(bit.read(1) << 13);
+ epG[0] |= (ushort)(bit.read(1) << 12);
+ epG[0] |= (ushort)(bit.read(1) << 11);
+ epG[0] |= (ushort)(bit.read(1) << 10);
+ epB[1] |= (ushort)(bit.read(4) << 0);
+ epB[0] |= (ushort)(bit.read(1) << 15);
+ epB[0] |= (ushort)(bit.read(1) << 14);
+ epB[0] |= (ushort)(bit.read(1) << 13);
+ epB[0] |= (ushort)(bit.read(1) << 12);
+ epB[0] |= (ushort)(bit.read(1) << 11);
+ epB[0] |= (ushort)(bit.read(1) << 10);
+ break;
+
+ case 18:
+ epR[0] |= (ushort)(bit.read(8) << 0);
+ epG[3] |= (ushort)(bit.read(1) << 4);
+ epB[2] |= (ushort)(bit.read(1) << 4);
+ epG[0] |= (ushort)(bit.read(8) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 2);
+ epG[2] |= (ushort)(bit.read(1) << 4);
+ epB[0] |= (ushort)(bit.read(8) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 3);
+ epB[3] |= (ushort)(bit.read(1) << 4);
+ epR[1] |= (ushort)(bit.read(6) << 0);
+ epG[2] |= (ushort)(bit.read(4) << 0);
+ epG[1] |= (ushort)(bit.read(5) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 0);
+ epG[3] |= (ushort)(bit.read(4) << 0);
+ epB[1] |= (ushort)(bit.read(5) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 1);
+ epB[2] |= (ushort)(bit.read(4) << 0);
+ epR[2] |= (ushort)(bit.read(6) << 0);
+ epR[3] |= (ushort)(bit.read(6) << 0);
+ break;
+
+ case 22:
+ epR[0] |= (ushort)(bit.read(8) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 0);
+ epB[2] |= (ushort)(bit.read(1) << 4);
+ epG[0] |= (ushort)(bit.read(8) << 0);
+ epG[2] |= (ushort)(bit.read(1) << 5);
+ epG[2] |= (ushort)(bit.read(1) << 4);
+ epB[0] |= (ushort)(bit.read(8) << 0);
+ epG[3] |= (ushort)(bit.read(1) << 5);
+ epB[3] |= (ushort)(bit.read(1) << 4);
+ epR[1] |= (ushort)(bit.read(5) << 0);
+ epG[3] |= (ushort)(bit.read(1) << 4);
+ epG[2] |= (ushort)(bit.read(4) << 0);
+ epG[1] |= (ushort)(bit.read(6) << 0);
+ epG[3] |= (ushort)(bit.read(4) << 0);
+ epB[1] |= (ushort)(bit.read(5) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 1);
+ epB[2] |= (ushort)(bit.read(4) << 0);
+ epR[2] |= (ushort)(bit.read(5) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 2);
+ epR[3] |= (ushort)(bit.read(5) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 3);
+ break;
+
+ case 26:
+ epR[0] |= (ushort)(bit.read(8) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 1);
+ epB[2] |= (ushort)(bit.read(1) << 4);
+ epG[0] |= (ushort)(bit.read(8) << 0);
+ epB[2] |= (ushort)(bit.read(1) << 5);
+ epG[2] |= (ushort)(bit.read(1) << 4);
+ epB[0] |= (ushort)(bit.read(8) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 5);
+ epB[3] |= (ushort)(bit.read(1) << 4);
+ epR[1] |= (ushort)(bit.read(5) << 0);
+ epG[3] |= (ushort)(bit.read(1) << 4);
+ epG[2] |= (ushort)(bit.read(4) << 0);
+ epG[1] |= (ushort)(bit.read(5) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 0);
+ epG[3] |= (ushort)(bit.read(4) << 0);
+ epB[1] |= (ushort)(bit.read(6) << 0);
+ epB[2] |= (ushort)(bit.read(4) << 0);
+ epR[2] |= (ushort)(bit.read(5) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 2);
+ epR[3] |= (ushort)(bit.read(5) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 3);
+ break;
+
+ case 30:
+ epR[0] |= (ushort)(bit.read(6) << 0);
+ epG[3] |= (ushort)(bit.read(1) << 4);
+ epB[3] |= (ushort)(bit.read(1) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 1);
+ epB[2] |= (ushort)(bit.read(1) << 4);
+ epG[0] |= (ushort)(bit.read(6) << 0);
+ epG[2] |= (ushort)(bit.read(1) << 5);
+ epB[2] |= (ushort)(bit.read(1) << 5);
+ epB[3] |= (ushort)(bit.read(1) << 2);
+ epG[2] |= (ushort)(bit.read(1) << 4);
+ epB[0] |= (ushort)(bit.read(6) << 0);
+ epG[3] |= (ushort)(bit.read(1) << 5);
+ epB[3] |= (ushort)(bit.read(1) << 3);
+ epB[3] |= (ushort)(bit.read(1) << 5);
+ epB[3] |= (ushort)(bit.read(1) << 4);
+ epR[1] |= (ushort)(bit.read(6) << 0);
+ epG[2] |= (ushort)(bit.read(4) << 0);
+ epG[1] |= (ushort)(bit.read(6) << 0);
+ epG[3] |= (ushort)(bit.read(4) << 0);
+ epB[1] |= (ushort)(bit.read(6) << 0);
+ epB[2] |= (ushort)(bit.read(4) << 0);
+ epR[2] |= (ushort)(bit.read(6) << 0);
+ epR[3] |= (ushort)(bit.read(6) << 0);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ else
+ {
+ switch (mode)
+ {
+ case 0:
+ epG[2] |= (ushort)(bit.read(1) << 4);
+ epB[2] |= (ushort)(bit.read(1) << 4);
+ epB[3] |= (ushort)(bit.read(1) << 4);
+ epR[0] |= (ushort)(bit.read(10) << 0);
+ epG[0] |= (ushort)(bit.read(10) << 0);
+ epB[0] |= (ushort)(bit.read(10) << 0);
+ epR[1] |= (ushort)(bit.read(5) << 0);
+ epG[3] |= (ushort)(bit.read(1) << 4);
+ epG[2] |= (ushort)(bit.read(4) << 0);
+ epG[1] |= (ushort)(bit.read(5) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 0);
+ epG[3] |= (ushort)(bit.read(4) << 0);
+ epB[1] |= (ushort)(bit.read(5) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 1);
+ epB[2] |= (ushort)(bit.read(4) << 0);
+ epR[2] |= (ushort)(bit.read(5) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 2);
+ epR[3] |= (ushort)(bit.read(5) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 3);
+ break;
+
+ case 1:
+ epG[2] |= (ushort)(bit.read(1) << 5);
+ epG[3] |= (ushort)(bit.read(1) << 4);
+ epG[3] |= (ushort)(bit.read(1) << 5);
+ epR[0] |= (ushort)(bit.read(7) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 1);
+ epB[2] |= (ushort)(bit.read(1) << 4);
+ epG[0] |= (ushort)(bit.read(7) << 0);
+ epB[2] |= (ushort)(bit.read(1) << 5);
+ epB[3] |= (ushort)(bit.read(1) << 2);
+ epG[2] |= (ushort)(bit.read(1) << 4);
+ epB[0] |= (ushort)(bit.read(7) << 0);
+ epB[3] |= (ushort)(bit.read(1) << 3);
+ epB[3] |= (ushort)(bit.read(1) << 5);
+ epB[3] |= (ushort)(bit.read(1) << 4);
+ epR[1] |= (ushort)(bit.read(6) << 0);
+ epG[2] |= (ushort)(bit.read(4) << 0);
+ epG[1] |= (ushort)(bit.read(6) << 0);
+ epG[3] |= (ushort)(bit.read(4) << 0);
+ epB[1] |= (ushort)(bit.read(6) << 0);
+ epB[2] |= (ushort)(bit.read(4) << 0);
+ epR[2] |= (ushort)(bit.read(6) << 0);
+ epR[3] |= (ushort)(bit.read(6) << 0);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ Bc6hModeInfo mi = s_bc6hModeInfo[mode];
+
+ if (_signed)
+ {
+ epR[0] = sign_extend(epR[0], mi.endpointBits);
+ epG[0] = sign_extend(epG[0], mi.endpointBits);
+ epB[0] = sign_extend(epB[0], mi.endpointBits);
+ }
+
+ byte numSubsets = mi.partitionBits == 0 ? (byte)1 : (byte)2; // 存疑
+
+ for (byte ii = 1, num = (byte)(numSubsets * 2); ii < num; ++ii)
+ {
+ if (_signed
+ || (mi.transformed != 0))
+ {
+ epR[ii] = sign_extend(epR[ii], mi.deltaBits[0]);
+ epG[ii] = sign_extend(epG[ii], mi.deltaBits[1]);
+ epB[ii] = sign_extend(epB[ii], mi.deltaBits[2]);
+ }
+
+ if (mi.transformed != 0)
+ {
+ ushort mask = (ushort)((1 << mi.endpointBits) - 1);
+
+ epR[ii] = (ushort)((epR[ii] + epR[0]) & mask);
+ epG[ii] = (ushort)((epG[ii] + epG[0]) & mask);
+ epB[ii] = (ushort)((epB[ii] + epB[0]) & mask);
+
+ if (_signed)
+ {
+ epR[ii] = sign_extend(epR[ii], mi.endpointBits);
+ epG[ii] = sign_extend(epG[ii], mi.endpointBits);
+ epB[ii] = sign_extend(epB[ii], mi.endpointBits);
+ }
+ }
+ }
+
+ for (byte ii = 0, num = (byte)(numSubsets * 2); ii < num; ++ii)
+ {
+ epR[ii] = unquantize(epR[ii], _signed, mi.endpointBits);
+ epG[ii] = unquantize(epG[ii], _signed, mi.endpointBits);
+ epB[ii] = unquantize(epB[ii], _signed, mi.endpointBits);
+ }
+
+ byte partitionSetIdx = (byte)((mi.partitionBits != 0) ? bit.read(5) : 0);
+ byte indexBits = (mi.partitionBits != 0) ? (byte)3 : (byte)4;
+ byte[] factors = s_bptcFactors[indexBits - 2];
+
+ for (byte yy = 0; yy < 4; ++yy)
+ {
+ for (byte xx = 0; xx < 4; ++xx)
+ {
+ int idx = yy * 4 + xx;
+
+ byte subsetIndex = 0;
+ byte indexAnchor = 0;
+
+ if (0 != mi.partitionBits)
+ {
+ subsetIndex = (byte)((s_bptcP2[partitionSetIdx] >> idx) & 1);
+ indexAnchor = (subsetIndex != 0) ? s_bptcA2[partitionSetIdx] : (byte)0;
+ }
+
+ byte anchor = idx == indexAnchor ? (byte)1 : (byte)0;
+ byte num = (byte)(indexBits - anchor);
+ byte index = (byte)bit.read(num);
+
+ byte fc = factors[index];
+ byte fca = (byte)(64 - fc);
+ byte fcb = fc;
+
+ subsetIndex *= 2;
+ ushort rr = finish_unquantize((ushort)((epR[subsetIndex] * fca + epR[subsetIndex + 1] * fcb + 32) >> 6), _signed);
+ ushort gg = finish_unquantize((ushort)((epG[subsetIndex] * fca + epG[subsetIndex + 1] * fcb + 32) >> 6), _signed);
+ ushort bb = finish_unquantize((ushort)((epB[subsetIndex] * fca + epB[subsetIndex + 1] * fcb + 32) >> 6), _signed);
+
+ color[idx].Red = half_to_u8(rr);
+ color[idx].Green = half_to_u8(gg);
+ color[idx].Blue = half_to_u8(bb);
+ color[idx].Alpha = 255;
+ }
+ }
+ }
+
+ static byte half_to_u8(ushort v)
+ {
+ double value = (double)*(Half*)&v;
+ value = value <= 0.0031308d ? (12.92d * value) : (1.055d * Math.Pow(value, 1.0d / 2.4d) - 0.055d);
+ value = Math.Round(value * 255.0d);
+ if (value > 254.5)
+ {
+ return 255;
+ }
+ if (value < 0.5)
+ {
+ return 0;
+ }
+ return (byte)value;
+ }
+
+ static ushort sign_extend(ushort _value, byte _numBits)
+ {
+ ushort mask = (ushort)(1 << (_numBits - 1));
+ ushort result = (ushort)((_value ^ mask) - mask);
+ return result;
+ }
+
+ static ushort unquantize(ushort _value, bool _signed, byte _endpointBits)
+ {
+ ushort maxValue = (ushort)(1 << (_endpointBits - 1));
+
+ if (_signed)
+ {
+ if (_endpointBits >= 16)
+ {
+ return _value;
+ }
+
+ bool sign = (_value & 0x8000) != 0;
+ _value &= 0x7fff;
+
+ ushort unq;
+
+ if (0 == _value)
+ {
+ unq = 0;
+ }
+ else if (_value >= maxValue - 1)
+ {
+ unq = 0x7fff;
+ }
+ else
+ {
+ unq = (ushort)(((_value << 15) + 0x4000) >> (_endpointBits - 1));
+ }
+
+ return (ushort)(sign ? -unq : unq); // 存疑
+ }
+
+ if (_endpointBits >= 15)
+ {
+ return _value;
+ }
+
+ if (0 == _value)
+ {
+ return 0;
+ }
+
+ if (_value == maxValue)
+ {
+ return ushort.MaxValue;
+ }
+
+ return (ushort)(((_value << 15) + 0x4000) >> (_endpointBits - 1));
+ }
+
+ static ushort finish_unquantize(ushort _value, bool _signed)
+ {
+ if (_signed)
+ {
+ ushort sign = (ushort)(_value & 0x8000);
+ _value &= 0x7fff;
+
+ return (ushort)(((_value * 31) >> 5) | sign);
+ }
+
+ return (ushort)((_value * 31) >> 6);
+ }
+
+ public static void DecodeBC7Block(byte* texPtr, YFColor* color)
+ {
+ // 计算块使用的模式
+ BitReader bit = new BitReader(texPtr);
+ int mode = 0;
+ while (mode < 8 && bit.read(1) == 0)
+ {
+ mode++;
+ }
+ // 如果模式未定义则返回RGBA值均为0的块
+ if (mode == 8)
+ {
+ for (int i = 0; i < 16; i++)
+ {
+ color->Red = 0;
+ color->Green = 0;
+ color->Blue = 0;
+ color->Alpha = 0;
+ color++;
+ }
+ return;
+ }
+ Bc7ModeInfo mi = s_bp7ModeInfo[mode];
+ byte modePBits = 0 != mi.endpointPBits ? mi.endpointPBits : mi.sharedPBits;
+ byte partitionSetIdx = (byte)bit.read(mi.partitionBits);
+ byte rotationMode = (byte)bit.read(mi.rotationBits);
+ byte indexSelectionMode = (byte)bit.read(mi.indexSelectionBits);
+ byte* epR = stackalloc byte[6];
+ byte* epG = stackalloc byte[6];
+ byte* epB = stackalloc byte[6];
+ byte* epA = stackalloc byte[6];
+ for (int ii = 0; ii < mi.numSubsets; ii++)
+ {
+ epR[ii << 1] = (byte)(bit.read(mi.colorBits) << modePBits);
+ epR[(ii << 1) | 1] = (byte)(bit.read(mi.colorBits) << modePBits);
+ }
+ for (int ii = 0; ii < mi.numSubsets; ii++)
+ {
+ epG[ii << 1] = (byte)(bit.read(mi.colorBits) << modePBits);
+ epG[(ii << 1) | 1] = (byte)(bit.read(mi.colorBits) << modePBits);
+ }
+ for (int ii = 0; ii < mi.numSubsets; ii++)
+ {
+ epB[ii << 1] = (byte)(bit.read(mi.colorBits) << modePBits);
+ epB[(ii << 1) | 1] = (byte)(bit.read(mi.colorBits) << modePBits);
+ }
+ if (mi.alphaBits != 0)
+ {
+ for (int ii = 0; ii < mi.numSubsets; ii++)
+ {
+ epA[ii << 1] = (byte)(bit.read(mi.alphaBits) << modePBits);
+ epA[(ii << 1) | 1] = (byte)(bit.read(mi.alphaBits) << modePBits);
+ }
+ }
+ else
+ {
+ for (int ii = 0; ii < 6; ii++)
+ {
+ epA[ii] = 0xFF;
+ }
+ }
+ if (modePBits != 0)
+ {
+ for (int ii = 0; ii < mi.numSubsets; ii++)
+ {
+ byte pda = (byte)bit.read(modePBits);
+ byte pdb = (byte)(0 == mi.sharedPBits ? bit.read(modePBits) : pda);
+ epR[ii * 2 + 0] |= pda;
+ epR[ii * 2 + 1] |= pdb;
+ epG[ii * 2 + 0] |= pda;
+ epG[ii * 2 + 1] |= pdb;
+ epB[ii * 2 + 0] |= pda;
+ epB[ii * 2 + 1] |= pdb;
+ epA[ii * 2 + 0] |= pda;
+ epA[ii * 2 + 1] |= pdb;
+ }
+ }
+ byte colorBits = (byte)(mi.colorBits + modePBits);
+ for (int ii = 0; ii < mi.numSubsets; ++ii)
+ {
+ epR[ii * 2 + 0] = expand_quantized(epR[ii * 2 + 0], colorBits);
+ epR[ii * 2 + 1] = expand_quantized(epR[ii * 2 + 1], colorBits);
+ epG[ii * 2 + 0] = expand_quantized(epG[ii * 2 + 0], colorBits);
+ epG[ii * 2 + 1] = expand_quantized(epG[ii * 2 + 1], colorBits);
+ epB[ii * 2 + 0] = expand_quantized(epB[ii * 2 + 0], colorBits);
+ epB[ii * 2 + 1] = expand_quantized(epB[ii * 2 + 1], colorBits);
+ }
+ if (mi.alphaBits != 0)
+ {
+ byte alphaBits = (byte)(mi.alphaBits + modePBits);
+
+ for (int ii = 0; ii < mi.numSubsets; ++ii)
+ {
+ epA[ii * 2 + 0] = expand_quantized(epA[ii * 2 + 0], alphaBits);
+ epA[ii * 2 + 1] = expand_quantized(epA[ii * 2 + 1], alphaBits);
+ }
+ }
+ bool hasIndexBits1 = mi.indexBits[1] != 0;
+ byte[][] factors = new byte[2][]
+ {
+ s_bptcFactors[mi.indexBits[0] - 2],
+ s_bptcFactors[mi.indexBits[hasIndexBits1 ? 1 : 0] - 2]
+ };
+ ushort* offset = stackalloc ushort[2]
+ {
+ 0,
+ (ushort)(mi.numSubsets * (16 * mi.indexBits[0] - 1)),
+ };
+ byte* num = stackalloc byte[2];
+ byte* index = stackalloc byte[2];
+ for (int yy = 0; yy < 4; ++yy)
+ {
+ for (int xx = 0; xx < 4; ++xx)
+ {
+ int idx = yy * 4 + xx;
+
+ byte subsetIndex = 0;
+ byte indexAnchor = 0;
+ switch (mi.numSubsets)
+ {
+ case 2:
+ subsetIndex = (byte)((s_bptcP2[partitionSetIdx] >> idx) & 1);
+ indexAnchor = subsetIndex != 0 ? s_bptcA2[partitionSetIdx] : (byte)0;
+ break;
+
+ case 3:
+ subsetIndex = (byte)((s_bptcP3[partitionSetIdx] >> (2 * idx)) & 3);
+ indexAnchor = subsetIndex != 0 ? s_bptcA3[subsetIndex - 1, partitionSetIdx] : (byte)0;
+ break;
+ default:
+ break;
+ }
+
+ byte anchor = idx == indexAnchor ? (byte)1 : (byte)0;
+ num[0] = (byte)(mi.indexBits[0] - anchor);
+ num[1] = (byte)(hasIndexBits1 ? mi.indexBits[1] - anchor : 0);
+ index[0] = (byte)bit.peek(offset[0], num[0]);
+ index[1] = hasIndexBits1 ? (byte)bit.peek(offset[1], num[1]) : index[0];
+ offset[0] += num[0];
+ offset[1] += num[1];
+
+ byte fc = factors[indexSelectionMode][index[indexSelectionMode]];
+ byte fa = factors[indexSelectionMode == 0 ? 1 : 0][index[indexSelectionMode == 0 ? 1 : 0]];
+
+ byte fca = (byte)(64 - fc);
+ byte fcb = fc;
+ byte faa = (byte)(64 - fa);
+ byte fab = fa;
+
+ subsetIndex *= 2;
+ byte rr = (byte)((ushort)(epR[subsetIndex] * fca + epR[subsetIndex + 1] * fcb + 32) >> 6);
+ byte gg = (byte)((ushort)(epG[subsetIndex] * fca + epG[subsetIndex + 1] * fcb + 32) >> 6);
+ byte bb = (byte)((ushort)(epB[subsetIndex] * fca + epB[subsetIndex + 1] * fcb + 32) >> 6);
+ byte aa = (byte)((ushort)(epA[subsetIndex] * faa + epA[subsetIndex + 1] * fab + 32) >> 6);
+
+ switch (rotationMode)
+ {
+ case 1: (aa, rr) = (rr, aa); break;
+ case 2: (aa, gg) = (gg, aa); break;
+ case 3: (aa, bb) = (bb, aa); break;
+ default: break;
+ };
+
+ color[idx].Red = rr;
+ color[idx].Green = gg;
+ color[idx].Blue = bb;
+ color[idx].Alpha = aa;
+ }
+ }
+ }
+
+ static byte expand_quantized(byte v, int bits)
+ {
+ v <<= 8 - bits;
+ return (byte)(v | (v >> bits));
+ }
+
+ private struct Bc6hModeInfo
+ {
+ public byte transformed;
+ public byte partitionBits;
+ public byte endpointBits;
+ public fixed byte deltaBits[3];
+
+ public Bc6hModeInfo(byte transformed, byte partitionBits, byte endpointBits, byte deltaBits0, byte deltaBits1, byte deltaBits2)
+ {
+ this.transformed = transformed;
+ this.partitionBits = partitionBits;
+ this.endpointBits = endpointBits;
+ deltaBits[0] = deltaBits0;
+ deltaBits[1] = deltaBits1;
+ deltaBits[2] = deltaBits2;
+ }
+ }
+ private struct Bc7ModeInfo
+ {
+ public byte numSubsets;
+ public byte partitionBits;
+ public byte rotationBits;
+ public byte indexSelectionBits;
+ public byte colorBits;
+ public byte alphaBits;
+ public byte endpointPBits;
+ public byte sharedPBits;
+ public fixed byte indexBits[2];
+
+ public Bc7ModeInfo(byte numSubsets, byte partitionBits, byte rotationBits, byte indexSelectionBits, byte colorBits, byte alphaBits, byte endpointPBits, byte sharedPBits, byte indexBits0, byte indexBits1)
+ {
+ this.numSubsets = numSubsets;
+ this.partitionBits = partitionBits;
+ this.rotationBits = rotationBits;
+ this.indexSelectionBits = indexSelectionBits;
+ this.colorBits = colorBits;
+ this.alphaBits = alphaBits;
+ this.endpointPBits = endpointPBits;
+ this.sharedPBits = sharedPBits;
+ indexBits[0] = indexBits0;
+ indexBits[1] = indexBits1;
+ }
+ }
+
+ static uint[] s_bptcP3 = new uint[]
+ { // 76543210 0000 1111 2222 3333 4444 5555 6666 7777
+ 0xaa685050, // 0, 0, 1, 1, 0, 0, 1, 1, 0, 2, 2, 1, 2, 2, 2, 2
+ 0x6a5a5040, // 0, 0, 0, 1, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2, 2, 1
+ 0x5a5a4200, // 0, 0, 0, 0, 2, 0, 0, 1, 2, 2, 1, 1, 2, 2, 1, 1
+ 0x5450a0a8, // 0, 2, 2, 2, 0, 0, 2, 2, 0, 0, 1, 1, 0, 1, 1, 1
+ 0xa5a50000, // 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2
+ 0xa0a05050, // 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 2, 2, 0, 0, 2, 2
+ 0x5555a0a0, // 0, 0, 2, 2, 0, 0, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1
+ 0x5a5a5050, // 0, 0, 1, 1, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1
+ 0xaa550000, // 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2
+ 0xaa555500, // 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2
+ 0xaaaa5500, // 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2
+ 0x90909090, // 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2
+ 0x94949494, // 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2, 0, 1, 1, 2
+ 0xa4a4a4a4, // 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 2
+ 0xa9a59450, // 0, 0, 1, 1, 0, 1, 1, 2, 1, 1, 2, 2, 1, 2, 2, 2
+ 0x2a0a4250, // 0, 0, 1, 1, 2, 0, 0, 1, 2, 2, 0, 0, 2, 2, 2, 0
+ 0xa5945040, // 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 2, 1, 1, 2, 2
+ 0x0a425054, // 0, 1, 1, 1, 0, 0, 1, 1, 2, 0, 0, 1, 2, 2, 0, 0
+ 0xa5a5a500, // 0, 0, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2
+ 0x55a0a0a0, // 0, 0, 2, 2, 0, 0, 2, 2, 0, 0, 2, 2, 1, 1, 1, 1
+ 0xa8a85454, // 0, 1, 1, 1, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2
+ 0x6a6a4040, // 0, 0, 0, 1, 0, 0, 0, 1, 2, 2, 2, 1, 2, 2, 2, 1
+ 0xa4a45000, // 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 2, 2, 0, 1, 2, 2
+ 0x1a1a0500, // 0, 0, 0, 0, 1, 1, 0, 0, 2, 2, 1, 0, 2, 2, 1, 0
+ 0x0050a4a4, // 0, 1, 2, 2, 0, 1, 2, 2, 0, 0, 1, 1, 0, 0, 0, 0
+ 0xaaa59090, // 0, 0, 1, 2, 0, 0, 1, 2, 1, 1, 2, 2, 2, 2, 2, 2
+ 0x14696914, // 0, 1, 1, 0, 1, 2, 2, 1, 1, 2, 2, 1, 0, 1, 1, 0
+ 0x69691400, // 0, 0, 0, 0, 0, 1, 1, 0, 1, 2, 2, 1, 1, 2, 2, 1
+ 0xa08585a0, // 0, 0, 2, 2, 1, 1, 0, 2, 1, 1, 0, 2, 0, 0, 2, 2
+ 0xaa821414, // 0, 1, 1, 0, 0, 1, 1, 0, 2, 0, 0, 2, 2, 2, 2, 2
+ 0x50a4a450, // 0, 0, 1, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 0, 1, 1
+ 0x6a5a0200, // 0, 0, 0, 0, 2, 0, 0, 0, 2, 2, 1, 1, 2, 2, 2, 1
+ 0xa9a58000, // 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 2, 2, 1, 2, 2, 2
+ 0x5090a0a8, // 0, 2, 2, 2, 0, 0, 2, 2, 0, 0, 1, 2, 0, 0, 1, 1
+ 0xa8a09050, // 0, 0, 1, 1, 0, 0, 1, 2, 0, 0, 2, 2, 0, 2, 2, 2
+ 0x24242424, // 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 0
+ 0x00aa5500, // 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0
+ 0x24924924, // 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0
+ 0x24499224, // 0, 1, 2, 0, 2, 0, 1, 2, 1, 2, 0, 1, 0, 1, 2, 0
+ 0x50a50a50, // 0, 0, 1, 1, 2, 2, 0, 0, 1, 1, 2, 2, 0, 0, 1, 1
+ 0x500aa550, // 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1
+ 0xaaaa4444, // 0, 1, 0, 1, 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2
+ 0x66660000, // 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 1, 2, 1, 2, 1
+ 0xa5a0a5a0, // 0, 0, 2, 2, 1, 1, 2, 2, 0, 0, 2, 2, 1, 1, 2, 2
+ 0x50a050a0, // 0, 0, 2, 2, 0, 0, 1, 1, 0, 0, 2, 2, 0, 0, 1, 1
+ 0x69286928, // 0, 2, 2, 0, 1, 2, 2, 1, 0, 2, 2, 0, 1, 2, 2, 1
+ 0x44aaaa44, // 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 0, 1, 0, 1
+ 0x66666600, // 0, 0, 0, 0, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1
+ 0xaa444444, // 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 2, 2, 2
+ 0x54a854a8, // 0, 2, 2, 2, 0, 1, 1, 1, 0, 2, 2, 2, 0, 1, 1, 1
+ 0x95809580, // 0, 0, 0, 2, 1, 1, 1, 2, 0, 0, 0, 2, 1, 1, 1, 2
+ 0x96969600, // 0, 0, 0, 0, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 2
+ 0xa85454a8, // 0, 2, 2, 2, 0, 1, 1, 1, 0, 1, 1, 1, 0, 2, 2, 2
+ 0x80959580, // 0, 0, 0, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2
+ 0xaa141414, // 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 2, 2, 2, 2
+ 0x96960000, // 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 2, 2, 1, 1, 2
+ 0xaaaa1414, // 0, 1, 1, 0, 0, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2, 2
+ 0xa05050a0, // 0, 0, 2, 2, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 2, 2
+ 0xa0a5a5a0, // 0, 0, 2, 2, 1, 1, 2, 2, 1, 1, 2, 2, 0, 0, 2, 2
+ 0x96000000, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 1, 2
+ 0x40804080, // 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 1
+ 0xa9a8a9a8, // 0, 2, 2, 2, 1, 2, 2, 2, 0, 2, 2, 2, 1, 2, 2, 2
+ 0xaaaaaa44, // 0, 1, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
+ 0x2a4a5254, // 0, 1, 1, 1, 2, 0, 1, 1, 2, 2, 0, 1, 2, 2, 2, 0
+ };
+
+ static byte[,] s_bptcA3 = new byte[2, 64]
+ {
+ {
+ 3, 3, 15, 15, 8, 3, 15, 15,
+ 8, 8, 6, 6, 6, 5, 3, 3,
+ 3, 3, 8, 15, 3, 3, 6, 10,
+ 5, 8, 8, 6, 8, 5, 15, 15,
+ 8, 15, 3, 5, 6, 10, 8, 15,
+ 15, 3, 15, 5, 15, 15, 15, 15,
+ 3, 15, 5, 5, 5, 8, 5, 10,
+ 5, 10, 8, 13, 15, 12, 3, 3,
+ },
+ {
+ 15, 8, 8, 3, 15, 15, 3, 8,
+ 15, 15, 15, 15, 15, 15, 15, 8,
+ 15, 8, 15, 3, 15, 8, 15, 8,
+ 3, 15, 6, 10, 15, 15, 10, 8,
+ 15, 3, 15, 10, 10, 8, 9, 10,
+ 6, 15, 8, 15, 3, 6, 6, 8,
+ 15, 3, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 3, 15, 15, 8,
+ },
+ };
+
+ static ushort[] s_bptcP2 = new ushort[]
+ { // 3210 0000000000 1111111111 2222222222 3333333333
+ 0xcccc, // 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1
+ 0x8888, // 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1
+ 0xeeee, // 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1
+ 0xecc8, // 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1
+ 0xc880, // 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1
+ 0xfeec, // 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1
+ 0xfec8, // 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1
+ 0xec80, // 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1
+ 0xc800, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1
+ 0xffec, // 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ 0xfe80, // 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1
+ 0xe800, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1
+ 0xffe8, // 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ 0xff00, // 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1
+ 0xfff0, // 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+ 0xf000, // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1
+ 0xf710, // 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1
+ 0x008e, // 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0
+ 0x7100, // 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0
+ 0x08ce, // 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0
+ 0x008c, // 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0
+ 0x7310, // 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0
+ 0x3100, // 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0
+ 0x8cce, // 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1
+ 0x088c, // 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0
+ 0x3110, // 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0
+ 0x6666, // 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0
+ 0x366c, // 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0
+ 0x17e8, // 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0
+ 0x0ff0, // 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0
+ 0x718e, // 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0
+ 0x399c, // 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0
+ 0xaaaa, // 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1
+ 0xf0f0, // 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1
+ 0x5a5a, // 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0
+ 0x33cc, // 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0
+ 0x3c3c, // 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0
+ 0x55aa, // 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0
+ 0x9696, // 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1
+ 0xa55a, // 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1
+ 0x73ce, // 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0
+ 0x13c8, // 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0
+ 0x324c, // 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0
+ 0x3bdc, // 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0
+ 0x6996, // 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0
+ 0xc33c, // 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1
+ 0x9966, // 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1
+ 0x0660, // 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0
+ 0x0272, // 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0
+ 0x04e4, // 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0
+ 0x4e40, // 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0
+ 0x2720, // 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0
+ 0xc936, // 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1
+ 0x936c, // 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1
+ 0x39c6, // 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0
+ 0x639c, // 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0
+ 0x9336, // 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1
+ 0x9cc6, // 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1
+ 0x817e, // 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1
+ 0xe718, // 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1
+ 0xccf0, // 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1
+ 0x0fcc, // 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0
+ 0x7744, // 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0
+ 0xee22, // 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1
+ };
+
+ static byte[] s_bptcA2 = new byte[]
+ {
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 15, 2, 8, 2, 2, 8, 8, 15,
+ 2, 8, 2, 2, 8, 8, 2, 2,
+ 15, 15, 6, 8, 2, 8, 15, 15,
+ 2, 8, 2, 2, 2, 15, 15, 6,
+ 6, 2, 6, 8, 15, 15, 2, 2,
+ 15, 15, 15, 15, 15, 2, 2, 15,
+ };
+
+ private static byte[][] s_bptcFactors = new byte[3][]
+ {
+ new byte[16] { 0, 21, 43, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ new byte[16] { 0, 9, 18, 27, 37, 46, 55, 64, 0, 0, 0, 0, 0, 0, 0, 0 },
+ new byte[16] { 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 },
+ };
+
+ private static Bc6hModeInfo[] s_bc6hModeInfo = new Bc6hModeInfo[32]
+ { // +--------------------------- transformed
+ // | +------------------------ partition bits
+ // | | +--------------------- endpoint bits
+ // | | | +-------------- delta bits
+ new Bc6hModeInfo( 1, 5, 10, 5, 5, 5 ), // 00 2-bits
+ new Bc6hModeInfo( 1, 5, 7, 6, 6, 6 ), // 01
+ new Bc6hModeInfo( 1, 5, 11, 5, 4, 4 ), // 00010 5-bits
+ new Bc6hModeInfo( 0, 0, 10, 10, 10, 10 ), // 00011
+ new Bc6hModeInfo( 0, 0, 0, 0, 0, 0 ), // -
+ new Bc6hModeInfo( 0, 0, 0, 0, 0, 0 ), // -
+ new Bc6hModeInfo( 1, 5, 11, 4, 5, 4 ), // 00110
+ new Bc6hModeInfo( 1, 0, 11, 9, 9, 9 ), // 00010
+ new Bc6hModeInfo( 0, 0, 0, 0, 0, 0 ), // -
+ new Bc6hModeInfo( 0, 0, 0, 0, 0, 0 ), // -
+ new Bc6hModeInfo( 1, 5, 11, 4, 4, 5 ), // 00010
+ new Bc6hModeInfo( 1, 0, 12, 8, 8, 8 ), // 00010
+ new Bc6hModeInfo( 0, 0, 0, 0, 0, 0 ), // -
+ new Bc6hModeInfo( 0, 0, 0, 0, 0, 0 ), // -
+ new Bc6hModeInfo( 1, 5, 9, 5, 5, 5 ), // 00010
+ new Bc6hModeInfo( 1, 0, 16, 4, 4, 4 ), // 00010
+ new Bc6hModeInfo( 0, 0, 0, 0, 0, 0 ), // -
+ new Bc6hModeInfo( 0, 0, 0, 0, 0, 0 ), // -
+ new Bc6hModeInfo( 1, 5, 8, 6, 5, 5 ), // 00010
+ new Bc6hModeInfo( 0, 0, 0, 0, 0, 0 ), // -
+ new Bc6hModeInfo( 0, 0, 0, 0, 0, 0 ), // -
+ new Bc6hModeInfo( 0, 0, 0, 0, 0, 0 ), // -
+ new Bc6hModeInfo( 1, 5, 8, 5, 6, 5 ), // 00010
+ new Bc6hModeInfo( 0, 0, 0, 0, 0, 0 ), // -
+ new Bc6hModeInfo( 0, 0, 0, 0, 0, 0 ), // -
+ new Bc6hModeInfo( 0, 0, 0, 0, 0, 0 ), // -
+ new Bc6hModeInfo( 1, 5, 8, 5, 5, 6 ), // 00010
+ new Bc6hModeInfo( 0, 0, 0, 0, 0, 0 ), // -
+ new Bc6hModeInfo( 0, 0, 0, 0, 0, 0 ), // -
+ new Bc6hModeInfo( 0, 0, 0, 0, 0, 0 ), // -
+ new Bc6hModeInfo( 0, 5, 6, 6, 6, 6 ), // 00010
+ new Bc6hModeInfo( 0, 0, 0, 0, 0, 0 ), // -
+ };
+
+ private static Bc7ModeInfo[] s_bp7ModeInfo = new Bc7ModeInfo[8]
+ {
+ // +----------------------------- num subsets
+ // | +-------------------------- partition bits
+ // | | +----------------------- rotation bits
+ // | | | +-------------------- index selection bits
+ // | | | | +----------------- color bits
+ // | | | | | +-------------- alpha bits
+ // | | | | | | +----------- endpoint P-bits
+ // | | | | | | | +-------- shared P-bits
+ // | | | | | | | | +----- index bits 0
+ // | | | | | | | | | +-- index bits 1
+ new Bc7ModeInfo(3, 4, 0, 0, 4, 0, 1, 0, 3, 0), // 0
+ new Bc7ModeInfo(2, 6, 0, 0, 6, 0, 0, 1, 3, 0), // 1
+ new Bc7ModeInfo(3, 6, 0, 0, 5, 0, 0, 0, 2, 0), // 2
+ new Bc7ModeInfo(2, 6, 0, 0, 7, 0, 1, 0, 2, 0), // 3
+ new Bc7ModeInfo(1, 0, 2, 1, 5, 6, 0, 0, 2, 3), // 4
+ new Bc7ModeInfo(1, 0, 2, 0, 7, 8, 0, 0, 2, 2), // 5
+ new Bc7ModeInfo(1, 0, 0, 0, 7, 7, 1, 0, 4, 0), // 6
+ new Bc7ModeInfo(2, 6, 0, 0, 5, 5, 1, 0, 2, 0), // 7
+ };
+
+
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Shared/ChannelHelper.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Shared/ChannelHelper.cs
new file mode 100644
index 0000000..abcc693
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Shared/ChannelHelper.cs
@@ -0,0 +1,19 @@
+using System.Runtime.CompilerServices;
+
+namespace LibWindPop.Utils.Graphics.Texture.Shared
+{
+ internal static class ChannelHelper
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static (byte, byte, byte) GetRGBFromL(byte l)
+ {
+ return (l, l, l);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static byte GetLFromRGB(byte r, byte g, byte b)
+ {
+ return (byte)((r * 77 + g * 150 + b * 29) >> 8);
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Shared/EACCoder.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Shared/EACCoder.cs
new file mode 100644
index 0000000..5693cfa
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Shared/EACCoder.cs
@@ -0,0 +1,295 @@
+using System;
+using System.Runtime.CompilerServices;
+
+namespace LibWindPop.Utils.Graphics.Texture.Shared
+{
+ internal static unsafe class EACCoder
+ {
+ private static bool HasInit = false;
+
+ private static void Init()
+ {
+ if (HasInit) return;
+ HasInit = true;
+ int count = 0;
+ for (int m_base = 0; m_base < 256; m_base++)
+ {
+ for (int tab = 0; tab < 16; tab++)
+ {
+ for (int mul = 0; mul < 16; mul++)
+ {
+ for (int index = 0; index < 8; index++)
+ {
+ valtab[count] = get16bits11bits(m_base, tab, mul, index);
+ count++;
+ }
+ }
+ }
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int getPremulIndex(int m_base, int tab, int mul, int index)
+ {
+ return (m_base << 11) + (tab << 7) + (mul << 3) + index;
+ }
+
+ private static double calcError(YFColor* data, int m_base, int tab, int mul, double prevbest, int colorKind)
+ {
+ int offset = getPremulIndex(m_base, tab, mul, 0);
+ double error = 0;
+ for (int y = 0; y < 4; y++)
+ {
+ for (int x = 0; x < 4; x++)
+ {
+ double besthere = (1 << 20);
+ besthere *= besthere;
+ byte m_byte = data[(y << 2) | x][colorKind];
+ int alpha = (m_byte << 8) | m_byte;
+ for (int index = 0; index < 8; index++)
+ {
+ double indexError;
+ indexError = alpha - valtab[offset + index];
+ indexError *= indexError;
+ if (indexError < besthere)
+ besthere = indexError;
+ }
+ error += besthere;
+ if (error >= prevbest)
+ return prevbest + (1 << 30);
+ }
+ }
+ return error;
+ }
+
+ ///
+ /// 非常非常慢,做好心理准备再用
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static void EncodeR11G11EACBlock(Span eacword, ReadOnlySpan encode_data)
+ {
+ fixed (byte* data = eacword)
+ {
+ fixed (YFColor* img = encode_data)
+ {
+ EncodeSingleWord(data, img, YFColor.RED);
+ EncodeSingleWord(data + 8, img, YFColor.GREEN);
+ }
+ }
+ }
+
+ public static void EncodeR11EACBlock(Span eacword, ReadOnlySpan encode_data)
+ {
+ fixed (byte* data = eacword)
+ {
+ fixed (YFColor* img = encode_data)
+ {
+ EncodeSingleWord(data, img, YFColor.RED);
+ }
+ }
+ }
+
+ ///
+ /// 非常非常慢,做好心理准备再用
+ ///
+ ///
+ ///
+ ///
+ private static void EncodeSingleWord(byte* data, YFColor* img, int colorKind)
+ {
+ Init();
+ uint bestbase = 0, besttable = 0, bestmul = 0;
+ double besterror;
+ besterror = 1 << 20;
+ besterror *= besterror;
+ for (int m_base = 0; m_base < 256; m_base++)
+ {
+ for (int table = 0; table < 16; table++)
+ {
+ for (int mul = 0; mul < 16; mul++)
+ {
+ double e = calcError(img, m_base, table, mul, besterror, colorKind);
+ if (e < besterror)
+ {
+ bestbase = (uint)m_base;
+ besttable = (uint)table;
+ bestmul = (uint)mul;
+ besterror = e;
+ }
+ }
+ }
+ }
+ data[0] = (byte)bestbase;
+ data[1] = (byte)((bestmul << 4) + besttable);
+
+ for (int i = 2; i < 8; i++)
+ {
+ data[i] = 0;
+ }
+
+ int m_byte2 = 2;
+ int bit = 0;
+ for (int x = 0; x < 4; x++)
+ {
+ for (int y = 0; y < 4; y++)
+ {
+ besterror = 255 * 255;
+ besterror *= besterror;
+ int bestindex = 99;
+ byte m_byte = img[(y << 2) | x][colorKind];
+ int alpha = (m_byte << 8) | m_byte;
+ for (uint index = 0; index < 8; index++)
+ {
+ double indexError = alpha - get16bits11bits((int)bestbase, (int)besttable, (int)bestmul, (int)index);
+
+ indexError *= indexError;
+ if (indexError < besterror)
+ {
+ besterror = indexError;
+ bestindex = (int)index;
+ }
+ }
+
+ for (int numbit = 0; numbit < 3; numbit++)
+ {
+ data[m_byte2] |= getbit((byte)bestindex, 2 - numbit, 7 - bit);
+ bit++;
+ if (bit > 7)
+ {
+ bit = 0;
+ m_byte2++;
+ }
+ }
+ }
+ }
+ }
+
+ public static void DecodeR11G11EACBlock(ReadOnlySpan eacword, Span decode_data)
+ {
+ fixed (byte* data = eacword)
+ {
+ fixed (YFColor* img = decode_data)
+ {
+ DecodeSingleWord(data, (uint*)img, YFColor.RED);
+ DecodeSingleWord(data + 8, (uint*)img, YFColor.GREEN);
+ }
+ }
+ }
+
+ public static void DecodeR11EACBlock(ReadOnlySpan eacword, Span decode_data)
+ {
+ fixed (byte* data = eacword)
+ {
+ fixed (YFColor* img = decode_data)
+ {
+ DecodeSingleWord(data, (uint*)img, YFColor.RED);
+ }
+ }
+ }
+
+ private static void DecodeSingleWord(byte* data, uint* img, int colorKind)
+ {
+ byte* color = (byte*)img;
+ int alpha = data[0];
+ int table = data[1];
+ int bit = 0;
+ int m_byte = 2;
+ //extract an alpha value for each pixel.
+ for (int x = 0; x < 4; x++)
+ {
+ for (int y = 0; y < 4; y++)
+ {
+ //Extract table index
+ int index = 0;
+ for (int bitpos = 0; bitpos < 3; bitpos++)
+ {
+ index |= getbit(data[m_byte], 7 - bit, 2 - bitpos);
+ bit++;
+ if (bit > 7)
+ {
+ bit = 0;
+ m_byte++;
+ }
+ }
+ //make data compatible with the .pgm format. See the comment in compressBlockAlpha16() for details.
+ ushort uSixteen = get16bits11bits(alpha, table & 15, table >> 4, index);
+ //byte swap for pgm
+ color[(((y << 2) | x) << 2) | colorKind] = (byte)(uSixteen >> 8);
+
+ }
+ }
+ }
+
+ private static ushort get16bits11bits(int m_base, int table, int mul, int index)
+ {
+ int elevenbase = m_base * 8 + 4;
+
+ //i want the positive value here
+ int tabVal = -alphaBase[table, 3 - index % 4] - 1;
+ //and the sign, please
+ int sign = 1 - (index / 4);
+
+ if (sign != 0)
+ tabVal = tabVal + 1;
+ int elevenTabVal = tabVal * 8;
+
+ if (mul != 0)
+ elevenTabVal *= mul;
+ else
+ elevenTabVal /= 8;
+
+ if (sign != 0)
+ elevenTabVal = -elevenTabVal;
+
+ //calculate sum
+ int elevenbits = elevenbase + elevenTabVal;
+
+ //clamp..
+ if (elevenbits >= 256 * 8)
+ elevenbits = 256 * 8 - 1;
+ else if (elevenbits < 0)
+ elevenbits = 0;
+ //elevenbits now contains the 11 bit alpha value as defined in the spec.
+
+ //extend to 16 bits before returning, since we don't have any good 11-bit file formats.
+ ushort sixteenbits = (ushort)((elevenbits << 5) + (elevenbits >> 6));
+
+ return sixteenbits;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static byte getbit(byte input, int frompos, int topos)
+ {
+ if (frompos > topos)
+ {
+ return (byte)(((1 << frompos) & input) >> (frompos - topos));
+ }
+ return (byte)(((1 << frompos) & input) << (topos - frompos));
+ }
+
+ private static int[] valtab = new int[1024 * 512];
+
+ private static int[,] alphaBase = new int[16, 4]
+ {
+ { -15, -9, -6, -3 },
+ { -13, -10, -7, -3 },
+ { -13, -8, -5, -2 },
+ { -13, -6, -4, -2 },
+ { -12, -8, -6, -3 },
+ { -11, -9, -7, -3 },
+ { -11, -8, -7, -4 },
+ { -11, -8, -5, -3 },
+ { -10, -8, -6, -2 },
+ { -10, -8, -5, -2 },
+ { -10, -8, -4, -2 },
+ { -10, -7, -5, -2 },
+ { -10, -7, -4, -3 },
+ { -10, -3, -2, -1 },
+ { -9, -8, -6, -4 },
+ { -9, -7, -5, -3 }
+ };
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Shared/ETCCoder.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Shared/ETCCoder.cs
new file mode 100644
index 0000000..2ac28b7
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Shared/ETCCoder.cs
@@ -0,0 +1,1906 @@
+using System;
+using System.Buffers.Binary;
+using System.Runtime.CompilerServices;
+
+namespace LibWindPop.Utils.Graphics.Texture.Shared
+{
+ ///
+ /// 这个类提供的解码方法是移植爱立信的ETCPACK中的c/c++代码的
+ /// C#没有那种预编译指令,所以我把一些东西变成了函数
+ ///
+ internal static unsafe class ETCCoder
+ {
+ public static void DecodeR8G8B8A8ETC2EACBlock(ReadOnlySpan etcword, Span decode_data)
+ {
+ fixed (byte* texPtr = etcword)
+ {
+ fixed (YFColor* dataPtr = decode_data)
+ {
+ ETCDecoder.DecodeBlock_RGBA_ETC2_EAC(texPtr, dataPtr);
+ }
+ }
+ }
+
+ public static void DecodeR8G8B8A1ETC2Block(ReadOnlySpan etcword, Span decode_data)
+ {
+ fixed (byte* texPtr = etcword)
+ {
+ fixed (YFColor* dataPtr = decode_data)
+ {
+ ETCDecoder.DecodeBlock_RGB_A1_ETC2(texPtr, dataPtr);
+ }
+ }
+ }
+
+ public static void DecodeR8G8B8ETC2Block(ReadOnlySpan etcword, Span decode_data)
+ {
+ fixed (byte* texPtr = etcword)
+ {
+ fixed (YFColor* dataPtr = decode_data)
+ {
+ ETCDecoder.DecodeBlock_RGB_ETC2(texPtr, dataPtr);
+ }
+ }
+ }
+
+ public static void DecodeR8G8B8ETC1Block(ReadOnlySpan etcword, Span decode_data)
+ {
+ fixed (byte* texPtr = etcword)
+ {
+ fixed (YFColor* dataPtr = decode_data)
+ {
+ ETCDecoder.DecodeBlock_RGB_ETC1(texPtr, dataPtr);
+ }
+ }
+ }
+
+ public static void EncodeR8G8B8A8ETC2EACBlock(Span etcword, ReadOnlySpan encode_data)
+ {
+ //fixed (byte* texPtr = etcword)
+ //{
+ // fixed (YFColor* dataPtr = encode_data)
+ // {
+ // ETCDecoder.DecodeBlock_RGBA_ETC2_EAC(texPtr, dataPtr);
+ // }
+ //}
+ }
+
+ public static void EncodeR8G8B8A1ETC2Block(Span etcword, ReadOnlySpan encode_data)
+ {
+ //fixed (byte* texPtr = etcword)
+ //{
+ // fixed (YFColor* dataPtr = encode_data)
+ // {
+ // ETCDecoder.DecodeBlock_RGB_A1_ETC2(texPtr, dataPtr);
+ // }
+ //}
+ }
+
+ public static void EncodeR8G8B8ETC2Block(Span etcword, ReadOnlySpan encode_data)
+ {
+ //fixed (byte* texPtr = etcword)
+ //{
+ // fixed (YFColor* dataPtr = encode_data)
+ // {
+ // ETCDecoder.DecodeBlock_RGB_ETC2(texPtr, dataPtr);
+ // }
+ //}
+ }
+
+ public static void EncodeR8G8B8ETC1Block(Span etcword, ReadOnlySpan encode_data)
+ {
+ fixed (YFColor* dataPtr = encode_data)
+ {
+ ulong data = ETC1Encoder.GenETC1(dataPtr);
+ BinaryPrimitives.WriteUInt64BigEndian(etcword, data);
+ }
+ }
+
+ private static class ETCDecoder
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static uint GETBITS(uint source, int size, int startpos)
+ {
+ return (((source) >> (startpos - size + 1)) & ((1U << (size)) - 1));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static uint GETBITSHIGH(uint source, int size, int startpos)
+ {
+ return ((source) >> (startpos - size - 31)) & ((1U << (size)) - 1);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static int SHIFT(int size, int startpos)
+ {
+ return startpos - size + 1;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static int SHIFTHIGH(int size, int startpos)
+ {
+ return startpos - size - 31;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static int MASK(int size, int startpos)
+ {
+ return ((2 << (size - 1)) - 1) << SHIFT(size, startpos);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static int MASKHIGH(int size, int startpos)
+ {
+ return ((1 << (size)) - 1) << SHIFTHIGH(size, startpos);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static void PUTBITS(uint* dest, int data, int size, int startpos)
+ {
+ *dest = ((uint)(((int)*dest & ~MASK(size, startpos)) | ((data << SHIFT(size, startpos)) & MASK(size, startpos))));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static void PUTBITSHIGH(uint* dest, int data, int size, int startpos)
+ {
+ *dest = ((uint)(((int)*dest & ~MASKHIGH(size, startpos)) | ((data << SHIFTHIGH(size, startpos)) & MASKHIGH(size, startpos))));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static int CLAMP(int ll, int x, int ul)
+ {
+ return (((x) < (ll)) ? (ll) : (((x) > (ul)) ? (ul) : (x)));
+ }
+
+ static void unstuff57bits(uint planar_word1, uint planar_word2, uint* planar57_word1, uint* planar57_word2)
+ {
+ byte RO, GO1, GO2, BO1, BO2, BO3, RH1, RH2, GH, BH, RV, GV, BV;
+
+ RO = (byte)GETBITSHIGH(planar_word1, 6, 62);
+ GO1 = (byte)GETBITSHIGH(planar_word1, 1, 56);
+ GO2 = (byte)GETBITSHIGH(planar_word1, 6, 54);
+ BO1 = (byte)GETBITSHIGH(planar_word1, 1, 48);
+ BO2 = (byte)GETBITSHIGH(planar_word1, 2, 44);
+ BO3 = (byte)GETBITSHIGH(planar_word1, 3, 41);
+ RH1 = (byte)GETBITSHIGH(planar_word1, 5, 38);
+ RH2 = (byte)GETBITSHIGH(planar_word1, 1, 32);
+ GH = (byte)GETBITS(planar_word2, 7, 31);
+ BH = (byte)GETBITS(planar_word2, 6, 24);
+ RV = (byte)GETBITS(planar_word2, 6, 18);
+ GV = (byte)GETBITS(planar_word2, 7, 12);
+ BV = (byte)GETBITS(planar_word2, 6, 5);
+
+ *planar57_word1 = 0;
+ *planar57_word2 = 0;
+ PUTBITSHIGH(planar57_word1, RO, 6, 63);
+ PUTBITSHIGH(planar57_word1, GO1, 1, 57);
+ PUTBITSHIGH(planar57_word1, GO2, 6, 56);
+ PUTBITSHIGH(planar57_word1, BO1, 1, 50);
+ PUTBITSHIGH(planar57_word1, BO2, 2, 49);
+ PUTBITSHIGH(planar57_word1, BO3, 3, 47);
+ PUTBITSHIGH(planar57_word1, RH1, 5, 44);
+ PUTBITSHIGH(planar57_word1, RH2, 1, 39);
+ PUTBITSHIGH(planar57_word1, GH, 7, 38);
+ PUTBITS(planar57_word2, BH, 6, 31);
+ PUTBITS(planar57_word2, RV, 6, 25);
+ PUTBITS(planar57_word2, GV, 7, 19);
+ PUTBITS(planar57_word2, BV, 6, 12);
+ }
+
+ static void unstuff58bits(uint thumbH_word1, uint thumbH_word2, uint* thumbH58_word1, uint* thumbH58_word2)
+ {
+ uint part0, part1, part2, part3;
+
+ // move parts
+ part0 = GETBITSHIGH(thumbH_word1, 7, 62);
+ part1 = GETBITSHIGH(thumbH_word1, 2, 52);
+ part2 = GETBITSHIGH(thumbH_word1, 16, 49);
+ part3 = GETBITSHIGH(thumbH_word1, 1, 32);
+ *thumbH58_word1 = 0;
+ PUTBITSHIGH(thumbH58_word1, (int)part0, 7, 57);
+ PUTBITSHIGH(thumbH58_word1, (int)part1, 2, 50);
+ PUTBITSHIGH(thumbH58_word1, (int)part2, 16, 48);
+ PUTBITSHIGH(thumbH58_word1, (int)part3, 1, 32);
+
+ *thumbH58_word2 = thumbH_word2;
+ }
+
+ static void unstuff59bits(uint thumbT_word1, uint thumbT_word2, uint* thumbT59_word1, uint* thumbT59_word2)
+ {
+ byte R0a;
+
+ // Fix middle part
+ *thumbT59_word1 = thumbT_word1 >> 1;
+ // Fix db (lowest bit of d)
+ PUTBITSHIGH(thumbT59_word1, (int)thumbT_word1, 1, 32);
+ // Fix R0a (top two bits of R0)
+ R0a = (byte)GETBITSHIGH(thumbT_word1, 2, 60);
+ PUTBITSHIGH(thumbT59_word1, R0a, 2, 58);
+
+ // Zero top part (not needed)
+ PUTBITSHIGH(thumbT59_word1, 0, 5, 63);
+
+ *thumbT59_word2 = thumbT_word2;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static void decompressColor(int R_B, int G_B, int B_B, byte[,] colors_RGB444, byte[,] colors)
+ {
+
+ colors[0, 0] = (byte)((colors_RGB444[0, 0] << (8 - R_B)) | (colors_RGB444[0, 0] >> (R_B - (8 - R_B))));
+ colors[0, 1] = (byte)((colors_RGB444[0, 1] << (8 - G_B)) | (colors_RGB444[0, 1] >> (G_B - (8 - G_B))));
+ colors[0, 2] = (byte)((colors_RGB444[0, 2] << (8 - B_B)) | (colors_RGB444[0, 2] >> (B_B - (8 - B_B))));
+ colors[1, 0] = (byte)((colors_RGB444[1, 0] << (8 - R_B)) | (colors_RGB444[1, 0] >> (R_B - (8 - R_B))));
+ colors[1, 1] = (byte)((colors_RGB444[1, 1] << (8 - G_B)) | (colors_RGB444[1, 1] >> (G_B - (8 - G_B))));
+ colors[1, 2] = (byte)((colors_RGB444[1, 2] << (8 - B_B)) | (colors_RGB444[1, 2] >> (B_B - (8 - B_B))));
+ }
+
+ static void decompressBlockPlanar57c(uint compressed57_1, uint compressed57_2, YFColor* img)
+ {
+ byte* colorO = stackalloc byte[3];
+ byte* colorH = stackalloc byte[3];
+ byte* colorV = stackalloc byte[3];
+
+ colorO[0] = (byte)GETBITSHIGH(compressed57_1, 6, 63);
+ colorO[1] = (byte)GETBITSHIGH(compressed57_1, 7, 57);
+ colorO[2] = (byte)GETBITSHIGH(compressed57_1, 6, 50);
+ colorH[0] = (byte)GETBITSHIGH(compressed57_1, 6, 44);
+ colorH[1] = (byte)GETBITSHIGH(compressed57_1, 7, 38);
+ colorH[2] = (byte)GETBITS(compressed57_2, 6, 31);
+ colorV[0] = (byte)GETBITS(compressed57_2, 6, 25);
+ colorV[1] = (byte)GETBITS(compressed57_2, 7, 19);
+ colorV[2] = (byte)GETBITS(compressed57_2, 6, 12);
+
+ colorO[0] = (byte)((colorO[0] << 2) | (colorO[0] >> 4));
+ colorO[1] = (byte)((colorO[1] << 1) | (colorO[1] >> 6));
+ colorO[2] = (byte)((colorO[2] << 2) | (colorO[2] >> 4));
+
+ colorH[0] = (byte)((colorH[0] << 2) | (colorH[0] >> 4));
+ colorH[1] = (byte)((colorH[1] << 1) | (colorH[1] >> 6));
+ colorH[2] = (byte)((colorH[2] << 2) | (colorH[2] >> 4));
+
+ colorV[0] = (byte)((colorV[0] << 2) | (colorV[0] >> 4));
+ colorV[1] = (byte)((colorV[1] << 1) | (colorV[1] >> 6));
+ colorV[2] = (byte)((colorV[2] << 2) | (colorV[2] >> 4));
+
+ int xx, yy;
+
+ for (xx = 0; xx < 4; xx++)
+ {
+ for (yy = 0; yy < 4; yy++)
+ {
+ img[(yy << 2) | xx].Red = (byte)CLAMP(0, (xx * (colorH[0] - colorO[0]) + yy * (colorV[0] - colorO[0]) + 4 * colorO[0] + 2) >> 2, 255);
+ img[(yy << 2) | xx].Green = (byte)CLAMP(0, (xx * (colorH[1] - colorO[1]) + yy * (colorV[1] - colorO[1]) + 4 * colorO[1] + 2) >> 2, 255);
+ img[(yy << 2) | xx].Blue = (byte)CLAMP(0, (xx * (colorH[2] - colorO[2]) + yy * (colorV[2] - colorO[2]) + 4 * colorO[2] + 2) >> 2, 255);
+
+ //Equivalent method
+ /*img[channels*width*(starty+yy) + channels*(startx+xx) + 0] = (int)CLAMP(0, JAS_ROUND((xx*(colorH[0]-colorO[0])/4.0 + yy*(colorV[0]-colorO[0])/4.0 + colorO[0])), 255);
+ img[channels*width*(starty+yy) + channels*(startx+xx) + 1] = (int)CLAMP(0, JAS_ROUND((xx*(colorH[1]-colorO[1])/4.0 + yy*(colorV[1]-colorO[1])/4.0 + colorO[1])), 255);
+ img[channels*width*(starty+yy) + channels*(startx+xx) + 2] = (int)CLAMP(0, JAS_ROUND((xx*(colorH[2]-colorO[2])/4.0 + yy*(colorV[2]-colorO[2])/4.0 + colorO[2])), 255);*/
+
+ }
+ }
+ }
+
+ static void calculatePaintColors58H(byte d, byte p, byte[,] colors, byte[,] possible_colors)
+ {
+ possible_colors[3, 0] = (byte)CLAMP(0, colors[1, 0] - table58H[d], 255);
+ possible_colors[3, 1] = (byte)CLAMP(0, colors[1, 1] - table58H[d], 255);
+ possible_colors[3, 2] = (byte)CLAMP(0, colors[1, 2] - table58H[d], 255);
+
+ if (p == 0)
+ {
+ // C1
+ possible_colors[0, 0] = (byte)CLAMP(0, colors[0, 0] + table58H[d], 255);
+ possible_colors[0, 1] = (byte)CLAMP(0, colors[0, 1] + table58H[d], 255);
+ possible_colors[0, 2] = (byte)CLAMP(0, colors[0, 2] + table58H[d], 255);
+ // C2
+ possible_colors[1, 0] = (byte)CLAMP(0, colors[0, 0] - table58H[d], 255);
+ possible_colors[1, 1] = (byte)CLAMP(0, colors[0, 1] - table58H[d], 255);
+ possible_colors[1, 2] = (byte)CLAMP(0, colors[0, 2] - table58H[d], 255);
+ // C3
+ possible_colors[2, 0] = (byte)CLAMP(0, colors[1, 0] + table58H[d], 255);
+ possible_colors[2, 1] = (byte)CLAMP(0, colors[1, 1] + table58H[d], 255);
+ possible_colors[2, 2] = (byte)CLAMP(0, colors[1, 2] + table58H[d], 255);
+ }
+ else
+ {
+ throw new Exception("Invalid pattern. Terminating");
+ }
+ }
+
+ static void decompressBlockTHUMB58Hc(uint block_part1, uint block_part2, YFColor* img)
+ {
+ uint col0, col1;
+ byte[,] colors = new byte[2, 3];
+ byte[,] colorsRGB444 = new byte[2, 3];
+ byte[,] paint_colors = new byte[4, 3];
+ byte distance;
+ byte[,] block_mask = new byte[4, 4];
+
+ // First decode left part of block.
+ colorsRGB444[0, 0] = (byte)GETBITSHIGH(block_part1, 4, 57);
+ colorsRGB444[0, 1] = (byte)GETBITSHIGH(block_part1, 4, 53);
+ colorsRGB444[0, 2] = (byte)GETBITSHIGH(block_part1, 4, 49);
+
+ colorsRGB444[1, 0] = (byte)GETBITSHIGH(block_part1, 4, 45);
+ colorsRGB444[1, 1] = (byte)GETBITSHIGH(block_part1, 4, 41);
+ colorsRGB444[1, 2] = (byte)GETBITSHIGH(block_part1, 4, 37);
+
+ distance = 0;
+ distance = (byte)((GETBITSHIGH(block_part1, 2, 33)) << 1);
+
+ col0 = GETBITSHIGH(block_part1, 12, 57);
+ col1 = GETBITSHIGH(block_part1, 12, 45);
+
+ if (col0 >= col1)
+ {
+ distance |= 1;
+ }
+
+ // Extend the two colors to RGB888
+ decompressColor(4, 4, 4, colorsRGB444, colors);
+
+ calculatePaintColors58H(distance, 0, colors, paint_colors);
+
+ // Choose one of the four paint colors for each texel
+ for (byte x = 0; x < 4; ++x)
+ {
+ for (byte y = 0; y < 4; ++y)
+ {
+ //block_mask[x][y] = GETBITS(block_part2,2,31-(y*4+x)*2);
+ block_mask[x, y] = (byte)(GETBITS(block_part2, 1, (y + x * 4) + 16) << 1);
+ block_mask[x, y] |= (byte)GETBITS(block_part2, 1, (y + x * 4));
+ img[(y << 2) | x].Red =
+ (byte)CLAMP(0, paint_colors[block_mask[x, y], 0], 255); // RED
+ img[(y << 2) | x].Green =
+ (byte)CLAMP(0, paint_colors[block_mask[x, y], 1], 255); // GREEN
+ img[(y << 2) | x].Blue =
+ (byte)CLAMP(0, paint_colors[block_mask[x, y], 2], 255); // BLUE
+ }
+ }
+ }
+
+ static void calculatePaintColors59T(byte d, byte p, byte[,] colors, byte[,] possible_colors)
+ {
+ possible_colors[3, 0] = (byte)CLAMP(0, colors[1, 0] - table59T[d], 255);
+ possible_colors[3, 1] = (byte)CLAMP(0, colors[1, 1] - table59T[d], 255);
+ possible_colors[3, 2] = (byte)CLAMP(0, colors[1, 2] - table59T[d], 255);
+
+ if (p == 1)
+ {
+ possible_colors[0, 0] = colors[0, 0];
+ possible_colors[0, 1] = colors[0, 1];
+ possible_colors[0, 2] = colors[0, 2];
+ // C2
+ possible_colors[1, 0] = (byte)CLAMP(0, colors[1, 0] + table59T[d], 255);
+ possible_colors[1, 1] = (byte)CLAMP(0, colors[1, 1] + table59T[d], 255);
+ possible_colors[1, 2] = (byte)CLAMP(0, colors[1, 2] + table59T[d], 255);
+ // C1
+ possible_colors[2, 0] = colors[1, 0];
+ possible_colors[2, 1] = colors[1, 1];
+ possible_colors[2, 2] = colors[1, 2];
+
+ }
+ else
+ {
+ throw new Exception("Invalid pattern. Terminating");
+ }
+ }
+
+ static void decompressBlockTHUMB59Tc(uint block_part1, uint block_part2, YFColor* img)
+ {
+ byte[,] colorsRGB444 = new byte[2, 3];
+ byte[,] colors = new byte[2, 3];
+ byte[,] paint_colors = new byte[4, 3];
+ byte distance;
+ byte[,] block_mask = new byte[4, 4];
+
+ // First decode left part of block.
+ colorsRGB444[0, 0] = (byte)GETBITSHIGH(block_part1, 4, 58);
+ colorsRGB444[0, 1] = (byte)GETBITSHIGH(block_part1, 4, 54);
+ colorsRGB444[0, 2] = (byte)GETBITSHIGH(block_part1, 4, 50);
+
+ colorsRGB444[1, 0] = (byte)GETBITSHIGH(block_part1, 4, 46);
+ colorsRGB444[1, 1] = (byte)GETBITSHIGH(block_part1, 4, 42);
+ colorsRGB444[1, 2] = (byte)GETBITSHIGH(block_part1, 4, 38);
+
+ distance = (byte)GETBITSHIGH(block_part1, 3, 34);
+
+ // Extend the two colors to RGB888
+ decompressColor(4, 4, 4, colorsRGB444, colors);
+ calculatePaintColors59T(distance, 1, colors, paint_colors);
+
+ // Choose one of the four paint colors for each texel
+ for (byte x = 0; x < 4; ++x)
+ {
+ for (byte y = 0; y < 4; ++y)
+ {
+ //block_mask[x][y] = GETBITS(block_part2,2,31-(y*4+x)*2);
+ block_mask[x, y] = (byte)(GETBITS(block_part2, 1, y + x * 4 + 16) << 1);
+ block_mask[x, y] |= (byte)GETBITS(block_part2, 1, y + x * 4);
+ img[(y << 2) | x].Red = (byte)CLAMP(0, paint_colors[block_mask[x, y], 0], 255); // RED
+ img[(y << 2) | x].Green = (byte)CLAMP(0, paint_colors[block_mask[x, y], 1], 255); // GREEN
+ img[(y << 2) | x].Blue = (byte)CLAMP(0, paint_colors[block_mask[x, y], 2], 255); // BLUE
+ }
+ }
+ }
+
+ static void decompressBlockETC2c(uint block_part1, uint block_part2, YFColor* img)
+ {
+ int diffbit;
+ sbyte* color1 = stackalloc sbyte[3];
+ sbyte* diff = stackalloc sbyte[3];
+ sbyte red, green, blue;
+
+ diffbit = (int)GETBITSHIGH(block_part1, 1, 33);
+
+ if (diffbit != 0)
+ {
+ // We have diffbit = 1;
+
+ // Base color
+ color1[0] = (sbyte)GETBITSHIGH(block_part1, 5, 63);
+ color1[1] = (sbyte)GETBITSHIGH(block_part1, 5, 55);
+ color1[2] = (sbyte)GETBITSHIGH(block_part1, 5, 47);
+
+ // Diff color
+ diff[0] = (sbyte)GETBITSHIGH(block_part1, 3, 58);
+ diff[1] = (sbyte)GETBITSHIGH(block_part1, 3, 50);
+ diff[2] = (sbyte)GETBITSHIGH(block_part1, 3, 42);
+
+ // Extend sign bit to entire byte.
+ diff[0] <<= 5;
+ diff[1] <<= 5;
+ diff[2] <<= 5;
+ diff[0] >>= 5;
+ diff[1] >>= 5;
+ diff[2] >>= 5;
+
+ red = (sbyte)(color1[0] + diff[0]);
+ green = (sbyte)(color1[1] + diff[1]);
+ blue = (sbyte)(color1[2] + diff[2]);
+
+ if (red < 0 || red > 31)
+ {
+ uint block59_part1, block59_part2;
+ unstuff59bits(block_part1, block_part2, &block59_part1, &block59_part2);
+ decompressBlockTHUMB59Tc(block59_part1, block59_part2, img);
+ }
+ else if (green < 0 || green > 31)
+ {
+ uint block58_part1, block58_part2;
+ unstuff58bits(block_part1, block_part2, &block58_part1, &block58_part2);
+ decompressBlockTHUMB58Hc(block58_part1, block58_part2, img);
+ }
+ else if (blue < 0 || blue > 31)
+ {
+ uint block57_part1, block57_part2;
+
+ unstuff57bits(block_part1, block_part2, &block57_part1, &block57_part2);
+ decompressBlockPlanar57c(block57_part1, block57_part2, img);
+ }
+ else
+ {
+ decompressBlockDiffFlipC(block_part1, block_part2, img);
+ }
+ }
+ else
+ {
+ // We have diffbit = 0;
+ decompressBlockDiffFlipC(block_part1, block_part2, img);
+ }
+ }
+
+ static void decompressBlockDifferentialWithAlphaC(uint block_part1, uint block_part2, YFColor* img)
+ {
+ byte* avg_color = stackalloc byte[3];
+ byte* enc_color1 = stackalloc byte[3];
+ byte* enc_color2 = stackalloc byte[3];
+ sbyte* diff = stackalloc sbyte[3];
+ int table;
+ int index, shift;
+ int r, g, b;
+ int diffbit;
+ int flipbit;
+
+ //the diffbit now encodes whether or not the entire alpha channel is 255.
+ diffbit = (int)GETBITSHIGH(block_part1, 1, 33);
+ flipbit = (int)GETBITSHIGH(block_part1, 1, 32);
+
+ // First decode left part of block.
+ enc_color1[0] = (byte)GETBITSHIGH(block_part1, 5, 63);
+ enc_color1[1] = (byte)GETBITSHIGH(block_part1, 5, 55);
+ enc_color1[2] = (byte)GETBITSHIGH(block_part1, 5, 47);
+
+ // Expand from 5 to 8 bits
+ avg_color[0] = (byte)((enc_color1[0] << 3) | (enc_color1[0] >> 2));
+ avg_color[1] = (byte)((enc_color1[1] << 3) | (enc_color1[1] >> 2));
+ avg_color[2] = (byte)((enc_color1[2] << 3) | (enc_color1[2] >> 2));
+
+ table = (int)(GETBITSHIGH(block_part1, 3, 39) << 1);
+
+ uint pixel_indices_MSB, pixel_indices_LSB;
+
+ pixel_indices_MSB = GETBITS(block_part2, 16, 31);
+ pixel_indices_LSB = GETBITS(block_part2, 16, 15);
+
+ if ((flipbit) == 0)
+ {
+ // We should not flip
+ shift = 0;
+ for (int x = 0; x < 2; x++)
+ {
+ for (int y = 0; y < 4; y++)
+ {
+ index = (int)(((pixel_indices_MSB >> shift) & 1) << 1);
+ index |= (int)((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index = unscramble[index];
+
+ int mod = compressParams[table, index];
+ if (diffbit == 0 && (index == 1 || index == 2))
+ {
+ mod = 0;
+ }
+
+ r = img[(y << 2) | x].Red = (byte)CLAMP(0, avg_color[0] + mod, 255);
+ g = img[(y << 2) | x].Green = (byte)CLAMP(0, avg_color[1] + mod, 255);
+ b = img[(y << 2) | x].Blue = (byte)CLAMP(0, avg_color[2] + mod, 255);
+ if (diffbit == 0 && index == 1)
+ {
+ img[(y << 2) | x].Alpha = 0;
+ r = img[(y << 2) | x].Red = 0;
+ g = img[(y << 2) | x].Green = 0;
+ b = img[(y << 2) | x].Blue = 0;
+ }
+ else
+ {
+ img[(y << 2) | x].Alpha = 255;
+ }
+
+ }
+ }
+ }
+ else
+ {
+ // We should flip
+ shift = 0;
+ for (int x = 0; x < 4; x++)
+ {
+ for (int y = 0; y < 2; y++)
+ {
+ index = (int)(((pixel_indices_MSB >> shift) & 1) << 1);
+ index |= (int)((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index = unscramble[index];
+ int mod = compressParams[table, index];
+ if (diffbit == 0 && (index == 1 || index == 2))
+ {
+ mod = 0;
+ }
+ r = img[(y << 2) | x].Red = (byte)CLAMP(0, avg_color[0] + mod, 255);
+ g = img[(y << 2) | x].Green = (byte)CLAMP(0, avg_color[1] + mod, 255);
+ b = img[(y << 2) | x].Blue = (byte)CLAMP(0, avg_color[2] + mod, 255);
+ if (diffbit == 0 && index == 1)
+ {
+ img[(y << 2) | x].Alpha = 0;
+ r = img[(y << 2) | x].Red = 0;
+ g = img[(y << 2) | x].Green = 0;
+ b = img[(y << 2) | x].Blue = 0;
+ }
+ else
+ {
+ img[(y << 2) | x].Alpha = 255;
+ }
+ }
+ shift += 2;
+ }
+ }
+ // Now decode right part of block.
+ diff[0] = (sbyte)GETBITSHIGH(block_part1, 3, 58);
+ diff[1] = (sbyte)GETBITSHIGH(block_part1, 3, 50);
+ diff[2] = (sbyte)GETBITSHIGH(block_part1, 3, 42);
+
+ // Extend sign bit to entire byte.
+ diff[0] <<= 5;
+ diff[1] <<= 5;
+ diff[2] <<= 5;
+ diff[0] >>= 5;
+ diff[1] >>= 5;
+ diff[2] >>= 5;
+
+ // Calculate second color
+ enc_color2[0] = (byte)(enc_color1[0] + diff[0]);
+ enc_color2[1] = (byte)(enc_color1[1] + diff[1]);
+ enc_color2[2] = (byte)(enc_color1[2] + diff[2]);
+
+ // Expand from 5 to 8 bits
+ avg_color[0] = (byte)((enc_color2[0] << 3) | (enc_color2[0] >> 2));
+ avg_color[1] = (byte)((enc_color2[1] << 3) | (enc_color2[1] >> 2));
+ avg_color[2] = (byte)((enc_color2[2] << 3) | (enc_color2[2] >> 2));
+
+ table = (int)(GETBITSHIGH(block_part1, 3, 36) << 1);
+ pixel_indices_MSB = GETBITS(block_part2, 16, 31);
+ pixel_indices_LSB = GETBITS(block_part2, 16, 15);
+
+ if ((flipbit) == 0)
+ {
+ // We should not flip
+ shift = 8;
+ for (int x = 2; x < 4; x++)
+ {
+ for (int y = 0; y < 4; y++)
+ {
+ index = (int)(((pixel_indices_MSB >> shift) & 1) << 1);
+ index |= (int)((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index = unscramble[index];
+ int mod = compressParams[table, index];
+ if (diffbit == 0 && (index == 1 || index == 2))
+ {
+ mod = 0;
+ }
+
+ r = img[(y << 2) | x].Red = (byte)CLAMP(0, avg_color[0] + mod, 255);
+ g = img[(y << 2) | x].Green = (byte)CLAMP(0, avg_color[1] + mod, 255);
+ b = img[(y << 2) | x].Blue = (byte)CLAMP(0, avg_color[2] + mod, 255);
+ if (diffbit == 0 && index == 1)
+ {
+ img[(y << 2) | x].Alpha = 0;
+ r = img[(y << 2) | x].Red = 0;
+ g = img[(y << 2) | x].Green = 0;
+ b = img[(y << 2) | x].Blue = 0;
+ }
+ else
+ {
+ img[(y << 2) | x].Alpha = 255;
+ }
+ }
+ }
+ }
+ else
+ {
+ // We should flip
+ shift = 2;
+ for (int x = 0; x < 4; x++)
+ {
+ for (int y = 2; y < 4; y++)
+ {
+ index = (int)(((pixel_indices_MSB >> shift) & 1) << 1);
+ index |= (int)((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index = unscramble[index];
+ int mod = compressParams[table, index];
+ if (diffbit == 0 && (index == 1 || index == 2))
+ {
+ mod = 0;
+ }
+
+ r = img[(y << 2) | x].Red = (byte)CLAMP(0, avg_color[0] + mod, 255);
+ g = img[(y << 2) | x].Green = (byte)CLAMP(0, avg_color[1] + mod, 255);
+ b = img[(y << 2) | x].Blue = (byte)CLAMP(0, avg_color[2] + mod, 255);
+ if (diffbit == 0 && index == 1)
+ {
+ img[(y << 2) | x].Alpha = 0;
+ r = img[(y << 2) | x].Red = 0;
+ g = img[(y << 2) | x].Green = 0;
+ b = img[(y << 2) | x].Blue = 0;
+ }
+ else
+ {
+ img[(y << 2) | x].Alpha = 255;
+ }
+ }
+ shift += 2;
+ }
+ }
+ }
+
+ static void decompressBlockTHUMB58HAlphaC(uint block_part1, uint block_part2, YFColor* img)
+ {
+ uint col0, col1;
+ byte[,] colors = new byte[2, 3];
+ byte[,] colorsRGB444 = new byte[2, 3];
+ byte[,] paint_colors = new byte[4, 3];
+ byte distance;
+ byte[,] block_mask = new byte[4, 4];
+
+ // First decode left part of block.
+ colorsRGB444[0, 0] = (byte)GETBITSHIGH(block_part1, 4, 57);
+ colorsRGB444[0, 1] = (byte)GETBITSHIGH(block_part1, 4, 53);
+ colorsRGB444[0, 2] = (byte)GETBITSHIGH(block_part1, 4, 49);
+
+ colorsRGB444[1, 0] = (byte)GETBITSHIGH(block_part1, 4, 45);
+ colorsRGB444[1, 1] = (byte)GETBITSHIGH(block_part1, 4, 41);
+ colorsRGB444[1, 2] = (byte)GETBITSHIGH(block_part1, 4, 37);
+
+ distance = 0;
+ distance = (byte)((GETBITSHIGH(block_part1, 2, 33)) << 1);
+
+ col0 = GETBITSHIGH(block_part1, 12, 57);
+ col1 = GETBITSHIGH(block_part1, 12, 45);
+
+ if (col0 >= col1)
+ {
+ distance |= 1;
+ }
+
+ // Extend the two colors to RGB888
+ decompressColor(4, 4, 4, colorsRGB444, colors);
+
+ calculatePaintColors58H(distance, 0, colors, paint_colors);
+
+ // Choose one of the four paint colors for each texel
+ for (byte x = 0; x < 4; ++x)
+ {
+ for (byte y = 0; y < 4; ++y)
+ {
+ //block_mask[x][y] = GETBITS(block_part2,2,31-(y*4+x)*2);
+ block_mask[x, y] = (byte)(GETBITS(block_part2, 1, (y + x * 4) + 16) << 1);
+ block_mask[x, y] |= (byte)GETBITS(block_part2, 1, (y + x * 4));
+ img[(y << 2) | x].Red =
+ (byte)CLAMP(0, paint_colors[block_mask[x, y], 0], 255); // RED
+ img[(y << 2) | x].Green =
+ (byte)CLAMP(0, paint_colors[block_mask[x, y], 1], 255); // GREEN
+ img[(y << 2) | x].Blue =
+ (byte)CLAMP(0, paint_colors[block_mask[x, y], 2], 255); // BLUE
+
+ if (block_mask[x, y] == 2)
+ {
+ img[(y << 2) | x].Alpha = 0;
+ img[(y << 2) | x].Red = 0;
+ img[(y << 2) | x].Green = 0;
+ img[(y << 2) | x].Blue = 0;
+ }
+ else
+ {
+ img[(y << 2) | x].Alpha = 255;
+ }
+ }
+ }
+ }
+
+ static void decompressBlockTHUMB59TAlphaC(uint block_part1, uint block_part2, YFColor* img)
+ {
+
+ byte[,] colorsRGB444 = new byte[2, 3];
+ byte[,] colors = new byte[2, 3];
+ byte[,] paint_colors = new byte[4, 3];
+ byte distance;
+ byte[,] block_mask = new byte[4, 4];
+
+ // First decode left part of block.
+ colorsRGB444[0, 0] = (byte)GETBITSHIGH(block_part1, 4, 58);
+ colorsRGB444[0, 1] = (byte)GETBITSHIGH(block_part1, 4, 54);
+ colorsRGB444[0, 2] = (byte)GETBITSHIGH(block_part1, 4, 50);
+
+ colorsRGB444[1, 0] = (byte)GETBITSHIGH(block_part1, 4, 46);
+ colorsRGB444[1, 1] = (byte)GETBITSHIGH(block_part1, 4, 42);
+ colorsRGB444[1, 2] = (byte)GETBITSHIGH(block_part1, 4, 38);
+
+ distance = (byte)GETBITSHIGH(block_part1, 3, 34);
+
+ // Extend the two colors to RGB888
+ decompressColor(4, 4, 4, colorsRGB444, colors);
+ calculatePaintColors59T(distance, 1, colors, paint_colors);
+
+ // Choose one of the four paint colors for each texel
+ for (byte x = 0; x < 4; ++x)
+ {
+ for (byte y = 0; y < 4; ++y)
+ {
+ //block_mask[x][y] = GETBITS(block_part2,2,31-(y*4+x)*2);
+ block_mask[x, y] = (byte)(GETBITS(block_part2, 1, (y + x * 4) + 16) << 1);
+ block_mask[x, y] |= (byte)GETBITS(block_part2, 1, (y + x * 4));
+ img[(y << 2) | x].Red =
+ (byte)CLAMP(0, paint_colors[block_mask[x, y], 0], 255); // RED
+ img[(y << 2) | x].Green =
+ (byte)CLAMP(0, paint_colors[block_mask[x, y], 1], 255); // GREEN
+ img[(y << 2) | x].Blue =
+ (byte)CLAMP(0, paint_colors[block_mask[x, y], 2], 255); // BLUE
+ if (block_mask[x, y] == 2)
+ {
+ img[(y << 2) | x].Alpha = 0;
+ img[(y << 2) | x].Red = 0;
+ img[(y << 2) | x].Green = 0;
+ img[(y << 2) | x].Blue = 0;
+ }
+ else
+ {
+ img[(y << 2) | x].Alpha = 0xFF;
+ }
+ }
+ }
+ }
+
+ static void decompressBlockETC21BitAlphaC(uint block_part1, uint block_part2, YFColor* img)
+ {
+ int diffbit;
+ sbyte* color1 = stackalloc sbyte[3];
+ sbyte* diff = stackalloc sbyte[3];
+ sbyte red, green, blue;
+
+ diffbit = (int)GETBITSHIGH(block_part1, 1, 33);
+
+ if (diffbit != 0)
+ {
+ // We have diffbit = 1, meaning no transparent pixels. regular decompression.
+
+ // Base color
+ color1[0] = (sbyte)GETBITSHIGH(block_part1, 5, 63);
+ color1[1] = (sbyte)GETBITSHIGH(block_part1, 5, 55);
+ color1[2] = (sbyte)GETBITSHIGH(block_part1, 5, 47);
+
+ // Diff color
+ diff[0] = (sbyte)GETBITSHIGH(block_part1, 3, 58);
+ diff[1] = (sbyte)GETBITSHIGH(block_part1, 3, 50);
+ diff[2] = (sbyte)GETBITSHIGH(block_part1, 3, 42);
+
+ // Extend sign bit to entire byte.
+ diff[0] <<= 5;
+ diff[1] <<= 5;
+ diff[2] <<= 5;
+ diff[0] >>= 5;
+ diff[1] >>= 5;
+ diff[2] >>= 5;
+
+ red = (sbyte)(color1[0] + diff[0]);
+ green = (sbyte)(color1[1] + diff[1]);
+ blue = (sbyte)(color1[2] + diff[2]);
+
+ if (red < 0 || red > 31)
+ {
+ uint block59_part1, block59_part2;
+ unstuff59bits(block_part1, block_part2, &block59_part1, &block59_part2);
+ decompressBlockTHUMB59Tc(block59_part1, block59_part2, img);
+ }
+ else if (green < 0 || green > 31)
+ {
+ uint block58_part1, block58_part2;
+ unstuff58bits(block_part1, block_part2, &block58_part1, &block58_part2);
+ decompressBlockTHUMB58Hc(block58_part1, block58_part2, img);
+ }
+ else if (blue < 0 || blue > 31)
+ {
+ uint block57_part1, block57_part2;
+
+ unstuff57bits(block_part1, block_part2, &block57_part1, &block57_part2);
+ decompressBlockPlanar57c(block57_part1, block57_part2, img);
+ }
+ else
+ {
+ decompressBlockDifferentialWithAlphaC(block_part1, block_part2, img);
+ }
+ for (int x = 0; x < 4; x++)
+ {
+ for (int y = 0; y < 4; y++)
+ {
+ img[(y << 2) | x].Alpha = 0xFF;
+ }
+ }
+ }
+ else
+ {
+ // We have diffbit = 0, transparent pixels. Only T-, H- or regular diff-mode possible.
+
+ // Base color
+ color1[0] = (sbyte)GETBITSHIGH(block_part1, 5, 63);
+ color1[1] = (sbyte)GETBITSHIGH(block_part1, 5, 55);
+ color1[2] = (sbyte)GETBITSHIGH(block_part1, 5, 47);
+
+ // Diff color
+ diff[0] = (sbyte)GETBITSHIGH(block_part1, 3, 58);
+ diff[1] = (sbyte)GETBITSHIGH(block_part1, 3, 50);
+ diff[2] = (sbyte)GETBITSHIGH(block_part1, 3, 42);
+
+ // Extend sign bit to entire byte.
+ diff[0] <<= 5;
+ diff[1] <<= 5;
+ diff[2] <<= 5;
+ diff[0] >>= 5;
+ diff[1] >>= 5;
+ diff[2] >>= 5;
+
+ red = (sbyte)(color1[0] + diff[0]);
+ green = (sbyte)(color1[1] + diff[1]);
+ blue = (sbyte)(color1[2] + diff[2]);
+ if (red < 0 || red > 31)
+ {
+ uint block59_part1, block59_part2;
+ unstuff59bits(block_part1, block_part2, &block59_part1, &block59_part2);
+ decompressBlockTHUMB59TAlphaC(block59_part1, block59_part2, img);
+ }
+ else if (green < 0 || green > 31)
+ {
+ uint block58_part1, block58_part2;
+ unstuff58bits(block_part1, block_part2, &block58_part1, &block58_part2);
+ decompressBlockTHUMB58HAlphaC(block58_part1, block58_part2, img);
+ }
+ else if (blue < 0 || blue > 31)
+ {
+ uint block57_part1, block57_part2;
+
+ unstuff57bits(block_part1, block_part2, &block57_part1, &block57_part2);
+ decompressBlockPlanar57c(block57_part1, block57_part2, img);
+ for (int x = 0; x < 4; x++)
+ {
+ for (int y = 0; y < 4; y++)
+ {
+ img[(y << 2) | x].Alpha = 0xFF;
+ }
+ }
+ }
+ else
+ {
+ decompressBlockDifferentialWithAlphaC(block_part1, block_part2, img);
+ }
+ }
+ }
+
+ static void decompressBlockDiffFlipC(uint block_part1, uint block_part2, YFColor* img)
+ {
+ byte* avg_color = stackalloc byte[3];
+ byte* enc_color1 = stackalloc byte[3];
+ byte* enc_color2 = stackalloc byte[3];
+ sbyte* diff = stackalloc sbyte[3];
+ int table;
+ int index, shift;
+ int r, g, b;
+ int diffbit;
+ int flipbit;
+
+ diffbit = (int)GETBITSHIGH(block_part1, 1, 33);
+ flipbit = (int)GETBITSHIGH(block_part1, 1, 32);
+
+ if (diffbit == 0)
+ {
+ // We have diffbit = 0.
+
+ // First decode left part of block.
+ avg_color[0] = (byte)GETBITSHIGH(block_part1, 4, 63);
+ avg_color[1] = (byte)GETBITSHIGH(block_part1, 4, 55);
+ avg_color[2] = (byte)GETBITSHIGH(block_part1, 4, 47);
+
+ // Here, we should really multiply by 17 instead of 16. This can
+ // be done by just copying the four lower bits to the upper ones
+ // while keeping the lower bits.
+ avg_color[0] |= (byte)(avg_color[0] << 4);
+ avg_color[1] |= (byte)(avg_color[1] << 4);
+ avg_color[2] |= (byte)(avg_color[2] << 4);
+
+ table = (int)GETBITSHIGH(block_part1, 3, 39) << 1;
+
+ uint pixel_indices_MSB, pixel_indices_LSB;
+
+ pixel_indices_MSB = GETBITS(block_part2, 16, 31);
+ pixel_indices_LSB = GETBITS(block_part2, 16, 15);
+
+ if ((flipbit) == 0)
+ {
+ // We should not flip
+ shift = 0;
+ for (int x = 0; x < 2; x++)
+ {
+ for (int y = 0; y < 4; y++)
+ {
+ index = (int)(((pixel_indices_MSB >> shift) & 1) << 1);
+ index |= (int)((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index = unscramble[index];
+
+ r = img[(y << 2) | x].Red = (byte)CLAMP(0, avg_color[0] + compressParams[table, index], 255);
+ g = img[(y << 2) | x].Green = (byte)CLAMP(0, avg_color[1] + compressParams[table, index], 255);
+ b = img[(y << 2) | x].Blue = (byte)CLAMP(0, avg_color[2] + compressParams[table, index], 255);
+ }
+ }
+ }
+ else
+ {
+ // We should flip
+ shift = 0;
+ for (int x = 0; x < 4; x++)
+ {
+ for (int y = 0; y < 2; y++)
+ {
+ index = (int)(((pixel_indices_MSB >> shift) & 1) << 1);
+ index |= (int)((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index = unscramble[index];
+
+ r = img[(y << 2) | x].Red = (byte)CLAMP(0, avg_color[0] + compressParams[table, index], 255);
+ g = img[(y << 2) | x].Green = (byte)CLAMP(0, avg_color[1] + compressParams[table, index], 255);
+ b = img[(y << 2) | x].Blue = (byte)CLAMP(0, avg_color[2] + compressParams[table, index], 255);
+ }
+ shift += 2;
+ }
+ }
+
+ // Now decode other part of block.
+ avg_color[0] = (byte)GETBITSHIGH(block_part1, 4, 59);
+ avg_color[1] = (byte)GETBITSHIGH(block_part1, 4, 51);
+ avg_color[2] = (byte)GETBITSHIGH(block_part1, 4, 43);
+
+ // Here, we should really multiply by 17 instead of 16. This can
+ // be done by just copying the four lower bits to the upper ones
+ // while keeping the lower bits.
+ avg_color[0] |= (byte)(avg_color[0] << 4);
+ avg_color[1] |= (byte)(avg_color[1] << 4);
+ avg_color[2] |= (byte)(avg_color[2] << 4);
+
+ table = (int)(GETBITSHIGH(block_part1, 3, 36) << 1);
+ pixel_indices_MSB = GETBITS(block_part2, 16, 31);
+ pixel_indices_LSB = GETBITS(block_part2, 16, 15);
+
+ if ((flipbit) == 0)
+ {
+ // We should not flip
+ shift = 8;
+ for (int x = 2; x < 4; x++)
+ {
+ for (int y = 0; y < 4; y++)
+ {
+ index = (int)(((pixel_indices_MSB >> shift) & 1) << 1);
+ index |= (int)((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index = unscramble[index];
+
+ r = img[(y << 2) | x].Red = (byte)CLAMP(0, avg_color[0] + compressParams[table, index], 255);
+ g = img[(y << 2) | x].Green = (byte)CLAMP(0, avg_color[1] + compressParams[table, index], 255);
+ b = img[(y << 2) | x].Blue = (byte)CLAMP(0, avg_color[2] + compressParams[table, index], 255);
+ }
+ }
+ }
+ else
+ {
+ // We should flip
+ shift = 2;
+ for (int x = 0; x < 4; x++)
+ {
+ for (int y = 2; y < 4; y++)
+ {
+ index = (int)(((pixel_indices_MSB >> shift) & 1) << 1);
+ index |= (int)((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index = unscramble[index];
+
+ r = img[(y << 2) | x].Red = (byte)CLAMP(0, avg_color[0] + compressParams[table, index], 255);
+ g = img[(y << 2) | x].Green = (byte)CLAMP(0, avg_color[1] + compressParams[table, index], 255);
+ b = img[(y << 2) | x].Blue = (byte)CLAMP(0, avg_color[2] + compressParams[table, index], 255);
+ }
+ shift += 2;
+ }
+ }
+ }
+ else
+ {
+ // We have diffbit = 1.
+
+ // First decode left part of block.
+ enc_color1[0] = (byte)GETBITSHIGH(block_part1, 5, 63);
+ enc_color1[1] = (byte)GETBITSHIGH(block_part1, 5, 55);
+ enc_color1[2] = (byte)GETBITSHIGH(block_part1, 5, 47);
+
+ // Expand from 5 to 8 bits
+ avg_color[0] = (byte)((enc_color1[0] << 3) | (enc_color1[0] >> 2));
+ avg_color[1] = (byte)((enc_color1[1] << 3) | (enc_color1[1] >> 2));
+ avg_color[2] = (byte)((enc_color1[2] << 3) | (enc_color1[2] >> 2));
+
+ table = (int)(GETBITSHIGH(block_part1, 3, 39) << 1);
+
+ uint pixel_indices_MSB, pixel_indices_LSB;
+
+ pixel_indices_MSB = GETBITS(block_part2, 16, 31);
+ pixel_indices_LSB = GETBITS(block_part2, 16, 15);
+
+ if ((flipbit) == 0)
+ {
+ // We should not flip
+ shift = 0;
+ for (int x = 0; x < 2; x++)
+ {
+ for (int y = 0; y < 4; y++)
+ {
+ index = (int)(((pixel_indices_MSB >> shift) & 1) << 1);
+ index |= (int)((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index = unscramble[index];
+
+ r = img[(y << 2) | x].Red = (byte)CLAMP(0, avg_color[0] + compressParams[table, index], 255);
+ g = img[(y << 2) | x].Green = (byte)CLAMP(0, avg_color[1] + compressParams[table, index], 255);
+ b = img[(y << 2) | x].Blue = (byte)CLAMP(0, avg_color[2] + compressParams[table, index], 255);
+ }
+ }
+ }
+ else
+ {
+ // We should flip
+ shift = 0;
+ for (int x = 0; x < 4; x++)
+ {
+ for (int y = 0; y < 2; y++)
+ {
+ index = (int)(((pixel_indices_MSB >> shift) & 1) << 1);
+ index |= (int)((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index = unscramble[index];
+
+ r = img[(y << 2) | x].Red = (byte)CLAMP(0, avg_color[0] + compressParams[table, index], 255);
+ g = img[(y << 2) | x].Green = (byte)CLAMP(0, avg_color[1] + compressParams[table, index], 255);
+ b = img[(y << 2) | x].Blue = (byte)CLAMP(0, avg_color[2] + compressParams[table, index], 255);
+ }
+ shift += 2;
+ }
+ }
+
+ // Now decode right part of block.
+ diff[0] = (sbyte)GETBITSHIGH(block_part1, 3, 58);
+ diff[1] = (sbyte)GETBITSHIGH(block_part1, 3, 50);
+ diff[2] = (sbyte)GETBITSHIGH(block_part1, 3, 42);
+
+ // Extend sign bit to entire byte.
+ diff[0] = (sbyte)(diff[0] << 5);
+ diff[1] = (sbyte)(diff[1] << 5);
+ diff[2] = (sbyte)(diff[2] << 5);
+ diff[0] >>= 5;
+ diff[1] >>= 5;
+ diff[2] >>= 5;
+
+ // Calculale second color
+ enc_color2[0] = (byte)(enc_color1[0] + diff[0]);
+ enc_color2[1] = (byte)(enc_color1[1] + diff[1]);
+ enc_color2[2] = (byte)(enc_color1[2] + diff[2]);
+
+ // Expand from 5 to 8 bits
+ avg_color[0] = (byte)((enc_color2[0] << 3) | (enc_color2[0] >> 2));
+ avg_color[1] = (byte)((enc_color2[1] << 3) | (enc_color2[1] >> 2));
+ avg_color[2] = (byte)((enc_color2[2] << 3) | (enc_color2[2] >> 2));
+
+ table = (int)(GETBITSHIGH(block_part1, 3, 36) << 1);
+ pixel_indices_MSB = GETBITS(block_part2, 16, 31);
+ pixel_indices_LSB = GETBITS(block_part2, 16, 15);
+
+ if ((flipbit) == 0)
+ {
+ // We should not flip
+ shift = 8;
+ for (int x = 2; x < 4; x++)
+ {
+ for (int y = 0; y < 4; y++)
+ {
+ index = (int)(((pixel_indices_MSB >> shift) & 1) << 1);
+ index |= (int)((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index = unscramble[index];
+
+ r = img[(y << 2) | x].Red = (byte)CLAMP(0, avg_color[0] + compressParams[table, index], 255);
+ g = img[(y << 2) | x].Green = (byte)CLAMP(0, avg_color[1] + compressParams[table, index], 255);
+ b = img[(y << 2) | x].Blue = (byte)CLAMP(0, avg_color[2] + compressParams[table, index], 255);
+ }
+ }
+ }
+ else
+ {
+ // We should flip
+ shift = 2;
+ for (int x = 0; x < 4; x++)
+ {
+ for (int y = 2; y < 4; y++)
+ {
+ index = (int)(((pixel_indices_MSB >> shift) & 1) << 1);
+ index |= (int)((pixel_indices_LSB >> shift) & 1);
+ shift++;
+ index = unscramble[index];
+
+ r = img[(y << 2) | x].Red = (byte)CLAMP(0, avg_color[0] + compressParams[table, index], 255);
+ g = img[(y << 2) | x].Green = (byte)CLAMP(0, avg_color[1] + compressParams[table, index], 255);
+ b = img[(y << 2) | x].Blue = (byte)CLAMP(0, avg_color[2] + compressParams[table, index], 255);
+ }
+ shift += 2;
+ }
+ }
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static byte getbit(byte input, int frompos, int topos)
+ {
+ if (frompos > topos)
+ return (byte)(((1 << frompos) & input) >> (frompos - topos));
+ return (byte)(((1 << frompos) & input) << (topos - frompos));
+ }
+
+ static void decompressBlockAlphaC(byte* data, YFColor* img)
+ {
+ int alpha = data[0];
+ int table = data[1];
+
+ int bit = 0;
+ int m_byte = 2;
+ //extract an alpha value for each pixel.
+ for (int x = 0; x < 4; x++)
+ {
+ for (int y = 0; y < 4; y++)
+ {
+ //Extract table index
+ int index = 0;
+ for (int bitpos = 0; bitpos < 3; bitpos++)
+ {
+ index |= getbit(data[m_byte], 7 - bit, 2 - bitpos);
+ bit++;
+ if (bit > 7)
+ {
+ bit = 0;
+ m_byte++;
+ }
+ }
+ img[(y << 2) | x].Alpha = (byte)CLAMP(0, alpha + alphaTable[table, index], 255);
+ }
+ }
+ }
+
+ static int[,] alphaTable = new int[256, 8]
+ {
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { -3, -6, -9, -15, 2, 5, 8, 14 },
+ { -3, -7, -10, -13, 2, 6, 9, 12 },
+ { -2, -5, -8, -13, 1, 4, 7, 12 },
+ { -2, -4, -6, -13, 1, 3, 5, 12 },
+ { -3, -6, -8, -12, 2, 5, 7, 11 },
+ { -3, -7, -9, -11, 2, 6, 8, 10 },
+ { -4, -7, -8, -11, 3, 6, 7, 10 },
+ { -3, -5, -8, -11, 2, 4, 7, 10 },
+ { -2, -6, -8, -10, 1, 5, 7, 9 },
+ { -2, -5, -8, -10, 1, 4, 7, 9 },
+ { -2, -4, -8, -10, 1, 3, 7, 9 },
+ { -2, -5, -7, -10, 1, 4, 6, 9 },
+ { -3, -4, -7, -10, 2, 3, 6, 9 },
+ { -1, -2, -3, -10, 0, 1, 2, 9 },
+ { -4, -6, -8, -9, 3, 5, 7, 8 },
+ { -3, -5, -7, -9, 2, 4, 6, 8 },
+ { -6, -12, -18, -30, 4, 10, 16, 28 },
+ { -6, -14, -20, -26, 4, 12, 18, 24 },
+ { -4, -10, -16, -26, 2, 8, 14, 24 },
+ { -4, -8, -12, -26, 2, 6, 10, 24 },
+ { -6, -12, -16, -24, 4, 10, 14, 22 },
+ { -6, -14, -18, -22, 4, 12, 16, 20 },
+ { -8, -14, -16, -22, 6, 12, 14, 20 },
+ { -6, -10, -16, -22, 4, 8, 14, 20 },
+ { -4, -12, -16, -20, 2, 10, 14, 18 },
+ { -4, -10, -16, -20, 2, 8, 14, 18 },
+ { -4, -8, -16, -20, 2, 6, 14, 18 },
+ { -4, -10, -14, -20, 2, 8, 12, 18 },
+ { -6, -8, -14, -20, 4, 6, 12, 18 },
+ { -2, -4, -6, -20, 0, 2, 4, 18 },
+ { -8, -12, -16, -18, 6, 10, 14, 16 },
+ { -6, -10, -14, -18, 4, 8, 12, 16 },
+ { -9, -18, -27, -45, 6, 15, 24, 42 },
+ { -9, -21, -30, -39, 6, 18, 27, 36 },
+ { -6, -15, -24, -39, 3, 12, 21, 36 },
+ { -6, -12, -18, -39, 3, 9, 15, 36 },
+ { -9, -18, -24, -36, 6, 15, 21, 33 },
+ { -9, -21, -27, -33, 6, 18, 24, 30 },
+ { -12, -21, -24, -33, 9, 18, 21, 30 },
+ { -9, -15, -24, -33, 6, 12, 21, 30 },
+ { -6, -18, -24, -30, 3, 15, 21, 27 },
+ { -6, -15, -24, -30, 3, 12, 21, 27 },
+ { -6, -12, -24, -30, 3, 9, 21, 27 },
+ { -6, -15, -21, -30, 3, 12, 18, 27 },
+ { -9, -12, -21, -30, 6, 9, 18, 27 },
+ { -3, -6, -9, -30, 0, 3, 6, 27 },
+ { -12, -18, -24, -27, 9, 15, 21, 24 },
+ { -9, -15, -21, -27, 6, 12, 18, 24 },
+ { -12, -24, -36, -60, 8, 20, 32, 56 },
+ { -12, -28, -40, -52, 8, 24, 36, 48 },
+ { -8, -20, -32, -52, 4, 16, 28, 48 },
+ { -8, -16, -24, -52, 4, 12, 20, 48 },
+ { -12, -24, -32, -48, 8, 20, 28, 44 },
+ { -12, -28, -36, -44, 8, 24, 32, 40 },
+ { -16, -28, -32, -44, 12, 24, 28, 40 },
+ { -12, -20, -32, -44, 8, 16, 28, 40 },
+ { -8, -24, -32, -40, 4, 20, 28, 36 },
+ { -8, -20, -32, -40, 4, 16, 28, 36 },
+ { -8, -16, -32, -40, 4, 12, 28, 36 },
+ { -8, -20, -28, -40, 4, 16, 24, 36 },
+ { -12, -16, -28, -40, 8, 12, 24, 36 },
+ { -4, -8, -12, -40, 0, 4, 8, 36 },
+ { -16, -24, -32, -36, 12, 20, 28, 32 },
+ { -12, -20, -28, -36, 8, 16, 24, 32 },
+ { -15, -30, -45, -75, 10, 25, 40, 70 },
+ { -15, -35, -50, -65, 10, 30, 45, 60 },
+ { -10, -25, -40, -65, 5, 20, 35, 60 },
+ { -10, -20, -30, -65, 5, 15, 25, 60 },
+ { -15, -30, -40, -60, 10, 25, 35, 55 },
+ { -15, -35, -45, -55, 10, 30, 40, 50 },
+ { -20, -35, -40, -55, 15, 30, 35, 50 },
+ { -15, -25, -40, -55, 10, 20, 35, 50 },
+ { -10, -30, -40, -50, 5, 25, 35, 45 },
+ { -10, -25, -40, -50, 5, 20, 35, 45 },
+ { -10, -20, -40, -50, 5, 15, 35, 45 },
+ { -10, -25, -35, -50, 5, 20, 30, 45 },
+ { -15, -20, -35, -50, 10, 15, 30, 45 },
+ { -5, -10, -15, -50, 0, 5, 10, 45 },
+ { -20, -30, -40, -45, 15, 25, 35, 40 },
+ { -15, -25, -35, -45, 10, 20, 30, 40 },
+ { -18, -36, -54, -90, 12, 30, 48, 84 },
+ { -18, -42, -60, -78, 12, 36, 54, 72 },
+ { -12, -30, -48, -78, 6, 24, 42, 72 },
+ { -12, -24, -36, -78, 6, 18, 30, 72 },
+ { -18, -36, -48, -72, 12, 30, 42, 66 },
+ { -18, -42, -54, -66, 12, 36, 48, 60 },
+ { -24, -42, -48, -66, 18, 36, 42, 60 },
+ { -18, -30, -48, -66, 12, 24, 42, 60 },
+ { -12, -36, -48, -60, 6, 30, 42, 54 },
+ { -12, -30, -48, -60, 6, 24, 42, 54 },
+ { -12, -24, -48, -60, 6, 18, 42, 54 },
+ { -12, -30, -42, -60, 6, 24, 36, 54 },
+ { -18, -24, -42, -60, 12, 18, 36, 54 },
+ { -6, -12, -18, -60, 0, 6, 12, 54 },
+ { -24, -36, -48, -54, 18, 30, 42, 48 },
+ { -18, -30, -42, -54, 12, 24, 36, 48 },
+ { -21, -42, -63, -105, 14, 35, 56, 98 },
+ { -21, -49, -70, -91, 14, 42, 63, 84 },
+ { -14, -35, -56, -91, 7, 28, 49, 84 },
+ { -14, -28, -42, -91, 7, 21, 35, 84 },
+ { -21, -42, -56, -84, 14, 35, 49, 77 },
+ { -21, -49, -63, -77, 14, 42, 56, 70 },
+ { -28, -49, -56, -77, 21, 42, 49, 70 },
+ { -21, -35, -56, -77, 14, 28, 49, 70 },
+ { -14, -42, -56, -70, 7, 35, 49, 63 },
+ { -14, -35, -56, -70, 7, 28, 49, 63 },
+ { -14, -28, -56, -70, 7, 21, 49, 63 },
+ { -14, -35, -49, -70, 7, 28, 42, 63 },
+ { -21, -28, -49, -70, 14, 21, 42, 63 },
+ { -7, -14, -21, -70, 0, 7, 14, 63 },
+ { -28, -42, -56, -63, 21, 35, 49, 56 },
+ { -21, -35, -49, -63, 14, 28, 42, 56 },
+ { -24, -48, -72, -120, 16, 40, 64, 112 },
+ { -24, -56, -80, -104, 16, 48, 72, 96 },
+ { -16, -40, -64, -104, 8, 32, 56, 96 },
+ { -16, -32, -48, -104, 8, 24, 40, 96 },
+ { -24, -48, -64, -96, 16, 40, 56, 88 },
+ { -24, -56, -72, -88, 16, 48, 64, 80 },
+ { -32, -56, -64, -88, 24, 48, 56, 80 },
+ { -24, -40, -64, -88, 16, 32, 56, 80 },
+ { -16, -48, -64, -80, 8, 40, 56, 72 },
+ { -16, -40, -64, -80, 8, 32, 56, 72 },
+ { -16, -32, -64, -80, 8, 24, 56, 72 },
+ { -16, -40, -56, -80, 8, 32, 48, 72 },
+ { -24, -32, -56, -80, 16, 24, 48, 72 },
+ { -8, -16, -24, -80, 0, 8, 16, 72 },
+ { -32, -48, -64, -72, 24, 40, 56, 64 },
+ { -24, -40, -56, -72, 16, 32, 48, 64 },
+ { -27, -54, -81, -135, 18, 45, 72, 126 },
+ { -27, -63, -90, -117, 18, 54, 81, 108 },
+ { -18, -45, -72, -117, 9, 36, 63, 108 },
+ { -18, -36, -54, -117, 9, 27, 45, 108 },
+ { -27, -54, -72, -108, 18, 45, 63, 99 },
+ { -27, -63, -81, -99, 18, 54, 72, 90 },
+ { -36, -63, -72, -99, 27, 54, 63, 90 },
+ { -27, -45, -72, -99, 18, 36, 63, 90 },
+ { -18, -54, -72, -90, 9, 45, 63, 81 },
+ { -18, -45, -72, -90, 9, 36, 63, 81 },
+ { -18, -36, -72, -90, 9, 27, 63, 81 },
+ { -18, -45, -63, -90, 9, 36, 54, 81 },
+ { -27, -36, -63, -90, 18, 27, 54, 81 },
+ { -9, -18, -27, -90, 0, 9, 18, 81 },
+ { -36, -54, -72, -81, 27, 45, 63, 72 },
+ { -27, -45, -63, -81, 18, 36, 54, 72 },
+ { -30, -60, -90, -150, 20, 50, 80, 140 },
+ { -30, -70, -100, -130, 20, 60, 90, 120 },
+ { -20, -50, -80, -130, 10, 40, 70, 120 },
+ { -20, -40, -60, -130, 10, 30, 50, 120 },
+ { -30, -60, -80, -120, 20, 50, 70, 110 },
+ { -30, -70, -90, -110, 20, 60, 80, 100 },
+ { -40, -70, -80, -110, 30, 60, 70, 100 },
+ { -30, -50, -80, -110, 20, 40, 70, 100 },
+ { -20, -60, -80, -100, 10, 50, 70, 90 },
+ { -20, -50, -80, -100, 10, 40, 70, 90 },
+ { -20, -40, -80, -100, 10, 30, 70, 90 },
+ { -20, -50, -70, -100, 10, 40, 60, 90 },
+ { -30, -40, -70, -100, 20, 30, 60, 90 },
+ { -10, -20, -30, -100, 0, 10, 20, 90 },
+ { -40, -60, -80, -90, 30, 50, 70, 80 },
+ { -30, -50, -70, -90, 20, 40, 60, 80 },
+ { -33, -66, -99, -165, 22, 55, 88, 154 },
+ { -33, -77, -110, -143, 22, 66, 99, 132 },
+ { -22, -55, -88, -143, 11, 44, 77, 132 },
+ { -22, -44, -66, -143, 11, 33, 55, 132 },
+ { -33, -66, -88, -132, 22, 55, 77, 121 },
+ { -33, -77, -99, -121, 22, 66, 88, 110 },
+ { -44, -77, -88, -121, 33, 66, 77, 110 },
+ { -33, -55, -88, -121, 22, 44, 77, 110 },
+ { -22, -66, -88, -110, 11, 55, 77, 99 },
+ { -22, -55, -88, -110, 11, 44, 77, 99 },
+ { -22, -44, -88, -110, 11, 33, 77, 99 },
+ { -22, -55, -77, -110, 11, 44, 66, 99 },
+ { -33, -44, -77, -110, 22, 33, 66, 99 },
+ { -11, -22, -33, -110, 0, 11, 22, 99 },
+ { -44, -66, -88, -99, 33, 55, 77, 88 },
+ { -33, -55, -77, -99, 22, 44, 66, 88 },
+ { -36, -72, -108, -180, 24, 60, 96, 168 },
+ { -36, -84, -120, -156, 24, 72, 108, 144 },
+ { -24, -60, -96, -156, 12, 48, 84, 144 },
+ { -24, -48, -72, -156, 12, 36, 60, 144 },
+ { -36, -72, -96, -144, 24, 60, 84, 132 },
+ { -36, -84, -108, -132, 24, 72, 96, 120 },
+ { -48, -84, -96, -132, 36, 72, 84, 120 },
+ { -36, -60, -96, -132, 24, 48, 84, 120 },
+ { -24, -72, -96, -120, 12, 60, 84, 108 },
+ { -24, -60, -96, -120, 12, 48, 84, 108 },
+ { -24, -48, -96, -120, 12, 36, 84, 108 },
+ { -24, -60, -84, -120, 12, 48, 72, 108 },
+ { -36, -48, -84, -120, 24, 36, 72, 108 },
+ { -12, -24, -36, -120, 0, 12, 24, 108 },
+ { -48, -72, -96, -108, 36, 60, 84, 96 },
+ { -36, -60, -84, -108, 24, 48, 72, 96 },
+ { -39, -78, -117, -195, 26, 65, 104, 182 },
+ { -39, -91, -130, -169, 26, 78, 117, 156 },
+ { -26, -65, -104, -169, 13, 52, 91, 156 },
+ { -26, -52, -78, -169, 13, 39, 65, 156 },
+ { -39, -78, -104, -156, 26, 65, 91, 143 },
+ { -39, -91, -117, -143, 26, 78, 104, 130 },
+ { -52, -91, -104, -143, 39, 78, 91, 130 },
+ { -39, -65, -104, -143, 26, 52, 91, 130 },
+ { -26, -78, -104, -130, 13, 65, 91, 117 },
+ { -26, -65, -104, -130, 13, 52, 91, 117 },
+ { -26, -52, -104, -130, 13, 39, 91, 117 },
+ { -26, -65, -91, -130, 13, 52, 78, 117 },
+ { -39, -52, -91, -130, 26, 39, 78, 117 },
+ { -13, -26, -39, -130, 0, 13, 26, 117 },
+ { -52, -78, -104, -117, 39, 65, 91, 104 },
+ { -39, -65, -91, -117, 26, 52, 78, 104 },
+ { -42, -84, -126, -210, 28, 70, 112, 196 },
+ { -42, -98, -140, -182, 28, 84, 126, 168 },
+ { -28, -70, -112, -182, 14, 56, 98, 168 },
+ { -28, -56, -84, -182, 14, 42, 70, 168 },
+ { -42, -84, -112, -168, 28, 70, 98, 154 },
+ { -42, -98, -126, -154, 28, 84, 112, 140 },
+ { -56, -98, -112, -154, 42, 84, 98, 140 },
+ { -42, -70, -112, -154, 28, 56, 98, 140 },
+ { -28, -84, -112, -140, 14, 70, 98, 126 },
+ { -28, -70, -112, -140, 14, 56, 98, 126 },
+ { -28, -56, -112, -140, 14, 42, 98, 126 },
+ { -28, -70, -98, -140, 14, 56, 84, 126 },
+ { -42, -56, -98, -140, 28, 42, 84, 126 },
+ { -14, -28, -42, -140, 0, 14, 28, 126 },
+ { -56, -84, -112, -126, 42, 70, 98, 112 },
+ { -42, -70, -98, -126, 28, 56, 84, 112 },
+ { -45, -90, -135, -225, 30, 75, 120, 210 },
+ { -45, -105, -150, -195, 30, 90, 135, 180 },
+ { -30, -75, -120, -195, 15, 60, 105, 180 },
+ { -30, -60, -90, -195, 15, 45, 75, 180 },
+ { -45, -90, -120, -180, 30, 75, 105, 165 },
+ { -45, -105, -135, -165, 30, 90, 120, 150 },
+ { -60, -105, -120, -165, 45, 90, 105, 150 },
+ { -45, -75, -120, -165, 30, 60, 105, 150 },
+ { -30, -90, -120, -150, 15, 75, 105, 135 },
+ { -30, -75, -120, -150, 15, 60, 105, 135 },
+ { -30, -60, -120, -150, 15, 45, 105, 135 },
+ { -30, -75, -105, -150, 15, 60, 90, 135 },
+ { -45, -60, -105, -150, 30, 45, 90, 135 },
+ { -15, -30, -45, -150, 0, 15, 30, 135 },
+ { -60, -90, -120, -135, 45, 75, 105, 120 },
+ { -45, -75, -105, -135, 30, 60, 90, 120 },
+ };
+ static byte[] table59T = new byte[8] { 3, 6, 11, 16, 23, 32, 41, 64 };
+ static byte[] table58H = new byte[8] { 3, 6, 11, 16, 23, 32, 41, 64 };
+ static int[] unscramble = new int[4] { 2, 3, 1, 0 };
+ static int[,] compressParams = new int[16, 4]
+ {
+ { -8, -2, 2, 8 },
+ { -8, -2, 2, 8 },
+ { -17, -5, 5, 17 },
+ { -17, -5, 5, 17 },
+ { -29, -9, 9, 29 },
+ { -29, -9, 9, 29 },
+ { -42, -13, 13, 42 },
+ { -42, -13, 13, 42 },
+ { -60, -18, 18, 60 },
+ { -60, -18, 18, 60 },
+ { -80, -24, 24, 80 },
+ { -80, -24, 24, 80 },
+ { -106, -33, 33, 106 },
+ { -106, -33, 33, 106 },
+ { -183, -47, 47, 183 },
+ { -183, -47, 47, 183 }
+ };
+
+ public static void DecodeBlock_RGB_ETC1(byte* texPtr, YFColor* color)
+ {
+ uint word1 = (uint)((texPtr[0] << 24) | (texPtr[1] << 16) | (texPtr[2] << 8) | texPtr[3]);
+ uint word2 = (uint)((texPtr[4] << 24) | (texPtr[5] << 16) | (texPtr[6] << 8) | texPtr[7]);
+ decompressBlockDiffFlipC(word1, word2, color);
+ for (int i = 0; i < 16; i++)
+ {
+ (color++)->Alpha = 0xFF;
+ }
+ }
+
+ public static void DecodeBlock_RGB_ETC2(byte* texPtr, YFColor* color)
+ {
+ uint word1 = (uint)((texPtr[0] << 24) | (texPtr[1] << 16) | (texPtr[2] << 8) | texPtr[3]);
+ uint word2 = (uint)((texPtr[4] << 24) | (texPtr[5] << 16) | (texPtr[6] << 8) | texPtr[7]);
+ decompressBlockETC2c(word1, word2, color);
+ for (int i = 0; i < 16; i++)
+ {
+ (color++)->Alpha = 0xFF;
+ }
+ }
+
+ public static void DecodeBlock_RGB_A1_ETC2(byte* texPtr, YFColor* color)
+ {
+ uint word1 = (uint)((texPtr[0] << 24) | (texPtr[1] << 16) | (texPtr[2] << 8) | texPtr[3]);
+ uint word2 = (uint)((texPtr[4] << 24) | (texPtr[5] << 16) | (texPtr[6] << 8) | texPtr[7]);
+ decompressBlockETC21BitAlphaC(word1, word2, color);
+ }
+
+ public static void DecodeBlock_RGBA_ETC2_EAC(byte* texPtr, YFColor* color)
+ {
+ decompressBlockAlphaC(texPtr, color);
+ texPtr += 8;
+ uint word1 = (uint)((texPtr[0] << 24) | (texPtr[1] << 16) | (texPtr[2] << 8) | texPtr[3]);
+ uint word2 = (uint)((texPtr[4] << 24) | (texPtr[5] << 16) | (texPtr[6] << 8) | texPtr[7]);
+ decompressBlockETC2c(word1, word2, color);
+ }
+ }
+
+ private static class ETC1Encoder
+ {
+ public static readonly int[,] ETC1Modifiers =
+ {
+ { 2, 8 },
+ { 5, 17 },
+ { 9, 29 },
+ { 13, 42 },
+ { 18, 60 },
+ { 24, 80 },
+ { 33, 106 },
+ { 47, 183 }
+ };
+
+ public static ulong GenETC1(YFColor* Colors)
+ {
+ ulong Horizontal = GenHorizontal(Colors);
+ ulong Vertical = GenVertical(Colors);
+ YFColor* sc = stackalloc YFColor[16];
+ DecodeETC1(Horizontal, sc);
+ int HorizontalScore = GetScore(Colors, sc);
+ DecodeETC1(Vertical, sc);
+ int VerticalScore = GetScore(Colors, sc);
+ return (HorizontalScore < VerticalScore) ? Horizontal : Vertical;
+ }
+
+ public static void DecodeETC1(ulong temp, YFColor* Result)
+ {
+ bool diffbit = ((temp >> 33) & 1) == 1;
+ bool flipbit = ((temp >> 32) & 1) == 1;
+ int r1, r2, g1, g2, b1, b2;
+ if (diffbit)
+ {
+ int r = (int)((temp >> 59) & 0x1F);
+ int g = (int)((temp >> 51) & 0x1F);
+ int b = (int)((temp >> 43) & 0x1F);
+ r1 = (r << 3) | ((r & 0x1C) >> 2);
+ g1 = (g << 3) | ((g & 0x1C) >> 2);
+ b1 = (b << 3) | ((b & 0x1C) >> 2);
+ r += (int)((temp >> 56) & 0x7) << 29 >> 29;
+ g += (int)((temp >> 48) & 0x7) << 29 >> 29;
+ b += (int)((temp >> 40) & 0x7) << 29 >> 29;
+ r2 = (r << 3) | ((r & 0x1C) >> 2);
+ g2 = (g << 3) | ((g & 0x1C) >> 2);
+ b2 = (b << 3) | ((b & 0x1C) >> 2);
+ }
+ else
+ {
+ r1 = (int)((temp >> 60) & 0xF) * 0x11;
+ g1 = (int)((temp >> 52) & 0xF) * 0x11;
+ b1 = (int)((temp >> 44) & 0xF) * 0x11;
+ r2 = (int)((temp >> 56) & 0xF) * 0x11;
+ g2 = (int)((temp >> 48) & 0xF) * 0x11;
+ b2 = (int)((temp >> 40) & 0xF) * 0x11;
+ }
+ int Table1 = (int)((temp >> 37) & 0x7);
+ int Table2 = (int)((temp >> 34) & 0x7);
+ for (int i = 0; i < 4; i++)
+ {
+ for (int j = 0; j < 4; j++)
+ {
+ int val = (int)((temp >> ((j << 2) | i)) & 0x1);
+ bool neg = ((temp >> (((j << 2) | i) + 16)) & 0x1) == 1;
+ if ((flipbit && i < 2) || (!flipbit && j < 2))
+ {
+ int add = ETC1Modifiers[Table1, val] * (neg ? -1 : 1);
+ Result[(i << 2) | j] = new YFColor(ColorClamp(r1 + add), ColorClamp(g1 + add), ColorClamp(b1 + add));
+ }
+ else
+ {
+ int add = ETC1Modifiers[Table2, val] * (neg ? -1 : 1);
+ Result[(i << 2) | j] = new YFColor(ColorClamp(r2 + add), ColorClamp(g2 + add), ColorClamp(b2 + add));
+ }
+ }
+ }
+ }
+
+ private static int GetScore(YFColor* Original, YFColor* Encode)
+ {
+ int Diff = 0;
+ for (int i = 0; i < 4 * 4; i++)
+ {
+ Diff += Math.Abs(Encode[i].Red - Original[i].Red);
+ Diff += Math.Abs(Encode[i].Green - Original[i].Green);
+ Diff += Math.Abs(Encode[i].Blue - Original[i].Blue);
+ }
+ return Diff;
+ }
+
+ private static void SetFlipMode(ref ulong Data, bool Mode)
+ {
+ Data &= ~(1ul << 32);
+ Data |= (Mode ? 1ul : 0ul) << 32;
+ }
+
+ private static void SetDiffMode(ref ulong Data, bool Mode)
+ {
+ Data &= ~(1ul << 33);
+ Data |= (Mode ? 1ul : 0ul) << 33;
+ }
+
+ private static YFColor[] GetLeftColors(YFColor* Pixels)
+ {
+ YFColor[] Left = new YFColor[8];
+ for (int y = 0; y < 4; y++)
+ {
+ for (int x = 0; x < 2; x++)
+ {
+ Left[y * 2 + x] = Pixels[y * 4 + x];
+ }
+ }
+ return Left;
+ }
+
+ private static YFColor[] GetRightColors(YFColor* Pixels)
+ {
+ YFColor[] Right = new YFColor[8];
+ for (int y = 0; y < 4; y++)
+ {
+ for (int x = 2; x < 4; x++)
+ {
+ Right[y * 2 + x - 2] = Pixels[y * 4 + x];
+ }
+ }
+ return Right;
+ }
+
+ private static YFColor[] GetTopColors(YFColor* Pixels)
+ {
+ YFColor[] Top = new YFColor[8];
+ for (int y = 0; y < 2; y++)
+ {
+ for (int x = 0; x < 4; x++)
+ {
+ Top[y * 4 + x] = Pixels[y * 4 + x];
+ }
+ }
+ return Top;
+ }
+
+ private static YFColor[] GetBottomColors(YFColor* Pixels)
+ {
+ YFColor[] Bottom = new YFColor[8];
+ for (int y = 2; y < 4; y++)
+ {
+ for (int x = 0; x < 4; x++)
+ {
+ Bottom[(y - 2) * 4 + x] = Pixels[y * 4 + x];
+ }
+ }
+ return Bottom;
+ }
+
+ private static ulong GenHorizontal(YFColor* Colors)
+ {
+ ulong data = 0;
+ SetFlipMode(ref data, false);
+ //Left
+ YFColor[] Left = GetLeftColors(Colors);
+ YFColor basec1;
+ int mod = GenModifier(out basec1, Left);
+ SetTable1(ref data, mod);
+ GenPixDiff(ref data, Left, basec1, mod, 0, 2, 0, 4);
+ //Right
+ YFColor[] Right = GetRightColors(Colors);
+ YFColor basec2;
+ mod = GenModifier(out basec2, Right);
+ SetTable2(ref data, mod);
+ GenPixDiff(ref data, Right, basec2, mod, 2, 4, 0, 4);
+ SetBaseColors(ref data, basec1, basec2);
+ return data;
+ }
+
+ private static ulong GenVertical(YFColor* Colors)
+ {
+ ulong data = 0;
+ SetFlipMode(ref data, true);
+ //Top
+ YFColor[] Top = GetTopColors(Colors);
+ YFColor basec1;
+ int mod = GenModifier(out basec1, Top);
+ SetTable1(ref data, mod);
+ GenPixDiff(ref data, Top, basec1, mod, 0, 4, 0, 2);
+ //Bottom
+ YFColor[] Bottom = GetBottomColors(Colors);
+ YFColor basec2;
+ mod = GenModifier(out basec2, Bottom);
+ SetTable2(ref data, mod);
+ GenPixDiff(ref data, Bottom, basec2, mod, 0, 4, 2, 4);
+ SetBaseColors(ref data, basec1, basec2);
+ return data;
+ }
+
+ private static void SetBaseColors(ref ulong Data, YFColor Color1, YFColor Color2)
+ {
+ int R1 = Color1.Red;
+ int G1 = Color1.Green;
+ int B1 = Color1.Blue;
+ int R2 = Color2.Red;
+ int G2 = Color2.Green;
+ int B2 = Color2.Blue;
+ //First look if differencial is possible.
+ int RDiff = (R2 - R1) / 8;
+ int GDiff = (G2 - G1) / 8;
+ int BDiff = (B2 - B1) / 8;
+ if (RDiff > -4 && RDiff < 3 && GDiff > -4 && GDiff < 3 && BDiff > -4 && BDiff < 3)
+ {
+ SetDiffMode(ref Data, true);
+ R1 /= 8;
+ G1 /= 8;
+ B1 /= 8;
+ Data |= (ulong)R1 << 59;
+ Data |= (ulong)G1 << 51;
+ Data |= (ulong)B1 << 43;
+ Data |= (ulong)(RDiff & 0x7) << 56;
+ Data |= (ulong)(GDiff & 0x7) << 48;
+ Data |= (ulong)(BDiff & 0x7) << 40;
+ }
+ else
+ {
+ Data |= (ulong)(R1 / 0x11) << 60;
+ Data |= (ulong)(G1 / 0x11) << 52;
+ Data |= (ulong)(B1 / 0x11) << 44;
+
+ Data |= (ulong)(R2 / 0x11) << 56;
+ Data |= (ulong)(G2 / 0x11) << 48;
+ Data |= (ulong)(B2 / 0x11) << 40;
+ }
+ }
+
+ private static void GenPixDiff(ref ulong Data, YFColor[] Pixels, YFColor BaseColor, int Modifier, int XOffs, int XEnd, int YOffs, int YEnd)
+ {
+ int BaseMean = (BaseColor.Red + BaseColor.Green + BaseColor.Blue) / 3;
+ int i = 0;
+ for (int yy = YOffs; yy < YEnd; yy++)
+ {
+ for (int xx = XOffs; xx < XEnd; xx++)
+ {
+ int Diff = ((Pixels[i].Red + Pixels[i].Green + Pixels[i].Blue) / 3) - BaseMean;
+
+ if (Diff < 0) Data |= 1ul << (xx * 4 + yy + 16);
+ int tbldiff1 = Math.Abs(Diff) - ETC1Modifiers[Modifier, 0];
+ int tbldiff2 = Math.Abs(Diff) - ETC1Modifiers[Modifier, 1];
+
+ if (Math.Abs(tbldiff2) < Math.Abs(tbldiff1)) Data |= 1ul << (xx * 4 + yy);
+ i++;
+ }
+ }
+ }
+
+ private static int GenModifier(out YFColor BaseColor, YFColor[] Pixels)
+ {
+ YFColor Max = new YFColor(255, 255, 255);
+ YFColor Min = new YFColor(0, 0, 0);
+ int MinY = int.MaxValue;
+ int MaxY = int.MinValue;
+ for (int i = 0; i < 8; i++)
+ {
+ if (Pixels[i].Alpha == 0) continue;
+ int Y = (Pixels[i].Red + Pixels[i].Green + Pixels[i].Blue) / 3;
+ if (Y > MaxY)
+ {
+ MaxY = Y;
+ Max = Pixels[i];
+ }
+ if (Y < MinY)
+ {
+ MinY = Y;
+ Min = Pixels[i];
+ }
+ }
+ int DiffMean = (Max.Red - Min.Red + Max.Green - Min.Green + Max.Blue - Min.Blue) / 3;
+ int ModDiff = int.MaxValue;
+ int Modifier = -1;
+ int Mode = -1;
+ for (int i = 0; i < 8; i++)
+ {
+ int SS = ETC1Modifiers[i, 0] * 2;
+ int SB = ETC1Modifiers[i, 0] + ETC1Modifiers[i, 1];
+ int BB = ETC1Modifiers[i, 1] * 2;
+ if (SS > 255) SS = 255;
+ if (SB > 255) SB = 255;
+ if (BB > 255) BB = 255;
+ if (Math.Abs(DiffMean - SS) < ModDiff)
+ {
+ ModDiff = Math.Abs(DiffMean - SS);
+ Modifier = i;
+ Mode = 0;
+ }
+ if (Math.Abs(DiffMean - SB) < ModDiff)
+ {
+ ModDiff = Math.Abs(DiffMean - SB);
+ Modifier = i;
+ Mode = 1;
+ }
+ if (Math.Abs(DiffMean - BB) < ModDiff)
+ {
+ ModDiff = Math.Abs(DiffMean - BB);
+ Modifier = i;
+ Mode = 2;
+ }
+ }
+ if (Mode == 1)
+ {
+ float div1 = ETC1Modifiers[Modifier, 0] / (float)ETC1Modifiers[Modifier, 1];
+ float div2 = 1f - div1;
+ BaseColor = new YFColor(ColorClamp(Min.Red * div1 + Max.Red * div2), ColorClamp(Min.Green * div1 + Max.Green * div2), ColorClamp(Min.Blue * div1 + Max.Blue * div2));
+ }
+ else
+ {
+ BaseColor = new YFColor((byte)((Min.Red + Max.Red) >> 1), (byte)((Min.Green + Max.Green) >> 1), (byte)((Min.Blue + Max.Blue) >> 1));
+ }
+ return Modifier;
+ }
+
+ private static void SetTable1(ref ulong Data, int Table)
+ {
+ Data &= ~(7ul << 37);
+ Data |= (ulong)(Table & 0x7) << 37;
+ }
+
+ private static void SetTable2(ref ulong Data, int Table)
+ {
+ Data &= ~(7ul << 34);
+ Data |= (ulong)(Table & 0x7) << 34;
+ }
+
+ public static byte ColorClamp(float Color) //加颜色可能加出来超过结果的
+ {
+ int color = (int)Color;
+ if (color > 255) return 255;
+ if (color < 0) return 0;
+ return (byte)color;
+ }
+
+ public static byte ColorClamp(int Color) //加颜色可能加出来超过结果的
+ {
+ if (Color > 255) return 255;
+ if (Color < 0) return 0;
+ return (byte)Color;
+ }
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Shared/PS4TextureHelper.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Shared/PS4TextureHelper.cs
new file mode 100644
index 0000000..f425840
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Shared/PS4TextureHelper.cs
@@ -0,0 +1,58 @@
+using System;
+
+namespace LibWindPop.Utils.Graphics.Texture.Shared
+{
+ internal static class PS4TextureHelper
+ {
+ public static int GetMicroTexelIndexFromPosition(int xPos, int yPos)
+ {
+ int Twiddled = 0;
+ int SrcBitPos = 1;
+ int DstBitPos = 1;
+ while (SrcBitPos < 8)
+ {
+ if ((xPos & SrcBitPos) != 0)
+ {
+ Twiddled |= DstBitPos;
+ }
+ if ((yPos & SrcBitPos) != 0)
+ {
+ Twiddled |= DstBitPos << 1;
+ }
+ SrcBitPos <<= 1;
+ DstBitPos <<= 2;
+ }
+ return Twiddled;
+ }
+
+ public static (int alignWidth, int alignHeight) GetAlignSizeFromTexelSize(int width, int height)
+ {
+ if (width < 8 && height < 8)
+ {
+ if (!BitHelper.IsPowerOfTwo(width))
+ {
+ width = BitHelper.GetClosestPowerOfTwoAbove(width);
+ }
+ if (!BitHelper.IsPowerOfTwo(height))
+ {
+ height = BitHelper.GetClosestPowerOfTwoAbove(height);
+ }
+ width = height = Math.Max(width, height);
+ }
+ else
+ {
+ if ((width & 0x7) != 0)
+ {
+ width |= 0x7;
+ width++;
+ }
+ if ((height & 0x7) != 0)
+ {
+ height |= 0x7;
+ height++;
+ }
+ }
+ return (width, height);
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Shared/PVRTCCoder.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Shared/PVRTCCoder.cs
new file mode 100644
index 0000000..988d374
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Shared/PVRTCCoder.cs
@@ -0,0 +1,2622 @@
+using System;
+using System.Buffers;
+
+namespace LibWindPop.Utils.Graphics.Texture.Shared
+{
+ internal static unsafe class PVRTCCoder
+ {
+ public static void EncodeTexture_RGBA_PVRTCII_4BPP(byte* texPtr, YFColor* color, uint width, uint height)
+ {
+ PVRTCEncoder.CompressPVRTC(texPtr, color, width, height, 4, true, true);
+ }
+
+ public static void EncodeTexture_RGBA_PVRTCII_2BPP(byte* texPtr, YFColor* color, uint width, uint height)
+ {
+ PVRTCEncoder.CompressPVRTC(texPtr, color, width, height, 2, true, true);
+ }
+
+ public static void EncodeTexture_RGBA_PVRTCI_4BPP(byte* texPtr, YFColor* color, uint width, uint height)
+ {
+ PVRTCEncoder.CompressPVRTC(texPtr, color, width, height, 4, false, true);
+ }
+
+ public static void EncodeTexture_RGB_PVRTCI_4BPP(byte* texPtr, YFColor* color, uint width, uint height)
+ {
+ PVRTCEncoder.CompressPVRTC(texPtr, color, width, height, 4, false, false);
+ }
+
+ public static void EncodeTexture_RGBA_PVRTCI_2BPP(byte* texPtr, YFColor* color, uint width, uint height)
+ {
+ PVRTCEncoder.CompressPVRTC(texPtr, color, width, height, 2, false, true);
+ }
+
+ public static void EncodeTexture_RGB_PVRTCI_2BPP(byte* texPtr, YFColor* color, uint width, uint height)
+ {
+ PVRTCEncoder.CompressPVRTC(texPtr, color, width, height, 2, false, false);
+ }
+
+ public static void DecodeTexture_RGBA_PVRTCII_4BPP(byte* texPtr, YFColor* color, uint width, uint height)
+ {
+ PVRTCDecoder.DecompressPVRTC(texPtr, color, width, height, 4, true, true);
+ }
+
+ public static void DecodeTexture_RGBA_PVRTCII_2BPP(byte* texPtr, YFColor* color, uint width, uint height)
+ {
+ PVRTCDecoder.DecompressPVRTC(texPtr, color, width, height, 2, true, true);
+ }
+
+ public static void DecodeTexture_RGBA_PVRTCI_4BPP(byte* texPtr, YFColor* color, uint width, uint height)
+ {
+ PVRTCDecoder.DecompressPVRTC(texPtr, color, width, height, 4, false, true);
+ }
+
+ public static void DecodeTexture_RGB_PVRTCI_4BPP(byte* texPtr, YFColor* color, uint width, uint height)
+ {
+ PVRTCDecoder.DecompressPVRTC(texPtr, color, width, height, 4, false, false);
+ }
+
+ public static void DecodeTexture_RGBA_PVRTCI_2BPP(byte* texPtr, YFColor* color, uint width, uint height)
+ {
+ PVRTCDecoder.DecompressPVRTC(texPtr, color, width, height, 2, false, true);
+ }
+
+ public static void DecodeTexture_RGB_PVRTCI_2BPP(byte* texPtr, YFColor* color, uint width, uint height)
+ {
+ PVRTCDecoder.DecompressPVRTC(texPtr, color, width, height, 2, false, false);
+ }
+
+ ///
+ /// PowerVR SDK provide PVRTCI decoding function
+ /// I modify it to support PVRTCII decoding
+ /// Source file:
+ /// https://github.com/powervr-graphics/Native_SDK/blob/master/framework/PVRCore/texture/PVRTDecompress.cpp
+ /// Source project:
+ /// https://github.com/powervr-graphics/Native_SDK
+ ///
+ private static class PVRTCDecoder
+ {
+ ///
+ /// a 128-bit color struct
+ ///
+ private struct Pixel128S
+ {
+ public int red, green, blue, alpha;
+ };
+
+ ///
+ /// a pvrtc word
+ ///
+ private struct PVRTCWord
+ {
+ ///
+ /// index data
+ ///
+ public uint ModulationData;
+ ///
+ /// color data
+ ///
+ public uint ColorData;
+ };
+
+ ///
+ /// a struct to save four pvrtc block position
+ ///
+ private struct PVRTCWordIndices
+ {
+ public fixed int P[2], Q[2], R[2], S[2];
+ };
+
+ ///
+ /// decode colorA as RGBA5554 from ColorData
+ ///
+ /// PVRTCWord ColorData
+ /// if the PVRTCWord is PVRTCII
+ /// colorA as RGBA5554
+ private static YFColor GetColorA(uint ColorData, bool versionII)
+ {
+ YFColor color;
+ uint mask = versionII ? 0x80000000 : 0x8000;
+ if ((ColorData & mask) != 0)
+ {
+ color.Red = (byte)((ColorData & 0x7c00) >> 10);
+ color.Green = (byte)((ColorData & 0x3e0) >> 5);
+ color.Blue = (byte)((ColorData & 0x1e) | ((ColorData & 0x1e) >> 4));
+ color.Alpha = (byte)0xf;
+ }
+ else
+ {
+ color.Red = (byte)(((ColorData & 0xf00) >> 7) | ((ColorData & 0xf00) >> 11));
+ color.Green = (byte)(((ColorData & 0xf0) >> 3) | ((ColorData & 0xf0) >> 7));
+ color.Blue = (byte)(((ColorData & 0xe) << 1) | ((ColorData & 0xe) >> 2));
+ color.Alpha = (byte)((ColorData & 0x7000) >> 11);
+ }
+ return color;
+ }
+
+ ///
+ /// decode colorB as RGBA5554 from ColorData
+ ///
+ /// PVRTCWord ColorData
+ /// if the PVRTCWord is PVRTCII
+ /// colorB as RGBA5554
+ private static YFColor GetColorB(uint ColorData, bool versionII)
+ {
+ YFColor color;
+ if ((ColorData & 0x80000000) != 0)
+ {
+ color.Red = (byte)((ColorData & 0x7c000000) >> 26);
+ color.Green = (byte)((ColorData & 0x3e00000) >> 21);
+ color.Blue = (byte)((ColorData & 0x1f0000) >> 16);
+ color.Alpha = (byte)0xf;
+ }
+ else
+ {
+ uint adds = versionII ? 1U : 0U;
+ color.Red = (byte)(((ColorData & 0xf000000) >> 23) | ((ColorData & 0xf000000) >> 27));
+ color.Green = (byte)(((ColorData & 0xf00000) >> 19) | ((ColorData & 0xf00000) >> 23));
+ color.Blue = (byte)(((ColorData & 0xf0000) >> 15) | ((ColorData & 0xf0000) >> 19));
+ color.Alpha = (byte)(((ColorData & 0x70000000) >> 27) | adds);
+ }
+ return color;
+ }
+
+ ///
+ /// Expand RGBA5554 as RGBA8888
+ ///
+ /// RGBA5554 color
+ /// RGBA8888 color
+ private static Pixel128S Color5554To8888(YFColor color5554)
+ {
+ return new Pixel128S
+ {
+ red = (color5554.Red << 3) | (color5554.Red >> 2),
+ green = (color5554.Green << 3) | (color5554.Green >> 2),
+ blue = (color5554.Blue << 3) | (color5554.Blue >> 2),
+ alpha = (color5554.Alpha << 4) | color5554.Alpha,
+ };
+ }
+
+ ///
+ /// interpolate color as 4x4/8x4 block from 4 color
+ ///
+ /// P(Left Top Point) Color
+ /// Q(Right Top Point) Color
+ /// R(Left Bottom Point) Color
+ /// S(Right Bottom Point) Color
+ /// buffer to save interpolated color
+ /// color bits per pixel
+ private static void InterpolateColors(YFColor P, YFColor Q, YFColor R, YFColor S, Pixel128S* pPixel, byte bpp)
+ {
+ int WordWidth = bpp == 2 ? 8 : 4;
+ int WordHeight = 4;
+ Pixel128S hP = new Pixel128S
+ {
+ red = P.Red,
+ green = P.Green,
+ blue = P.Blue,
+ alpha = P.Alpha
+ };
+ Pixel128S hQ = new Pixel128S
+ {
+ red = Q.Red,
+ green = Q.Green,
+ blue = Q.Blue,
+ alpha = Q.Alpha
+ };
+ Pixel128S hR = new Pixel128S
+ {
+ red = R.Red,
+ green = R.Green,
+ blue = R.Blue,
+ alpha = R.Alpha
+ };
+ Pixel128S hS = new Pixel128S
+ {
+ red = S.Red,
+ green = S.Green,
+ blue = S.Blue,
+ alpha = S.Alpha
+ };
+ Pixel128S QminusP = new Pixel128S
+ {
+ red = hQ.red - hP.red,
+ green = hQ.green - hP.green,
+ blue = hQ.blue - hP.blue,
+ alpha = hQ.alpha - hP.alpha
+ };
+ Pixel128S SminusR = new Pixel128S
+ {
+ red = hS.red - hR.red,
+ green = hS.green - hR.green,
+ blue = hS.blue - hR.blue,
+ alpha = hS.alpha - hR.alpha
+ };
+ hP.red *= WordWidth;
+ hP.green *= WordWidth;
+ hP.blue *= WordWidth;
+ hP.alpha *= WordWidth;
+ hR.red *= WordWidth;
+ hR.green *= WordWidth;
+ hR.blue *= WordWidth;
+ hR.alpha *= WordWidth;
+ if (bpp == 2)
+ {
+ for (int x = 0; x < WordWidth; x++)
+ {
+ Pixel128S result = new Pixel128S
+ {
+ red = 4 * hP.red,
+ green = 4 * hP.green,
+ blue = 4 * hP.blue,
+ alpha = 4 * hP.alpha
+ };
+ Pixel128S dY = new Pixel128S
+ {
+ red = hR.red - hP.red,
+ green = hR.green - hP.green,
+ blue = hR.blue - hP.blue,
+ alpha = hR.alpha - hP.alpha
+ };
+ for (int y = 0; y < WordHeight; y++)
+ {
+ pPixel[y * WordWidth + x].red = (result.red >> 7) + (result.red >> 2);
+ pPixel[y * WordWidth + x].green = (result.green >> 7) + (result.green >> 2);
+ pPixel[y * WordWidth + x].blue = (result.blue >> 7) + (result.blue >> 2);
+ pPixel[y * WordWidth + x].alpha = (result.alpha >> 5) + (result.alpha >> 1);
+ result.red += dY.red;
+ result.green += dY.green;
+ result.blue += dY.blue;
+ result.alpha += dY.alpha;
+ }
+ hP.red += QminusP.red;
+ hP.green += QminusP.green;
+ hP.blue += QminusP.blue;
+ hP.alpha += QminusP.alpha;
+ hR.red += SminusR.red;
+ hR.green += SminusR.green;
+ hR.blue += SminusR.blue;
+ hR.alpha += SminusR.alpha;
+ }
+ }
+ else
+ {
+ for (int y = 0; y < WordHeight; y++)
+ {
+ Pixel128S result = new Pixel128S
+ {
+ red = 4 * hP.red,
+ green = 4 * hP.green,
+ blue = 4 * hP.blue,
+ alpha = 4 * hP.alpha
+ };
+ Pixel128S dY = new Pixel128S
+ {
+ red = hR.red - hP.red,
+ green = hR.green - hP.green,
+ blue = hR.blue - hP.blue,
+ alpha = hR.alpha - hP.alpha
+ };
+ for (int x = 0; x < WordWidth; x++)
+ {
+ pPixel[y * WordWidth + x].red = (result.red >> 6) + (result.red >> 1);
+ pPixel[y * WordWidth + x].green = (result.green >> 6) + (result.green >> 1);
+ pPixel[y * WordWidth + x].blue = (result.blue >> 6) + (result.blue >> 1);
+ pPixel[y * WordWidth + x].alpha = (result.alpha >> 4) + result.alpha;
+ result.red += dY.red;
+ result.green += dY.green;
+ result.blue += dY.blue;
+ result.alpha += dY.alpha;
+ }
+ hP.red += QminusP.red;
+ hP.green += QminusP.green;
+ hP.blue += QminusP.blue;
+ hP.alpha += QminusP.alpha;
+ hR.red += SminusR.red;
+ hR.green += SminusR.green;
+ hR.blue += SminusR.blue;
+ hR.alpha += SminusR.alpha;
+ }
+ }
+ }
+
+ ///
+ /// 从PVRTCWord中获取一个四分之一PVRTC块的索引调制值和索引值
+ ///
+ /// PVRTCWord
+ /// offset X
+ /// offset Y
+ /// index pointer
+ /// modes pointer
+ /// color bits per pixel
+ private static void UnpackModulations(PVRTCWord word, int offsetX, int offsetY, int** ModulationValues, int** ModulationModes, byte bpp)
+ {
+ uint WordModMode = word.ColorData & 0x1;
+ uint ModulationBits = word.ModulationData;
+ if (bpp == 2)
+ {
+ if (WordModMode != 0)
+ {
+ if ((ModulationBits & 0x1) != 0)
+ {
+ if ((ModulationBits & (0x1 << 20)) != 0)
+ {
+ WordModMode = 3;
+ }
+ else
+ {
+ WordModMode = 2;
+ }
+
+ if ((ModulationBits & (0x1 << 21)) != 0)
+ {
+ ModulationBits |= (0x1 << 20);
+ }
+ else
+ {
+ ModulationBits &= ~((uint)0x1 << 20);
+ }
+ }
+
+ if ((ModulationBits & 0x2) != 0)
+ {
+ ModulationBits |= 0x1;
+ }
+ else
+ {
+ ModulationBits &= ~(uint)0x1;
+ }
+
+ for (int y = 0; y < 4; y++)
+ {
+ for (int x = 0; x < 8; x++)
+ {
+ ModulationModes[x + offsetX][y + offsetY] = (int)WordModMode;
+
+ if (((x ^ y) & 1) == 0)
+ {
+ ModulationValues[x + offsetX][y + offsetY] = (int)(ModulationBits & 3);
+ ModulationBits >>= 2;
+ }
+ }
+ }
+ }
+ else
+ {
+ for (int y = 0; y < 4; y++)
+ {
+ for (int x = 0; x < 8; x++)
+ {
+ ModulationModes[x + offsetX][y + offsetY] = (int)WordModMode;
+
+ if ((ModulationBits & 1) != 0)
+ {
+ ModulationValues[x + offsetX][y + offsetY] = 0x3;
+ }
+ else
+ {
+ ModulationValues[x + offsetX][y + offsetY] = 0x0;
+ }
+ ModulationBits >>= 1;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (WordModMode != 0)
+ {
+ for (int y = 0; y < 4; y++)
+ {
+ for (int x = 0; x < 4; x++)
+ {
+ ModulationModes[y + offsetY][x + offsetX] = (int)WordModMode;
+ ModulationValues[y + offsetY][x + offsetX] = (int)(ModulationBits & 3);
+ if (ModulationValues[y + offsetY][x + offsetX] == 1)
+ {
+ ModulationValues[y + offsetY][x + offsetX] = 4;
+ }
+ else if (ModulationValues[y + offsetY][x + offsetX] == 2)
+ {
+ ModulationValues[y + offsetY][x + offsetX] = 14;
+ }
+ else if (ModulationValues[y + offsetY][x + offsetX] == 3)
+ {
+ ModulationValues[y + offsetY][x + offsetX] = 8;
+ }
+ ModulationBits >>= 2;
+ }
+ }
+ }
+ else
+ {
+ for (int y = 0; y < 4; y++)
+ {
+ for (int x = 0; x < 4; x++)
+ {
+ ModulationModes[y + offsetY][x + offsetX] = (int)WordModMode;
+ ModulationValues[y + offsetY][x + offsetX] = (int)(ModulationBits & 3);
+ ModulationValues[y + offsetY][x + offsetX] *= 3;
+ if (ModulationValues[y + offsetY][x + offsetX] > 3) { ModulationValues[y + offsetY][x + offsetX] -= 1; }
+ ModulationBits >>= 2;
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// array for interpolating colorA/B
+ ///
+ private static readonly int[] RepVals0 = { 0, 3, 5, 8 };
+
+ ///
+ /// 获取用于线性插值的颜色的插值比例(主要给2BPP用的,4BPP直接返回对应位置插值比例)
+ ///
+ /// 2BPP索引/4BPP插值比例指针
+ /// 2BPP调制模式指针
+ /// x坐标
+ /// y坐标
+ /// color bits per pixel
+ /// 插值比例
+ private static int GetModulationValues(int** ModulationValues, int** ModulationModes, uint xPos, uint yPos, byte bpp)
+ {
+ if (bpp == 2)
+ {
+ if (ModulationModes[xPos][yPos] == 0)
+ {
+ return RepVals0[ModulationValues[xPos][yPos]];
+ }
+ else
+ {
+ if (((xPos ^ yPos) & 1) == 0)
+ {
+ return RepVals0[ModulationValues[xPos][yPos]];
+ }
+ else if (ModulationModes[xPos][yPos] == 1)
+ {
+ return (RepVals0[ModulationValues[xPos][yPos - 1]] + RepVals0[ModulationValues[xPos][yPos + 1]] + RepVals0[ModulationValues[xPos - 1][yPos]] + RepVals0[ModulationValues[xPos + 1][yPos]] + 2) >> 2;
+ }
+ else if (ModulationModes[xPos][yPos] == 2)
+ {
+ return (RepVals0[ModulationValues[xPos - 1][yPos]] + RepVals0[ModulationValues[xPos + 1][yPos]] + 1) >> 1;
+ }
+ else
+ {
+ return (RepVals0[ModulationValues[xPos][yPos - 1]] + RepVals0[ModulationValues[xPos][yPos + 1]] + 1) >> 1;
+ }
+ }
+ }
+ else
+ {
+ return ModulationValues[xPos][yPos];
+ }
+ }
+
+ ///
+ /// 获取本地调色板中指定坐标和索引的颜色
+ /// 这是利用下面链接中的本地调色板表格写的函数:
+ /// https://www.khronos.org/registry/DataFormat/specs/1.3/dataformat.1.3.html
+ /// 情况比较复杂,不在这里介绍了
+ ///
+ /// 左上角P点的颜色A(RGBA8888)
+ /// 左上角P点的颜色B(RGBA8888)
+ /// 右上角Q点的颜色A(RGBA8888)
+ /// 右上角Q点的颜色B(RGBA8888)
+ /// 左下角R点的颜色A(RGBA8888)
+ /// 左下角R点的颜色B(RGBA8888)
+ /// 右下角S点的颜色A(RGBA8888)
+ /// 右下角S点的颜色B(RGBA8888)
+ /// 在中间子块中的x坐标
+ /// 在中间子块中的y坐标
+ /// 索引但不完全是索引,而是调制比
+ /// 对应位置的颜色
+ private static Pixel128S GetPaletteColor(Pixel128S Pa, Pixel128S Pb, Pixel128S Qa, Pixel128S Qb, Pixel128S Ra, Pixel128S Rb, Pixel128S Sa, Pixel128S Sb, uint x, uint y, int modvalue)
+ {
+ if (y == 0)
+ {
+ if (x == 0)
+ {
+ if (modvalue == 0)
+ {
+ return Pa;
+ }
+ else if (modvalue == 4 || modvalue == 3)
+ {
+ return new Pixel128S
+ {
+ red = (Pa.red * 5 + Pb.red * 3) >> 3,
+ green = (Pa.green * 5 + Pb.green * 3) >> 3,
+ blue = (Pa.blue * 5 + Pb.blue * 3) >> 3,
+ alpha = (Pa.alpha * 5 + Pb.alpha * 3) >> 3,
+ };
+ }
+ else if (modvalue > 10 || modvalue == 5)
+ {
+ return new Pixel128S
+ {
+ red = (Pa.red * 3 + Pb.red * 5) >> 3,
+ green = (Pa.green * 3 + Pb.green * 5) >> 3,
+ blue = (Pa.blue * 3 + Pb.blue * 5) >> 3,
+ alpha = (Pa.alpha * 3 + Pb.alpha * 5) >> 3,
+ };
+ }
+ else if (modvalue == 8)
+ {
+ return Pb;
+ }
+ }
+ else if (x == 1 || x == 2 || x == 3)
+ {
+ if (modvalue == 0)
+ {
+ return Pa;
+ }
+ else if (modvalue == 4 || modvalue == 3)
+ {
+ return Pb;
+ }
+ else if (modvalue > 10 || modvalue == 5)
+ {
+ return Qa;
+ }
+ else if (modvalue == 8)
+ {
+ return Qb;
+ }
+ }
+ }
+ else if (y == 1)
+ {
+ if (x == 0)
+ {
+ if (modvalue == 0)
+ {
+ return Pa;
+ }
+ else if (modvalue == 4 || modvalue == 3)
+ {
+ return Pb;
+ }
+ else if (modvalue > 10 || modvalue == 5)
+ {
+ return Ra;
+ }
+ else if (modvalue == 8)
+ {
+ return Rb;
+ }
+ }
+ else if (x == 1)
+ {
+ if (modvalue == 0)
+ {
+ return Pa;
+ }
+ else if (modvalue == 4 || modvalue == 3)
+ {
+ return Pb;
+ }
+ else if (modvalue > 10 || modvalue == 5)
+ {
+ return Qa;
+ }
+ else if (modvalue == 8)
+ {
+ return Rb;
+ }
+ }
+ else if (x == 2)
+ {
+ if (modvalue == 0)
+ {
+ return Pa;
+ }
+ else if (modvalue == 4 || modvalue == 3)
+ {
+ return Pb;
+ }
+ else if (modvalue > 10 || modvalue == 5)
+ {
+ return Qa;
+ }
+ else if (modvalue == 8)
+ {
+ return Qb;
+ }
+ }
+ else if (x == 3)
+ {
+ if (modvalue == 0)
+ {
+ return Sa;
+ }
+ else if (modvalue == 4 || modvalue == 3)
+ {
+ return Pb;
+ }
+ else if (modvalue > 10 || modvalue == 5)
+ {
+ return Qa;
+ }
+ else if (modvalue == 8)
+ {
+ return Qb;
+ }
+ }
+ }
+ else if (y == 2)
+ {
+ if (x == 0 || x == 1)
+ {
+ if (modvalue == 0)
+ {
+ return Pa;
+ }
+ else if (modvalue == 4 || modvalue == 3)
+ {
+ return Pb;
+ }
+ else if (modvalue > 10 || modvalue == 5)
+ {
+ return Ra;
+ }
+ else if (modvalue == 8)
+ {
+ return Rb;
+ }
+ }
+ else if (x == 2)
+ {
+ if (modvalue == 0)
+ {
+ return Pa;
+ }
+ else if (modvalue == 4 || modvalue == 3)
+ {
+ return Sb;
+ }
+ else if (modvalue > 10 || modvalue == 5)
+ {
+ return Ra;
+ }
+ else if (modvalue == 8)
+ {
+ return Qb;
+ }
+ }
+ else if (x == 3)
+ {
+ if (modvalue == 0)
+ {
+ return Sa;
+ }
+ else if (modvalue == 4 || modvalue == 3)
+ {
+ return Sb;
+ }
+ else if (modvalue > 10 || modvalue == 5)
+ {
+ return Qa;
+ }
+ else if (modvalue == 8)
+ {
+ return Qb;
+ }
+ }
+ }
+ else if (y == 3)
+ {
+ if (x == 0)
+ {
+ if (modvalue == 0)
+ {
+ return Pa;
+ }
+ else if (modvalue == 4 || modvalue == 3)
+ {
+ return Pb;
+ }
+ else if (modvalue > 10 || modvalue == 5)
+ {
+ return Ra;
+ }
+ else if (modvalue == 8)
+ {
+ return Rb;
+ }
+ }
+ else if (x == 1)
+ {
+ if (modvalue == 0)
+ {
+ return Pa;
+ }
+ else if (modvalue == 4 || modvalue == 3)
+ {
+ return Sb;
+ }
+ else if (modvalue > 10 || modvalue == 5)
+ {
+ return Ra;
+ }
+ else if (modvalue == 8)
+ {
+ return Rb;
+ }
+ }
+ else if (x == 2)
+ {
+ if (modvalue == 0)
+ {
+ return Sa;
+ }
+ else if (modvalue == 4 || modvalue == 3)
+ {
+ return Sb;
+ }
+ else if (modvalue > 10 || modvalue == 5)
+ {
+ return Ra;
+ }
+ else if (modvalue == 8)
+ {
+ return Rb;
+ }
+ }
+ else if (x == 3)
+ {
+ if (modvalue == 0)
+ {
+ return Sa;
+ }
+ else if (modvalue == 4 || modvalue == 3)
+ {
+ return Sb;
+ }
+ else if (modvalue > 10 || modvalue == 5)
+ {
+ return Ra;
+ }
+ else if (modvalue == 8)
+ {
+ return Qb;
+ }
+ }
+ }
+ return default(Pixel128S);
+ }
+
+ ///
+ /// 从4个PVRTCWord中解码出4*4/8*4颜色块
+ ///
+ /// 左上角P点PVRTCWord
+ /// 右上角Q点PVRTCWord
+ /// 左下角R点PVRTCWord
+ /// 右下角S点PVRTCWord
+ /// 颜色块指针
+ /// color bits per pixel
+ /// 索引
+ /// 2BPP调制模式
+ /// 解码的颜色A指针
+ /// 解码的颜色B指针
+ /// 是否为PVRTCII纹理
+ static void PvrtcGetDecompressedPixels(PVRTCWord P, PVRTCWord Q, PVRTCWord R, PVRTCWord S, YFColor* pColorData, byte bpp, int** ModulationValues, int** ModulationModes, Pixel128S* upscaledColorA, Pixel128S* upscaledColorB, bool versionII)
+ {
+ uint WordWidth = bpp == 2 ? 8U : 4U;
+ uint WordHeight = 4;
+ uint HalfWordWidth = WordWidth >> 1;
+ uint HalfWordHeight = WordHeight >> 1;
+ UnpackModulations(P, 0, 0, ModulationValues, ModulationModes, bpp);
+ UnpackModulations(Q, (int)WordWidth, 0, ModulationValues, ModulationModes, bpp);
+ UnpackModulations(R, 0, (int)WordHeight, ModulationValues, ModulationModes, bpp);
+ UnpackModulations(S, (int)WordWidth, (int)WordHeight, ModulationValues, ModulationModes, bpp);
+ YFColor Pa = GetColorA(P.ColorData, versionII);
+ YFColor Pb = GetColorB(P.ColorData, versionII);
+ YFColor Qa = GetColorA(Q.ColorData, versionII);
+ YFColor Qb = GetColorB(Q.ColorData, versionII);
+ YFColor Ra = GetColorA(R.ColorData, versionII);
+ YFColor Rb = GetColorB(R.ColorData, versionII);
+ YFColor Sa = GetColorA(S.ColorData, versionII);
+ YFColor Sb = GetColorB(S.ColorData, versionII);
+ if (versionII && ((P.ColorData & 0x8000) != 0)) // HardTransition,只受P影响
+ {
+ Pixel128S PaD = Color5554To8888(Pa);
+ Pixel128S PbD = Color5554To8888(Pb);
+ Pixel128S QaD = Color5554To8888(Qa);
+ Pixel128S QbD = Color5554To8888(Qb);
+ Pixel128S RaD = Color5554To8888(Ra);
+ Pixel128S RbD = Color5554To8888(Rb);
+ Pixel128S SaD = Color5554To8888(Sa);
+ Pixel128S SbD = Color5554To8888(Sb);
+ for (uint y = 0; y < WordHeight; y++)
+ {
+ for (uint x = 0; x < WordWidth; x++)
+ {
+ int mod = GetModulationValues(ModulationValues, ModulationModes, x + HalfWordWidth, y + HalfWordHeight, bpp);
+ Pixel128S result;
+ if ((bpp != 2) && (ModulationModes[x + HalfWordWidth][y + HalfWordHeight] == 1))
+ {
+ result = GetPaletteColor(PaD, PbD, QaD, QbD, RaD, RbD, SaD, SbD, y, x, mod);
+ }
+ else
+ {
+ uint rX, rY;
+ if (bpp == 2)
+ {
+ rX = x;
+ rY = y;
+ }
+ else // 4BPP一切都是反着的
+ {
+ rX = y;
+ rY = x;
+ }
+ Pixel128S BaseColorA, BaseColorB;
+ if (rX < HalfWordWidth)
+ {
+ // P or R
+ if (rY < HalfWordHeight)
+ {
+ BaseColorA = PaD;
+ BaseColorB = PbD;
+ }
+ else
+ {
+ BaseColorA = RaD;
+ BaseColorB = RbD;
+ }
+ }
+ else
+ {
+ // Q or S
+ if (rY < HalfWordHeight)
+ {
+ BaseColorA = QaD;
+ BaseColorB = QbD;
+ }
+ else
+ {
+ BaseColorA = SaD;
+ BaseColorB = SbD;
+ }
+ }
+ result.red = (BaseColorA.red * (8 - mod) + BaseColorB.red * mod) / 8;
+ result.green = (BaseColorA.green * (8 - mod) + BaseColorB.green * mod) / 8;
+ result.blue = (BaseColorA.blue * (8 - mod) + BaseColorB.blue * mod) / 8;
+ result.alpha = (BaseColorA.alpha * (8 - mod) + BaseColorB.alpha * mod) / 8;
+ }
+ if (bpp == 2)
+ {
+ uint index = y * WordWidth + x;
+ pColorData[index].Red = (byte)result.red;
+ pColorData[index].Green = (byte)result.green;
+ pColorData[index].Blue = (byte)result.blue;
+ pColorData[index].Alpha = (byte)result.alpha;
+ }
+ else
+ {
+ uint index = y + x * WordHeight;
+ pColorData[index].Red = (byte)result.red;
+ pColorData[index].Green = (byte)result.green;
+ pColorData[index].Blue = (byte)result.blue;
+ pColorData[index].Alpha = (byte)result.alpha;
+ }
+ }
+ }
+ }
+ else
+ {
+ InterpolateColors(Pa, Qa, Ra, Sa, upscaledColorA, bpp);
+ InterpolateColors(Pb, Qb, Rb, Sb, upscaledColorB, bpp);
+ for (uint y = 0; y < WordHeight; y++)
+ {
+ for (uint x = 0; x < WordWidth; x++)
+ {
+ int mod = GetModulationValues(ModulationValues, ModulationModes, x + HalfWordWidth, y + HalfWordHeight, bpp);
+ Pixel128S result;
+ uint yWordWidthx = y * WordWidth + x;
+ if (mod > 10)
+ {
+ result.red = 0;
+ result.green = 0;
+ result.blue = 0;
+ result.alpha = 0;
+ }
+ else
+ {
+ result.red = (upscaledColorA[yWordWidthx].red * (8 - mod) + upscaledColorB[yWordWidthx].red * mod) / 8;
+ result.green = (upscaledColorA[yWordWidthx].green * (8 - mod) + upscaledColorB[yWordWidthx].green * mod) / 8;
+ result.blue = (upscaledColorA[yWordWidthx].blue * (8 - mod) + upscaledColorB[yWordWidthx].blue * mod) / 8;
+ result.alpha = (upscaledColorA[yWordWidthx].alpha * (8 - mod) + upscaledColorB[yWordWidthx].alpha * mod) / 8;
+ }
+ if (bpp == 2)
+ {
+ pColorData[yWordWidthx].Red = (byte)result.red;
+ pColorData[yWordWidthx].Green = (byte)result.green;
+ pColorData[yWordWidthx].Blue = (byte)result.blue;
+ pColorData[yWordWidthx].Alpha = (byte)result.alpha;
+ }
+ else
+ {
+ uint index = y + x * WordHeight;
+ pColorData[index].Red = (byte)result.red;
+ pColorData[index].Green = (byte)result.green;
+ pColorData[index].Blue = (byte)result.blue;
+ pColorData[index].Alpha = (byte)result.alpha;
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// Get PVRTCWord real index
+ ///
+ /// word count
+ /// word index
+ /// real index
+ private static uint WrapWordIndex(uint numWords, int word)
+ {
+ return (uint)((word + numWords) % numWords);
+ }
+
+ ///
+ /// Get word index from size and position
+ ///
+ /// imageWidth / blockWidth
+ /// imageHeight / blockHeight
+ /// x in word
+ /// y in word
+ /// 是否为PVRTCII纹理
+ /// index
+ private static uint GetIndex(uint XSize, uint YSize, uint XPos, uint YPos, bool versionII)
+ {
+ if (versionII)
+ {
+ return YPos * XSize + XPos;
+ }
+ else
+ {
+ uint MinDimension = XSize;
+ uint MaxValue = YPos;
+ uint Twiddled = 0;
+ uint SrcBitPos = 1;
+ uint DstBitPos = 1;
+ int ShiftCount = 0;
+ if (YSize < XSize)
+ {
+ MinDimension = YSize;
+ MaxValue = XPos;
+ }
+ while (SrcBitPos < MinDimension)
+ {
+ if ((YPos & SrcBitPos) != 0)
+ {
+ Twiddled |= DstBitPos;
+ }
+ if ((XPos & SrcBitPos) != 0)
+ {
+ Twiddled |= (DstBitPos << 1);
+ }
+ SrcBitPos <<= 1;
+ DstBitPos <<= 2;
+ ShiftCount++;
+ }
+ MaxValue >>= ShiftCount;
+ Twiddled |= MaxValue << (ShiftCount << 1);
+ return Twiddled;
+ }
+ }
+
+ ///
+ /// Copy decoded data to image
+ ///
+ /// image data pointer
+ /// image width
+ /// decoded data pointer
+ /// Word position
+ /// color bits per pixel
+ /// if needs to decode alpha
+ private static void MapDecompressedData(YFColor* pOutput, int width, YFColor* pWord, PVRTCWordIndices words, byte bpp, bool hasA)
+ {
+ uint WordWidth = bpp == 2 ? 8U : 4U;
+ uint WordHeight = 4;
+ uint dw = WordWidth >> 1;
+ uint dh = WordHeight >> 1;
+ for (uint y = 0; y < dh; y++)
+ {
+ for (uint x = 0; x < dw; x++)
+ {
+ long leftIndex = ((words.P[1] * WordHeight) + y + dh) * width + words.P[0] * WordWidth + x + dw;
+ long rightIndex = y * WordWidth + x;
+ pOutput[leftIndex].Red = pWord[rightIndex].Red;
+ pOutput[leftIndex].Green = pWord[rightIndex].Green;
+ pOutput[leftIndex].Blue = pWord[rightIndex].Blue;
+ pOutput[leftIndex].Alpha = hasA ? pWord[rightIndex].Alpha : (byte)0xFF;
+ leftIndex = ((words.Q[1] * WordHeight) + y + dh) * width + words.Q[0] * WordWidth + x;
+ rightIndex = y * WordWidth + x + dw;
+ pOutput[leftIndex].Red = pWord[rightIndex].Red;
+ pOutput[leftIndex].Green = pWord[rightIndex].Green;
+ pOutput[leftIndex].Blue = pWord[rightIndex].Blue;
+ pOutput[leftIndex].Alpha = hasA ? pWord[rightIndex].Alpha : (byte)0xFF;
+ leftIndex = ((words.R[1] * WordHeight) + y) * width + words.R[0] * WordWidth + x + dw;
+ rightIndex = (y + dh) * WordWidth + x;
+ pOutput[leftIndex].Red = pWord[rightIndex].Red;
+ pOutput[leftIndex].Green = pWord[rightIndex].Green;
+ pOutput[leftIndex].Blue = pWord[rightIndex].Blue;
+ pOutput[leftIndex].Alpha = hasA ? pWord[rightIndex].Alpha : (byte)0xFF;
+ leftIndex = ((words.S[1] * WordHeight) + y) * width + words.S[0] * WordWidth + x;
+ rightIndex = (y + dh) * WordWidth + x + dw;
+ pOutput[leftIndex].Red = pWord[rightIndex].Red;
+ pOutput[leftIndex].Green = pWord[rightIndex].Green;
+ pOutput[leftIndex].Blue = pWord[rightIndex].Blue;
+ pOutput[leftIndex].Alpha = hasA ? pWord[rightIndex].Alpha : (byte)0xFF;
+ }
+ }
+ }
+
+ ///
+ /// 解码PVRTC数据
+ ///
+ /// pvrtc word pointer
+ /// image data pointer
+ /// image width
+ /// image height
+ /// color bits per pixel
+ /// 是否为PVRTCII纹理
+ /// if needs to decode alpha
+ public static void DecompressPVRTC(byte* pCompressedData, YFColor* pDecompressedData, uint Width, uint Height, byte bpp, bool versionII, bool hasA)
+ {
+ uint WordWidth = bpp == 2 ? 8U : 4U;
+ uint WordHeight = 4;
+ uint* pWordMembers = (uint*)pCompressedData;
+ int NumXWords = (int)(Width / WordWidth);
+ int NumYWords = (int)(Height / WordHeight);
+ PVRTCWordIndices indices;
+ int alloc_mem =
+ (int)(WordWidth * WordHeight) * sizeof(YFColor)
+ + 4 * sizeof(uint)
+ + 32 * sizeof(Pixel128S)
+ + 32 * sizeof(Pixel128S)
+ + 256 * sizeof(int)
+ + 16 * sizeof(int*)
+ + 16 * sizeof(int*);
+ using (IMemoryOwner owner = MemoryPool.Shared.Rent(alloc_mem))
+ {
+ Span owner_span = owner.Memory.Span[..alloc_mem];
+ fixed (byte* owner_ptr = owner_span)
+ {
+ nuint owner_ptr_num = (nuint)owner_ptr;
+ YFColor* pPixels = (YFColor*)owner_ptr_num; //stackalloc YFColor[(int)(WordWidth * WordHeight)];
+ owner_ptr_num += (uint)((int)(WordWidth * WordHeight) * sizeof(YFColor));
+ uint* WordOffsets = (uint*)owner_ptr_num; // stackalloc uint[4];
+ owner_ptr_num += 4 * sizeof(uint);
+ Pixel128S* upscaledColorA = (Pixel128S*)owner_ptr_num; // stackalloc Pixel128S[32];
+ owner_ptr_num += (uint)(32 * sizeof(Pixel128S));
+ Pixel128S* upscaledColorB = (Pixel128S*)owner_ptr_num; // stackalloc Pixel128S[32];
+ owner_ptr_num += (uint)(32 * sizeof(Pixel128S));
+ int* int_buffer = (int*)owner_ptr_num; // stackalloc int[16 * 8 + 16 * 8];
+ owner_ptr_num += 256 * sizeof(int);
+ int** ModulationValues = (int**)owner_ptr_num; // stackalloc int*[16]; // [16][8]
+ owner_ptr_num += (uint)(16 * sizeof(int*));
+ int** ModulationModes = (int**)owner_ptr_num; // stackalloc int*[16]; // [16][8]
+ owner_ptr_num += (uint)(16 * sizeof(int*));
+ for (int i = 0; i < 16; i++)
+ {
+ ModulationValues[i] = int_buffer;
+ int_buffer += 8;
+ }
+ for (int i = 0; i < 16; i++)
+ {
+ ModulationModes[i] = int_buffer;
+ int_buffer += 8;
+ }
+ //Decompress
+ for (int wordY = -1; wordY < NumYWords - 1; wordY++)
+ {
+ for (int wordX = -1; wordX < NumXWords - 1; wordX++)
+ {
+ indices.P[0] = (int)WrapWordIndex((uint)NumXWords, wordX);
+ indices.P[1] = (int)WrapWordIndex((uint)NumYWords, wordY);
+ indices.Q[0] = (int)WrapWordIndex((uint)NumXWords, wordX + 1);
+ indices.Q[1] = (int)WrapWordIndex((uint)NumYWords, wordY);
+ indices.R[0] = (int)WrapWordIndex((uint)NumXWords, wordX);
+ indices.R[1] = (int)WrapWordIndex((uint)NumYWords, wordY + 1);
+ indices.S[0] = (int)WrapWordIndex((uint)NumXWords, wordX + 1);
+ indices.S[1] = (int)WrapWordIndex((uint)NumYWords, wordY + 1);
+ WordOffsets[0] = GetIndex((uint)NumXWords, (uint)NumYWords, (uint)indices.P[0], (uint)indices.P[1], versionII) << 1;
+ WordOffsets[1] = GetIndex((uint)NumXWords, (uint)NumYWords, (uint)indices.Q[0], (uint)indices.Q[1], versionII) << 1;
+ WordOffsets[2] = GetIndex((uint)NumXWords, (uint)NumYWords, (uint)indices.R[0], (uint)indices.R[1], versionII) << 1;
+ WordOffsets[3] = GetIndex((uint)NumXWords, (uint)NumYWords, (uint)indices.S[0], (uint)indices.S[1], versionII) << 1;
+ PVRTCWord P, Q, R, S;
+ P.ColorData = pWordMembers[WordOffsets[0] + 1];
+ P.ModulationData = pWordMembers[WordOffsets[0]];
+ Q.ColorData = pWordMembers[WordOffsets[1] + 1];
+ Q.ModulationData = pWordMembers[WordOffsets[1]];
+ R.ColorData = pWordMembers[WordOffsets[2] + 1];
+ R.ModulationData = pWordMembers[WordOffsets[2]];
+ S.ColorData = pWordMembers[WordOffsets[3] + 1];
+ S.ModulationData = pWordMembers[WordOffsets[3]];
+ PvrtcGetDecompressedPixels(P, Q, R, S, pPixels, bpp, ModulationValues, ModulationModes, upscaledColorA, upscaledColorB, versionII);
+ MapDecompressedData(pDecompressedData, (int)Width, pPixels, indices, bpp, hasA);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ///
+ /// google image_compression provide RGBA_PVRTCI_2BPP encoding function
+ /// I modify it to support RGB_PVRTCI_4/2BPP, RGBA_PVRTCI_4BPP and RGBA_PVRTCII_4/2BPP encoding
+ /// Source file:
+ /// https://github.com/google/image-compression/blob/master/image_compression/internal/pvrtc_compressor.cc
+ /// Source project:
+ /// https://github.com/google/image-compression
+ ///
+ private static class PVRTCEncoder
+ {
+ ///
+ /// 4BPP格式PVRTCWord采用的模式
+ ///
+ private enum ModulationMode_4BPP
+ {
+ ///
+ /// 对高频低频图中的两个颜色进行双线性插值,索引和颜色比例如下:
+ /// 0: color0
+ /// 1: 5 / 8 * color0 + 3 / 8 * color1
+ /// 2: 3 / 8 * color0 + 5 / 8 * color1
+ /// 3: color1
+ ///
+ StandardBilinear,
+ ///
+ /// (本编码器不支持)对高频低频图中的两个颜色进行线性插值,对
+ /// 索引为2的图像清空其Alpha通道,索引和颜色比例如下:
+ /// 0: color0
+ /// 1: 1 / 2 * color0 + 1 / 2 * color1
+ /// 2: 1 / 2 * color0 + 1 / 2 * color1, Alpha = 0
+ /// 3: color1
+ ///
+ PunchThrough
+ }
+
+ ///
+ /// 2BPP格式PVRTCWord采用的混合模式
+ ///
+ private enum ModulationMode_2BPP
+ {
+ // 1 bit per pixel for each pixel.
+ ///
+ /// 每个点使用1比特直接存储其索引
+ ///
+ Direct1BPP,
+ // The following three modes have 2 bits per pixel for every other pixel in
+ // the block, in a checkerboard pattern. The mode specifies how to infer color
+ // for the intervening pixels.
+ ///
+ /// 存储时每个点使用2比特的方式存储其在高频低频图的线性插值调色板中的索引,
+ /// 然后对未存储索引的点取其水平和垂直方向相邻的四个点的索引值的平均值并四
+ /// 舍五入到最近的整数,对于边缘的点,如果当前PVRTCWord中不包含相邻点,则
+ /// 获取周围的PVRTCWord中对应点的索引,如果周围连PVRTCWord都没有,就去图像
+ /// 的另一侧去找PVRTCWord然后获取对应点的索引
+ ///
+ Interpolated2BPP, // Average the 4 orthoganally connected neighbors.
+ ///
+ /// 存储时每个点使用2比特的方式存储其在高频低频图的线性插值调色板中的索引,
+ /// 然后对未存储索引的点取其垂直方向相邻的两个点的索引值的平均值并四舍五入
+ /// 到最近的整数,对于边缘的点,如果当前PVRTCWord中不包含相邻点,则获取周
+ /// 围的PVRTCWord中对应点的索引,如果周围连PVRTCWord都没有,就去图像的另一
+ /// 侧去找PVRTCWord然后获取对应点的索引
+ ///
+ VerticallyInterpolated2BPP, // Average the 2 vertical neighbors.
+ ///
+ /// 存储时每个点使用2比特的方式存储其在高频低频图的线性插值调色板中的索引,
+ /// 然后对未存储索引的点取其水平方向相邻的两个点的索引值的平均值并四舍五入
+ /// 到最近的整数,对于边缘的点,如果当前PVRTCWord中不包含相邻点,则获取周
+ /// 围的PVRTCWord中对应点的索引,如果周围连PVRTCWord都没有,就去图像的另一
+ /// 侧去找PVRTCWord然后获取对应点的索引
+ ///
+ HorizontallyInterpolated2BPP // Average the 2 horizontal neighbors.
+ };
+
+ // Block width and height as for 2BPP PVRTC.
+ ///
+ /// 2BPP块宽度8是2的3次方,所以这个值是3
+ ///
+ const uint kLog2BlockWidth_2BPP = 3;
+
+ ///
+ /// 2BPP块高度4是2的2次方,所以这个值是2
+ ///
+ const uint kLog2BlockHeight_2BPP = 2;
+
+ ///
+ /// 2BPP块宽度是8
+ ///
+ const uint kBlockWidth_2BPP = 8;
+
+ ///
+ /// 2BPP块高度是4
+ ///
+ const uint kBlockHeight_2BPP = 4;
+
+ // 我加个4BPP支持
+ ///
+ /// 4BPP块宽度4是2的2次方,所以这个值是2
+ ///
+ const uint kLog2BlockWidth_4BPP = 2;
+
+ ///
+ /// 4BPP块高度4是2的2次方,所以这个值是2
+ ///
+ const uint kLog2BlockHeight_4BPP = 2;
+
+ ///
+ /// 4BPP块宽度是4
+ ///
+ const uint kBlockWidth_4BPP = 4;
+
+ ///
+ /// 4BPP块高度是4
+ ///
+ const uint kBlockHeight_4BPP = 4;
+
+ //-----------------------------------------------------------------------------
+
+ //
+ // General helper functions.
+ //
+
+ // Little-endian write.
+ ///
+ /// 将32位无符号整数写入给定指针位置,并返回增加位置后的指针
+ ///
+ /// 被写入的32位无符号整数值
+ /// 被写入的位置指针
+ /// 增加位置后的指针
+ static byte* Append32(uint value, byte* output)
+ {
+ *output++ = (byte)(value & 0xFF);
+ *output++ = (byte)((value >> 8) & 0xFF);
+ *output++ = (byte)((value >> 16) & 0xFF);
+ *output++ = (byte)(value >> 24);
+ return output;
+ }
+
+ ///
+ /// 返回32位有符号整数的绝对值
+ ///
+ /// 被计算绝对值的32位有符号整数
+ /// 计算出的绝对值
+ static uint abs(int v)
+ {
+ if (v <= 0) return (uint)-v;
+ return (uint)v;
+ }
+
+ // A quick measure of how different are two colors for the human eye.
+ // The bigger the return value, the more different.
+ ///
+ /// 快速计算两个颜色的差异值,返回值越大则颜色差异越大
+ /// 原理是分别计算R,G,B,A通道的差值的绝对值,并求和
+ ///
+ /// 第一个颜色
+ /// 第二个颜色
+ /// 差异值(R,G,B,A通道的差值的绝对值的和)
+ static uint ColorDiff(YFColor color0, YFColor color1, bool hasA)
+ {
+ uint rgb_delta = abs(color0.Red - color1.Red) + abs(color0.Green - color1.Green) + abs(color0.Blue - color1.Blue);
+ if (hasA)
+ {
+ rgb_delta += abs(color0.Alpha - color1.Alpha);
+ }
+ return rgb_delta;
+ }
+
+ // Calculates *|x| and *|y| for the Z-order curve value |z|.
+ ///
+ /// 通过反莫顿编码下的颜色索引计算对应的坐标
+ ///
+ /// 反莫顿编码的颜色索引
+ /// 对应x坐标指针
+ /// 对应y坐标指针
+ static void FromZOrder(uint z, uint* x, uint* y, uint width, uint height, bool versionII)
+ {
+ if (versionII)
+ {
+ *y = z / width;
+ *x = z % width;
+ }
+ else
+ {
+ uint minB = width;
+ if (height < width) minB = height;
+ uint AddTimes = z / (minB * minB);
+ uint ReadZ = z & (minB * minB - 1);
+ *x = *y = 0;
+ for (int j = 0; j < 16; j++)
+ {
+ *x |= ((ReadZ >> (j * 2 + 1)) & 1) << j;
+ *y |= ((ReadZ >> (j * 2 + 0)) & 1) << j;
+ }
+ if (width > height)
+ {
+ *x += AddTimes * minB;
+ }
+ else if (width < height)
+ {
+ *y += AddTimes * minB;
+ }
+ }
+ }
+
+ ///
+ /// 获取用于“与运算”取指定数量位数据的值
+ ///
+ /// 取的数据的位数
+ /// 用于“与运算”取指定数量位数据的值
+ static uint GetMask(int num_ones)
+ {
+ return (uint)((1 << num_ones) - 1);
+ }
+
+ // Returns the result of encoding the 8 bits of input down as |bit_depth| bits
+ // and then decoding back up to 8 bits.
+ // Encoding will simply preserve only the top |bit_depth| bits.
+ // Decoding will bitwise-or these bits repeatedly into the result, so that the
+ // output values range as smoothly as possible from 0 to 255.
+ ///
+ /// 返回将输入的8位整数降为指定位数的整数,然后解码回8位的结果
+ ///
+ /// 输入的8位整数
+ /// 位数
+ /// 降级然后解码后的8位整数
+ static byte ApplyBitDepthReduction(byte input, int bit_depth)
+ {
+ byte encoding_mask = (byte)((GetMask(bit_depth) << (8 - bit_depth)) & 0xFF);
+ byte encoded_bits = (byte)(input & encoding_mask);
+ byte result = (byte)(encoded_bits | (encoded_bits >> bit_depth));
+ if (bit_depth <= 3)
+ {
+ // The encoded bits will have to be repeated again for the least significant
+ // output bits.
+ result |= (byte)(encoded_bits >> (bit_depth << 1));
+ }
+ return result;
+ }
+
+ //-----------------------------------------------------------------------------
+
+ //
+ // PVRTC-specific helper functions operating on pixels and blocks of pixels.
+ //
+
+
+ ///
+ /// 对高频低频图中的两个颜色进行线性插值生成调色板,并返回指定索引位置的颜色
+ /// 4BPP插值方式依赖于索引和ModulationFlags
+ /// 如果ModulationFlags是0,那么对不同索引有如下四种插值方式:
+ /// 0 = color0
+ /// 1 = 5/8ths color0, 3/8ths color1
+ /// 2 = 3/8ths color0, 5/8ths color1
+ /// 3 = color1
+ /// 如果ModulationFlags是1,那么对不同索引有如下四种插值方式:
+ /// 0 = color0
+ /// 1 = 4/8ths color0, 4/8ths color1
+ /// 2 = 4/8ths color0, 4/8ths color1, Alpha = 0
+ /// 3 = color1
+ ///
+ /// 高频信号颜色
+ /// 低频信号颜色
+ /// 颜色索引
+ /// 调制模式
+ /// 索引对应颜色
+ static YFColor ApplyModulation_4BPP(YFColor color0, YFColor color1, uint mod, ModulationMode_4BPP flags)
+ {
+ YFColor result = default(YFColor);
+ switch (flags)
+ {
+ case ModulationMode_4BPP.StandardBilinear:
+ switch (mod)
+ {
+ case 0:
+ result = color0;
+ break;
+ case 1:
+ result.Red = (byte)((5 * color0.Red + 3 * color1.Red) >> 3);
+ result.Green = (byte)((5 * color0.Green + 3 * color1.Green) >> 3);
+ result.Blue = (byte)((5 * color0.Blue + 3 * color1.Blue) >> 3);
+ result.Alpha = (byte)((5 * color0.Alpha + 3 * color1.Alpha) >> 3);
+ break;
+ case 2:
+ result.Red = (byte)((3 * color0.Red + 5 * color1.Red) >> 3);
+ result.Green = (byte)((3 * color0.Green + 5 * color1.Green) >> 3);
+ result.Blue = (byte)((3 * color0.Blue + 5 * color1.Blue) >> 3);
+ result.Alpha = (byte)((3 * color0.Alpha + 5 * color1.Alpha) >> 3);
+ break;
+ case 3:
+ result = color1;
+ break;
+ }
+ break;
+ case ModulationMode_4BPP.PunchThrough:
+ switch (mod)
+ {
+ case 0:
+ result = color0;
+ break;
+ case 1:
+ result.Red = (byte)((color0.Red + color1.Red) >> 1);
+ result.Green = (byte)((color0.Green + color1.Green) >> 1);
+ result.Blue = (byte)((color0.Blue + color1.Blue) >> 1);
+ result.Alpha = (byte)((color0.Alpha + color1.Alpha) >> 1);
+ break;
+ case 2:
+ result.Red = (byte)((color0.Red + color1.Red) >> 1);
+ result.Green = (byte)((color0.Green + color1.Green) >> 1);
+ result.Blue = (byte)((color0.Blue + color1.Blue) >> 1);
+ result.Alpha = 0;
+ break;
+ case 3:
+ result = color1;
+ break;
+ }
+ break;
+ }
+ return result;
+ }
+
+ // Returns the color interpolated between |color0| and |color1| as specified by
+ // |mod| which can range from 0 to 3:
+ // 0 = color0
+ // 1 = 5/8ths color0, 3/8ths color1
+ // 2 = 3/8ths color0, 5/8ths color1
+ // 3 = color1
+ ///
+ /// 对高频低频图中的两个颜色进行线性插值生成调色板,并返回指定索引位置的颜色
+ /// 2BPP插值方式依赖于索引,对不同索引有如下四种插值方式:
+ /// 0 = color0
+ /// 1 = 5/8ths color0, 3/8ths color1
+ /// 2 = 3/8ths color0, 5/8ths color1
+ /// 3 = color1
+ ///
+ /// 高频信号颜色
+ /// 低频信号颜色
+ /// 颜色索引
+ /// 索引对应颜色
+ static YFColor ApplyModulation_2BPP(YFColor color0, YFColor color1, uint mod)
+ {
+ YFColor result;// = color0;
+ switch (mod)
+ {
+ case 0:
+ result = color0;
+ break;
+ case 1:
+ result.Red = (byte)((5 * color0.Red + 3 * color1.Red) >> 3);
+ result.Green = (byte)((5 * color0.Green + 3 * color1.Green) >> 3);
+ result.Blue = (byte)((5 * color0.Blue + 3 * color1.Blue) >> 3);
+ result.Alpha = (byte)((5 * color0.Alpha + 3 * color1.Alpha) >> 3);
+ break;
+ case 2:
+ result.Red = (byte)((3 * color0.Red + 5 * color1.Red) >> 3);
+ result.Green = (byte)((3 * color0.Green + 5 * color1.Green) >> 3);
+ result.Blue = (byte)((3 * color0.Blue + 5 * color1.Blue) >> 3);
+ result.Alpha = (byte)((3 * color0.Alpha + 5 * color1.Alpha) >> 3);
+ break;
+ case 3:
+ result = color1;
+ break;
+ default:
+ throw new Exception($"PVRTCCoder: ApplyModulation_2BPP with unknow mod {mod}");
+ }
+ return result;
+ }
+
+ // 返回最能表示color的索引
+ ///
+ /// 在高频低频颜色生成的调色版中返回最能表示指定颜色的索引
+ /// 函数在生成调色版后,对给定颜色依次计算每个颜色的差异值
+ /// 然后选择差异值最小的索引,返回该索引值
+ ///
+ /// 给定颜色
+ /// 高频信号颜色
+ /// 低频信号颜色
+ /// 调制模式
+ /// 最适合颜色的索引
+ static uint BestModulation_4BPP(YFColor color, YFColor color0, YFColor color1, ModulationMode_4BPP flags, bool hasA)
+ {
+ uint diff = ColorDiff(color, color0, hasA);
+ uint best_diff = diff;
+ uint best_mod = 0;
+
+ for (uint current_mod = 1; current_mod < 4; ++current_mod)
+ {
+ YFColor current_color = ApplyModulation_4BPP(color0, color1, current_mod, flags);
+ diff = ColorDiff(color, current_color, hasA);
+ if (diff < best_diff)
+ {
+ best_diff = diff;
+ best_mod = current_mod;
+ }
+ }
+
+ return best_mod;
+ }
+
+ // Returns which modulation (from 0 through 3) best represents |color| given
+ // the color palette |color0| and |color1|.
+ // 返回最能表示color的索引
+ ///
+ /// 在高频低频颜色生成的调色版中返回最能表示指定颜色的索引
+ /// 函数在生成调色版后,对给定颜色依次计算每个颜色的差异值
+ /// 然后选择差异值最小的索引,返回该索引值
+ ///
+ /// 给定颜色
+ /// 高频信号颜色
+ /// 低频信号颜色
+ /// 最适合颜色的索引
+ static uint BestModulation_2BPP(YFColor color, YFColor color0, YFColor color1, bool hasA)
+ {
+ uint diff = ColorDiff(color, color0, hasA);
+ uint best_diff = diff;
+ uint best_mod = 0;
+
+ for (uint current_mod = 1; current_mod < 4; ++current_mod)
+ {
+ YFColor current_color = ApplyModulation_2BPP(color0, color1, current_mod);
+ diff = ColorDiff(color, current_color, hasA);
+ if (diff < best_diff)
+ {
+ best_diff = diff;
+ best_mod = current_mod;
+ }
+ else
+ {
+ // If it's not getting better here, it won't get better later.
+ return best_mod;
+ }
+ }
+
+ return best_mod;
+ }
+
+ ///
+ /// 返回指定位置在四种颜色之间进行插值所得颜色(我写的位置可能有误)
+ ///
+ /// 左上角颜色
+ /// 右上角颜色
+ /// 左下角颜色
+ /// 右下角颜色
+ /// 在四个颜色块中的x坐标
+ /// 在四个颜色块中的y坐标
+ /// 插值所得颜色
+ static YFColor Interpolate4_4BPP(YFColor color00, YFColor color01, YFColor color10, YFColor color11, uint px, uint py)
+ {
+ // Calculate the weights that should be applied to the four input colors.
+ uint a = (kBlockHeight_4BPP - py) * (kBlockWidth_4BPP - px);
+ uint b = (kBlockHeight_4BPP - py) * px;
+ uint c = py * (kBlockWidth_4BPP - px);
+ uint d = py * px;
+ // Apply these weights.
+ uint downscale = kBlockWidth_4BPP * kBlockHeight_4BPP;
+ return new YFColor(
+ (byte)((a * color00.Red + b * color01.Red + c * color10.Red + d * color11.Red) /
+ downscale),
+ (byte)((a * color00.Green + b * color01.Green + c * color10.Green + d * color11.Green) /
+ downscale),
+ (byte)((a * color00.Blue + b * color01.Blue + c * color10.Blue + d * color11.Blue) /
+ downscale),
+ (byte)((a * color00.Alpha + b * color01.Alpha + c * color10.Alpha + d * color11.Alpha) /
+ downscale));
+ }
+
+ // Returns a color bilinearly interpolated between the four input colors.
+ // |px| ranges from 0 (pure |color00| or |color01|) to
+ // kBlockWidth (pure |color10| or color11|).
+ // |py| ranges from 0 (pure |color00| or |color10|) to
+ // kBlockHeight (pure |color01| or |color11|).
+ ///
+ /// 返回指定位置在四种颜色之间进行插值所得颜色(我写的位置可能有误)
+ ///
+ /// 左上角颜色
+ /// 右上角颜色
+ /// 左下角颜色
+ /// 右下角颜色
+ /// 在四个颜色块中的x坐标
+ /// 在四个颜色块中的y坐标
+ /// 插值所得颜色
+ static YFColor Interpolate4_2BPP(YFColor color00, YFColor color01, YFColor color10, YFColor color11, uint px, uint py)
+ {
+ // Calculate the weights that should be applied to the four input colors.
+ uint a = (kBlockHeight_2BPP - py) * (kBlockWidth_2BPP - px);
+ uint b = (kBlockHeight_2BPP - py) * px;
+ uint c = py * (kBlockWidth_2BPP - px);
+ uint d = py * px;
+ // Apply these weights.
+ uint downscale = kBlockWidth_2BPP * kBlockHeight_2BPP;
+ return new YFColor(
+ (byte)((a * color00.Red + b * color01.Red + c * color10.Red + d * color11.Red) /
+ downscale),
+ (byte)((a * color00.Green + b * color01.Green + c * color10.Green + d * color11.Green) /
+ downscale),
+ (byte)((a * color00.Blue + b * color01.Blue + c * color10.Blue + d * color11.Blue) /
+ downscale),
+ (byte)((a * color00.Alpha + b * color01.Alpha + c * color10.Alpha + d * color11.Alpha) /
+ downscale));
+ }
+
+ ///
+ /// 对于整个图像和给定点的坐标,返回此点在输入图像的双线性放大版本中
+ /// 像素的颜色
+ /// 输入图像在宽度上放大了kBlockWidth,在高度上放大了kBlockHeight
+ /// 插值覆盖图像的所有四条边
+ /// 对于放大图像中大小为kBlockWidth * kBlockHeight的每个像素块,以左
+ /// 上角为坐标原点,在(kBlockWidth / 2,kBlockHeight / 2)位置处的像
+ /// 素将使用不相关的颜色,其余将进行插值。
+ /// 根据
+ /// https://www.khronos.org/registry/gles/extensions/IMG/IMG_texture_compression_pvrtc.txt
+ /// 宽度和高度必须是2的幂
+ ///
+ /// 图像指针
+ /// 图像宽度,2的幂
+ /// 图像高度,2的幂
+ /// 给定点的x坐标
+ /// 给定点的y坐标
+ /// 插值后的颜色
+ static YFColor GetInterpolatedColor4BPP(YFColor* source, uint width, uint height, uint x, uint y)
+ {
+ // The left, top, right and bottom edges of the 2x2 pixel block in the source
+ // image that will be used to interpolate. Note that through wrapping (for
+ // example) source_left may be to the right of source_right.
+ // width and height are not power-of-two, so we can not use '&' instead of '%'.
+ uint source_left =
+ ((x - kBlockWidth_4BPP / 2) % width) >> (int)kLog2BlockWidth_4BPP;
+ uint source_top =
+ ((y - kBlockHeight_4BPP / 2) % height) >> (int)kLog2BlockHeight_4BPP;
+ uint source_right =
+ (source_left + 1) % (width >> (int)kLog2BlockWidth_4BPP);
+ uint source_bottom =
+ (source_top + 1) % (height >> (int)kLog2BlockHeight_4BPP);
+
+ // The bilinear weights to be used for interpolation.
+ uint x_weight = (x + kBlockWidth_4BPP / 2) % kBlockWidth_4BPP;
+ uint y_weight = (y + kBlockHeight_4BPP / 2) % kBlockHeight_4BPP;
+
+ uint source_width = width / kBlockWidth_4BPP;
+ YFColor color00 = source[source_top * source_width + source_left];
+ YFColor color01 = source[source_top * source_width + source_right];
+ YFColor color10 = source[source_bottom * source_width + source_left];
+ YFColor color11 = source[source_bottom * source_width + source_right];
+
+ return Interpolate4_4BPP(color00, color01, color10, color11, x_weight, y_weight);
+ }
+
+ // Returns the color for a pixel in a bilinearly upscaled version of an input
+ // image. The input image is upscaled kBlockWidth in width and kBlockHeight in
+ // height. The bilinear interpolation wraps on all four edges of the image.
+ // For every block of pixels of size (kBlockWidth * kBlockHeight) in the
+ // upscaled image, where the top left is (0,0), the pixel at position
+ // (kBlockWidth / 2, kBlockHeight / 2) will use the uninterpolated
+ // low-frequency image colors, and the rest will be interpolated.
+ // |source| the raw pixel data for the input image.
+ // |width| width of the upscaled image.
+ // |height| height of the upscaled image.
+ // |x| and |y| the position of the pixel in the upscaled image.
+ // According to:
+ // https://www.khronos.org/registry/gles/extensions/IMG/IMG_texture_compression_pvrtc.txt
+ // width and height must be power-of-two.
+ ///
+ /// 对于整个图像和给定点的坐标,返回此点在输入图像的双线性放大版本中
+ /// 像素的颜色
+ /// 输入图像在宽度上放大了kBlockWidth,在高度上放大了kBlockHeight
+ /// 插值覆盖图像的所有四条边
+ /// 对于放大图像中大小为kBlockWidth * kBlockHeight的每个像素块,以左
+ /// 上角为坐标原点,在(kBlockWidth / 2,kBlockHeight / 2)位置处的像
+ /// 素将使用不相关的颜色,其余将进行插值。
+ /// 根据
+ /// https://www.khronos.org/registry/gles/extensions/IMG/IMG_texture_compression_pvrtc.txt
+ /// 宽度和高度必须是2的幂
+ ///
+ /// 图像指针
+ /// 图像宽度,2的幂
+ /// 图像高度,2的幂
+ /// 给定点的x坐标
+ /// 给定点的y坐标
+ /// 插值后的颜色
+ static YFColor GetInterpolatedColor2BPP(YFColor* source, uint width, uint height, uint x, uint y)
+ {
+ // The left, top, right and bottom edges of the 2x2 pixel block in the source
+ // image that will be used to interpolate. Note that through wrapping (for
+ // example) source_left may be to the right of source_right.
+ // width and height are not power-of-two, so we can not use '&' instead of '%'.
+ uint source_left =
+ ((x - kBlockWidth_2BPP / 2) % width) >> (int)kLog2BlockWidth_2BPP;
+ uint source_top =
+ ((y - kBlockHeight_2BPP / 2) % height) >> (int)kLog2BlockHeight_2BPP;
+ uint source_right =
+ (source_left + 1) % (width >> (int)kLog2BlockWidth_2BPP);
+ uint source_bottom =
+ (source_top + 1) % (height >> (int)kLog2BlockHeight_2BPP);
+
+ // The bilinear weights to be used for interpolation.
+ uint x_weight = (x + kBlockWidth_2BPP / 2) % kBlockWidth_2BPP;
+ uint y_weight = (y + kBlockHeight_2BPP / 2) % kBlockHeight_2BPP;
+
+ uint source_width = width / kBlockWidth_2BPP;
+ YFColor color00 = source[source_top * source_width + source_left];
+ YFColor color01 = source[source_top * source_width + source_right];
+ YFColor color10 = source[source_bottom * source_width + source_left];
+ YFColor color11 = source[source_bottom * source_width + source_right];
+
+ return Interpolate4_2BPP(color00, color01, color10, color11, x_weight, y_weight);
+ }
+
+ // An ordering for colors roughly based on brightness.
+ ///
+ /// 大致基于亮度的颜色排序值,用于确定颜色相对是低频还是高频
+ /// 原理是求R,G,B,A通道的和
+ ///
+ /// 给定颜色
+ /// 颜色排序值(R,G,B,A通道的和)
+ static uint ColorBrightnessOrder(YFColor color)
+ {
+ return (uint)color.Red + color.Green + color.Blue + color.Alpha;
+ }
+
+ static byte GetChannel(YFColor color, int i, bool hasA)
+ {
+ return i switch
+ {
+ 0 => color.Red,
+ 1 => color.Green,
+ 2 => color.Blue,
+ 3 => hasA ? color.Alpha : (byte)0xFF,
+ _ => 0
+ };
+ }
+
+ // 获取块中颜色的极值
+ ///
+ /// 获取一个PVRTC块中的高频低频信号颜色
+ ///
+ /// 图像指针
+ /// 图像宽度,2的幂
+ /// 图像高度,2的幂
+ /// PVRTC块x坐标
+ /// PVRTC块y坐标
+ /// 高频信号颜色指针
+ /// 低频信号颜色指针
+ static void GetExtremesFast_4BPP(YFColor* image, uint width, uint height, uint x0, uint y0, uint* out_index_0, uint* out_index_1, bool hasA)
+ {
+ // Consider 5 different pairs; lightness, then R, G, B, A axes.
+ uint* UINT_BUFFER = stackalloc uint[20];
+ uint** best_fitness = stackalloc uint*[5]; //uint best_fitness[5][10]
+ uint** best_index = stackalloc uint*[5]; //uint best_index[5][10]
+ for (int i = 0; i < 5; i++)
+ {
+ best_fitness[i] = UINT_BUFFER;
+ UINT_BUFFER += 2;
+ }
+ for (int i = 0; i < 5; i++)
+ {
+ best_index[i] = UINT_BUFFER;
+ UINT_BUFFER += 2;
+ }
+ for (uint i = 0; i < 5; i++)
+ {
+ // For each pair of colors, the first must have the lowest possible value
+ // for the tested fitness, the second the highest possible; hence
+ // initialize "best" with extreme high and low values.
+ best_fitness[i][0] = 0xFFFFFFFFU;
+ best_fitness[i][1] = 0;
+ best_index[i][0] = 0;
+ best_index[i][1] = 0;
+ }
+
+ for (uint y = y0; y < y0 + kBlockHeight_4BPP; y++)
+ {
+ for (uint x = x0; x < x0 + kBlockWidth_4BPP; x++)
+ {
+ uint x_wrapped = (x + width) % width;
+ uint y_wrapped = (y + height) % height;
+ uint index = y_wrapped * width + x_wrapped;
+ YFColor color = image[index];
+
+ // For the first pair, use the lightness.
+ uint lightness = (uint)((77 * color.Red + 150 * color.Green + 28 * color.Blue) / 256);
+ if (lightness < best_fitness[0][0])
+ {
+ best_fitness[0][0] = lightness;
+ best_index[0][0] = index;
+ }
+ if (lightness > best_fitness[0][1])
+ {
+ best_fitness[0][1] = lightness;
+ best_index[0][1] = index;
+ }
+
+ // For the next 4 axes, use the R, G, B or A axis.
+ for (int component = 0; component < 4; component++)
+ {
+ int output_pair = component + 1;
+ byte c = GetChannel(color, component, hasA);
+ if (c < best_fitness[output_pair][0])
+ {
+ best_fitness[output_pair][0] = c;
+ best_index[output_pair][0] = index;
+ }
+ if (c > best_fitness[output_pair][1])
+ {
+ best_fitness[output_pair][1] = c;
+ best_index[output_pair][1] = index;
+ }
+ }
+ }
+ }
+
+ // Choose the pair for which the color difference is biggest. This makes the
+ // algorithm somewhat principal component-ish.
+ uint best_pair_diff = 0;
+ uint best_pair = 0;
+ for (uint i = 0; i < 5; i++)
+ {
+ uint diff = ColorDiff(image[best_index[i][0]], image[best_index[i][1]], hasA);
+ if (diff > best_pair_diff)
+ {
+ best_pair = i;
+ best_pair_diff = diff;
+ }
+ }
+
+ *out_index_0 = best_index[best_pair][0];
+ *out_index_1 = best_index[best_pair][1];
+
+ // *out_index_0 should be darker than *out_index_1 for consistency; swap if
+ // not.
+ if (ColorBrightnessOrder(image[*out_index_1]) < ColorBrightnessOrder(image[*out_index_0]))
+ {
+ uint temp = *out_index_0;
+ *out_index_0 = *out_index_1;
+ *out_index_1 = temp;
+ }
+ }
+
+ // Gets two colors that represent extremes of the range of colors within a block
+ // in a source image. A fast alternative to principal component analysis.
+ // This function also takes care of the wrapping of the coordinates, i.e. |x0|
+ // and |y0| can be outside the bounds of the image.
+ // |image| the source image pixel data.
+ // |width| source image width (must be a power of two).
+ // |height| source image height (must be a power of two).
+ // |x0| left edge of the block to be considered in pixels.
+ // |y0| top edge of the block to be considered in pixels.
+ // |out_index_0|, |out_index_1| output colors as indices into |image|.
+ ///
+ /// 获取一个PVRTC块中的高频低频信号颜色
+ ///
+ /// 图像指针
+ /// 图像宽度,2的幂
+ /// 图像高度,2的幂
+ /// PVRTC块x坐标
+ /// PVRTC块y坐标
+ /// 高频信号颜色指针
+ /// 低频信号颜色指针
+ static void GetExtremesFast_2BPP(YFColor* image, uint width, uint height, uint x0, uint y0, uint* out_index_0, uint* out_index_1, bool hasA)
+ {
+ // Consider 5 different pairs; lightness, then R, G, B, A axes.
+ uint* UINT_BUFFER = stackalloc uint[20];
+ uint** best_fitness = stackalloc uint*[5]; //uint best_fitness[5][10]
+ uint** best_index = stackalloc uint*[5]; //uint best_index[5][10]
+ for (int i = 0; i < 5; i++)
+ {
+ best_fitness[i] = UINT_BUFFER;
+ UINT_BUFFER += 2;
+ }
+ for (int i = 0; i < 5; i++)
+ {
+ best_index[i] = UINT_BUFFER;
+ UINT_BUFFER += 2;
+ }
+ for (uint i = 0; i < 5; i++)
+ {
+ // For each pair of colors, the first must have the lowest possible value
+ // for the tested fitness, the second the highest possible; hence
+ // initialize "best" with extreme high and low values.
+ best_fitness[i][0] = 0xFFFFFFFFU;
+ best_fitness[i][1] = 0;
+ best_index[i][0] = 0;
+ best_index[i][1] = 0;
+ }
+
+ for (uint y = y0; y < y0 + kBlockHeight_2BPP; y++)
+ {
+ for (uint x = x0; x < x0 + kBlockWidth_2BPP; x++)
+ {
+ uint x_wrapped = (x + width) % width;
+ uint y_wrapped = (y + height) % height;
+ uint index = y_wrapped * width + x_wrapped;
+ YFColor color = image[index];
+
+ // For the first pair, use the lightness.
+ uint lightness = (uint)((77 * color.Red + 150 * color.Green + 28 * color.Blue) / 256);
+ if (lightness < best_fitness[0][0])
+ {
+ best_fitness[0][0] = lightness;
+ best_index[0][0] = index;
+ }
+ if (lightness > best_fitness[0][1])
+ {
+ best_fitness[0][1] = lightness;
+ best_index[0][1] = index;
+ }
+
+ // For the next 4 axes, use the R, G, B or A axis.
+ for (int component = 0; component < 4; component++)
+ {
+ int output_pair = component + 1;
+ byte c = GetChannel(color, component, hasA);
+ if (c < best_fitness[output_pair][0])
+ {
+ best_fitness[output_pair][0] = c;
+ best_index[output_pair][0] = index;
+ }
+ if (c > best_fitness[output_pair][1])
+ {
+ best_fitness[output_pair][1] = c;
+ best_index[output_pair][1] = index;
+ }
+ }
+ }
+ }
+
+ // Choose the pair for which the color difference is biggest. This makes the
+ // algorithm somewhat principal component-ish.
+ uint best_pair_diff = 0;
+ uint best_pair = 0;
+ for (uint i = 0; i < 5; i++)
+ {
+ uint diff = ColorDiff(image[best_index[i][0]], image[best_index[i][1]], hasA);
+ if (diff > best_pair_diff)
+ {
+ best_pair = i;
+ best_pair_diff = diff;
+ }
+ }
+
+ *out_index_0 = best_index[best_pair][0];
+ *out_index_1 = best_index[best_pair][1];
+
+ // *out_index_0 should be darker than *out_index_1 for consistency; swap if
+ // not.
+ if (ColorBrightnessOrder(image[*out_index_1]) <
+ ColorBrightnessOrder(image[*out_index_0]))
+ {
+ uint temp = *out_index_0;
+ *out_index_0 = *out_index_1;
+ *out_index_1 = temp;
+ }
+ }
+
+ // Returns the color that the input color will become after encoding as an "A"
+ // or "B" color in a PVRTC compressed image (where they are converted to
+ // 16-bit), and then decoding back to 32-bit.
+ // This helps the compressor choose correct modulation values once the "A" and
+ // "B" colors are chosen.
+ // |is_b| is true if this is the "B" color; "A" and "B" are encoded differently.
+ ///
+ /// 将高频或低频颜色降级为PVRTCWord中存储的位数后解码为8位,并返回解码后的值
+ ///
+ /// 高频或低频颜色
+ /// 若为低频颜色则值为true,否则为false
+ /// 降级后解码所得颜色
+ static YFColor ApplyColorChannelReduction(YFColor color, bool is_b, bool hasA)
+ {
+ if ((!hasA) || color.Alpha == 255)
+ {
+ color.Red = ApplyBitDepthReduction(color.Red, 5);
+ color.Green = ApplyBitDepthReduction(color.Green, 5);
+ color.Blue = ApplyBitDepthReduction(color.Blue, is_b ? 5 : 4);
+ }
+ else
+ {
+ color.Red = ApplyBitDepthReduction(color.Red, 4);
+ color.Green = ApplyBitDepthReduction(color.Green, 4);
+ color.Blue = ApplyBitDepthReduction(color.Blue, is_b ? 4 : 3);
+ color.Alpha = ApplyBitDepthReduction(color.Alpha, 3);
+ }
+ return color;
+ }
+
+ // 将两种颜色和一个调制位编码为32位无符号整数
+ ///
+ /// 将高频低频信号颜色和调制位编码为32位无符号整数
+ ///
+ /// 高频颜色信号
+ /// 低频颜色信号
+ /// 调制模式
+ /// 编码的32位无符号整数
+ static uint EncodeColors_4BPP(YFColor colora, YFColor colorb, ModulationMode_4BPP mode, bool versionII, bool hasA)
+ {
+ uint value = 0;
+
+ if (versionII)
+ {
+ if ((!hasA) || (colora.Alpha == 255 && colorb.Alpha == 255))
+ {
+ SetBits(31, 1, 1, &value);
+ SetBits(16, 5, colorb.Blue >> 3, &value);
+ SetBits(21, 5, colorb.Green >> 3, &value);
+ SetBits(26, 5, colorb.Red >> 3, &value);
+ SetBits(15, 1, 0, &value); // Hard
+ SetBits(1, 4, colora.Blue >> 4, &value);
+ SetBits(5, 5, colora.Green >> 3, &value);
+ SetBits(10, 5, colora.Red >> 3, &value);
+ }
+ else
+ {
+ SetBits(31, 1, 0, &value);
+ SetBits(16, 4, colorb.Blue >> 4, &value);
+ SetBits(20, 4, colorb.Green >> 4, &value);
+ SetBits(24, 4, colorb.Red >> 4, &value);
+ SetBits(28, 3, colorb.Alpha >> 5, &value);
+ SetBits(15, 1, 0, &value); // Hard
+ SetBits(1, 3, colora.Blue >> 5, &value);
+ SetBits(4, 4, colora.Green >> 4, &value);
+ SetBits(8, 4, colora.Red >> 4, &value);
+ SetBits(12, 3, colora.Alpha >> 5, &value);
+ }
+ }
+ else
+ {
+ if ((!hasA) || colora.Alpha == 255)
+ {
+ SetBits(15, 1, 1, &value);
+ SetBits(1, 4, colora.Blue >> 4, &value);
+ SetBits(5, 5, colora.Green >> 3, &value);
+ SetBits(10, 5, colora.Red >> 3, &value);
+ }
+ else
+ {
+ SetBits(15, 1, 0, &value);
+ SetBits(1, 3, colora.Blue >> 5, &value);
+ SetBits(4, 4, colora.Green >> 4, &value);
+ SetBits(8, 4, colora.Red >> 4, &value);
+ SetBits(12, 3, colora.Alpha >> 5, &value);
+ }
+ if ((!hasA) || colorb.Alpha == 255)
+ {
+ SetBits(31, 1, 1, &value);
+ SetBits(16, 5, colorb.Blue >> 3, &value);
+ SetBits(21, 5, colorb.Green >> 3, &value);
+ SetBits(26, 5, colorb.Red >> 3, &value);
+ }
+ else
+ {
+ SetBits(31, 1, 0, &value);
+ SetBits(16, 4, colorb.Blue >> 4, &value);
+ SetBits(20, 4, colorb.Green >> 4, &value);
+ SetBits(24, 4, colorb.Red >> 4, &value);
+ SetBits(28, 3, colorb.Alpha >> 5, &value);
+ }
+ }
+ SetBits(0, 1, mode == ModulationMode_4BPP.StandardBilinear ? 0 : 1, &value);
+ return value;
+ }
+
+ // Encode two colors and a modulation mode into an unsigned int.
+ // The encoding is as follows, in the direction from MSB to LSB:
+ // 16 bit |colora|, 15 bit |colorb|, 1 bit |mod_mode|.
+ // Opaque colors are: 1 bit 1, 5 bit R, 5 bit G, 4/5 bit B.
+ // Translucent colors are: 1 bit 0, 3 bit A, 4 bit R, 4 bit G, 3/4 bit B.
+ ///
+ /// 将高频低频信号颜色和调制位编码为32位无符号整数
+ ///
+ /// 高频颜色信号
+ /// 低频颜色信号
+ /// 调制模式
+ /// 编码的32位无符号整数
+ static uint EncodeColors_2BPP(YFColor colora, YFColor colorb, ModulationMode_2BPP mode, bool versionII, bool hasA)
+ {
+ uint value = 0;
+ if (versionII)
+ {
+ if ((!hasA) || (colora.Alpha == 255 && colorb.Alpha == 255))
+ {
+ SetBits(31, 1, 1, &value);
+ SetBits(16, 5, colorb.Blue >> 3, &value);
+ SetBits(21, 5, colorb.Green >> 3, &value);
+ SetBits(26, 5, colorb.Red >> 3, &value);
+ SetBits(15, 1, 0, &value);
+ SetBits(1, 4, colora.Blue >> 4, &value);
+ SetBits(5, 5, colora.Green >> 3, &value);
+ SetBits(10, 5, colora.Red >> 3, &value);
+ }
+ else
+ {
+ SetBits(31, 1, 0, &value);
+ SetBits(16, 4, colorb.Blue >> 4, &value);
+ SetBits(20, 4, colorb.Green >> 4, &value);
+ SetBits(24, 4, colorb.Red >> 4, &value);
+ SetBits(28, 3, colorb.Alpha >> 5, &value);
+ SetBits(15, 1, 0, &value);
+ SetBits(1, 3, colora.Blue >> 5, &value);
+ SetBits(4, 4, colora.Green >> 4, &value);
+ SetBits(8, 4, colora.Red >> 4, &value);
+ SetBits(12, 3, colora.Alpha >> 5, &value);
+ }
+ }
+ else
+ {
+ if ((!hasA) || colora.Alpha == 255)
+ {
+ SetBits(15, 1, 1, &value);
+ SetBits(1, 4, colora.Blue >> 4, &value);
+ SetBits(5, 5, colora.Green >> 3, &value);
+ SetBits(10, 5, colora.Red >> 3, &value);
+ }
+ else
+ {
+ SetBits(15, 1, 0, &value);
+ SetBits(1, 3, colora.Blue >> 5, &value);
+ SetBits(4, 4, colora.Green >> 4, &value);
+ SetBits(8, 4, colora.Red >> 4, &value);
+ SetBits(12, 3, colora.Alpha >> 5, &value);
+ }
+ if ((!hasA) || colorb.Alpha == 255)
+ {
+ SetBits(31, 1, 1, &value);
+ SetBits(16, 5, colorb.Blue >> 3, &value);
+ SetBits(21, 5, colorb.Green >> 3, &value);
+ SetBits(26, 5, colorb.Red >> 3, &value);
+ }
+ else
+ {
+ SetBits(31, 1, 0, &value);
+ SetBits(16, 4, colorb.Blue >> 4, &value);
+ SetBits(20, 4, colorb.Green >> 4, &value);
+ SetBits(24, 4, colorb.Red >> 4, &value);
+ SetBits(28, 3, colorb.Alpha >> 5, &value);
+ }
+ }
+ SetBits(0, 1, mode == ModulationMode_2BPP.Direct1BPP ? 0 : 1, &value);
+ return value;
+ }
+
+ ///
+ /// 设置给定32位无符号整数中的指定位为指定值
+ ///
+ /// 起始位
+ /// 位长度
+ /// 设置的值
+ /// 给定32位无符号整数的指针
+ static void SetBits(int start_bit, int num_bits, int value, uint* bits)
+ {
+ // Negative numbers must be converted to unsigned so they do not
+ // sign-extend into the rest of the target value. This cast takes
+ // care of both positive and negative cases.
+ uint mask = GetMask(num_bits);
+ uint unsigned_value = (uint)value & mask;
+
+ // Clear any bits that are set, then set the new bits.
+ *bits = (*bits & ~(mask << start_bit)) | (unsigned_value << start_bit);
+ }
+
+ // 计算给定块使用的调制模式
+ ///
+ /// 计算PVRTC块应该使用哪种调制模式(4BPP只支持StandardBilinear,不会计算,直接返回)
+ ///
+ /// 图像指针
+ /// 图像宽度,2的幂
+ /// 图像高度,2的幂
+ /// PVRTC块起始x坐标
+ /// PVRTC块起始y坐标
+ /// 调制模式
+ static ModulationMode_4BPP CalculateBlockModulationMode_4BPP(byte* image_mod, uint width, uint height, uint block_x, uint block_y)
+ {
+ return ModulationMode_4BPP.StandardBilinear;
+ }
+
+ // Works out which modulation mode to use for a given block in an image.
+ // |image_mod| the modulation information for the image.
+ // |width| and |height| image_mod pixel dimensions (must be a power of two).
+ // |block_x| block x coordinate, i.e. ranging from 0 to |width| / kBlockWidth.
+ // |block_y| block y coordinate, i.e. ranging from 0 to |height| / kBlockHeight.
+ ///
+ /// 计算PVRTC块应该使用哪种调制模式
+ ///
+ /// 图像指针
+ /// 图像宽度,2的幂
+ /// 图像高度,2的幂
+ /// PVRTC块起始x坐标
+ /// PVRTC块起始y坐标
+ /// 调制模式
+ static ModulationMode_2BPP CalculateBlockModulationMode_2BPP(byte* image_mod, uint width, uint height, uint block_x, uint block_y)
+ {
+ // A count of how many pixels are best served by modulation values 2 or 3,
+ // i.e. intermediate between the extremes of one color or the other.
+ uint intermediate_value_count = 0;
+
+ // A measure of how much variation between pixels there is horizontally.
+ uint horizontal_count = 0;
+
+ // A measure of how much variation between pixels there is vertically.
+ uint vertical_count = 0;
+
+ for (uint y = 0; y < kBlockHeight_2BPP; y++)
+ {
+ for (uint x = 0; x < kBlockWidth_2BPP; x++)
+ {
+ uint index = (block_y * kBlockHeight_2BPP + y) * width + (block_x * kBlockWidth_2BPP + x);
+
+ if (image_mod[index] == 1 || image_mod[index] == 2)
+ {
+ intermediate_value_count++;
+ }
+
+ // Index of adjacent horizontal pixel in |image_mod|.
+ uint index_adjacent_horizontal = (block_y * kBlockHeight_2BPP + y) * width + ((block_x * kBlockWidth_2BPP + x + 1) / width);
+
+ // Index of adjacent vertical pixel in |image_mod|.
+ uint index_adjacent_vertical = ((block_y * kBlockHeight_2BPP + y + 1) / height) * width + block_x * kBlockWidth_2BPP + x;
+
+ horizontal_count += abs(image_mod[index] - image_mod[index_adjacent_vertical]);
+ vertical_count += abs(image_mod[index] - image_mod[index_adjacent_horizontal]);
+ }
+ }
+
+ if (intermediate_value_count <= 4)
+ {
+ return ModulationMode_2BPP.Direct1BPP;
+ }
+
+ const uint absolute_threshold = 10;
+ const uint ratio_threshold = 2;
+
+ if (vertical_count > absolute_threshold && vertical_count > horizontal_count * ratio_threshold)
+ {
+ return ModulationMode_2BPP.VerticallyInterpolated2BPP;
+ }
+ else if (horizontal_count > absolute_threshold && horizontal_count > vertical_count * ratio_threshold)
+ {
+ return ModulationMode_2BPP.HorizontallyInterpolated2BPP;
+ }
+
+ return ModulationMode_2BPP.Interpolated2BPP;
+ }
+
+ // 计算给定块中的32位无符号整数调制信息
+ ///
+ /// 将给定块中的颜色索引编码为32位无符号整数
+ ///
+ /// 颜色索引的指针
+ /// 图像宽度
+ /// 图像高度
+ /// PVRTC块起始x坐标
+ /// PVRTC块起始y坐标
+ /// 编码的32位无符号整数
+ static uint CalculateBlockModulationData_4BPP(byte* image_mod, uint width, uint height, uint block_x, uint block_y)
+ {
+ uint result = 0;
+ int bitpos = 0;
+ for (uint y = 0; y < 4; y++)
+ {
+ for (uint x = 0; x < 4; x++)
+ {
+ SetBits(bitpos, 2, image_mod[(block_y * 4 + y) * width + block_x * 4 + x], &result);
+ bitpos += 2;
+ }
+ }
+ return result;
+ }
+
+ // Calculates the 32 bits of modulation information to store for a given block
+ // in an image.
+ // |image_mod| the modulation information for the image.
+ // |width| and |height| image_mod pixel dimensions.
+ // |block_x| block x coordinate, i.e. ranging from 0 to |width| / kBlockWidth.
+ // |block_y| block y coordinate, i.e. ranging from 0 to |height| / kBlockHeight.
+ // |mode| which modulation mode to use.
+ ///
+ /// 将给定块中的颜色索引编码为32位无符号整数
+ ///
+ /// 颜色索引的指针
+ /// 图像宽度
+ /// 图像高度
+ /// PVRTC块起始x坐标
+ /// PVRTC块起始y坐标
+ /// 2BPP的PVRTC块调制模式
+ /// 编码的32位无符号整数
+ static uint CalculateBlockModulationData_2BPP(byte* image_mod, uint width, uint height, uint block_x, uint block_y, ModulationMode_2BPP mode)
+ {
+ uint result = 0;
+ int bitpos = 0;
+ for (uint y = 0; y < 4; y++)
+ {
+ for (uint x = 0; x < 8; x++)
+ {
+ uint index = (block_y * 4 + y) * width + (block_x * 8 + x);
+
+ if (mode == ModulationMode_2BPP.Direct1BPP)
+ {
+ int bit = image_mod[index] / 2;
+ SetBits(bitpos, 1, bit, &result);
+ bitpos++;
+ }
+ else
+ {
+ if (((x ^ y) & 1) != 0) continue; // checkerboard
+ int bit = image_mod[index];
+ // The bits at position 0 (0,0) and at position 20 (4,2) are the ones
+ // that use only a single bit for the modulation value, and the other
+ // bit for selecting the sub-mode.
+ if (bitpos == 0)
+ {
+ // The saved bit chooses average-of-4 or "other".
+ if (mode == ModulationMode_2BPP.Interpolated2BPP)
+ bit &= 2;
+ else
+ bit |= 1;
+ }
+ else if (bitpos == 20)
+ {
+ // The saved bit chooses vertical versus horizontal.
+ if (mode == ModulationMode_2BPP.VerticallyInterpolated2BPP)
+ bit |= 1;
+ else
+ bit &= 2;
+ }
+
+ SetBits(bitpos, 2, bit, &result);
+ bitpos += 2;
+ }
+ }
+ }
+ return result;
+ }
+
+ //-----------------------------------------------------------------------------
+
+ //
+ // Helper functions operating on entire images.
+ //
+
+ // 将原图像降频为高频和低频图像
+ ///
+ /// 将原图像降频为高频和低频图像
+ ///
+ /// 图像指针
+ /// 图像宽度,2的幂
+ /// 图像高度,2的幂
+ /// 高频图指针
+ /// 低频图指针
+ static void Morph_4BPP(YFColor* image, uint width, uint height, YFColor* outa, YFColor* outb, bool hasA)
+ {
+ for (uint y = 0; y < height; y += kBlockHeight_4BPP)
+ {
+ for (uint x = 0; x < width; x += kBlockWidth_4BPP)
+ {
+ uint indexa = 0;
+ uint indexb = 0;
+ GetExtremesFast_4BPP(image, width, height, x, y, &indexa, &indexb, hasA);
+
+ uint index_out = y / kBlockHeight_4BPP * (width / kBlockWidth_4BPP) + (x / kBlockWidth_4BPP);
+
+ outa[index_out] = ApplyColorChannelReduction(image[indexa], false, hasA);
+ outb[index_out] = ApplyColorChannelReduction(image[indexb], true, hasA);
+ }
+ }
+ }
+
+ // Fills in the two low-resolution images representing the "a" and "b" colors in
+ // the source |image|.
+ ///
+ /// 将原图像降频为高频和低频图像
+ ///
+ /// 图像指针
+ /// 图像宽度,2的幂
+ /// 图像高度,2的幂
+ /// 高频图指针
+ /// 低频图指针
+ static void Morph_2BPP(YFColor* image, uint width, uint height, YFColor* outa, YFColor* outb, bool hasA)
+ {
+ for (uint y = 0; y < height; y += kBlockHeight_2BPP)
+ {
+ for (uint x = 0; x < width; x += kBlockWidth_2BPP)
+ {
+ uint indexa = 0;
+ uint indexb = 0;
+ GetExtremesFast_2BPP(image, width, height, x, y, &indexa, &indexb, hasA);
+
+ uint index_out = y / kBlockHeight_2BPP * (width / kBlockWidth_2BPP) + (x / kBlockWidth_2BPP);
+
+ outa[index_out] = ApplyColorChannelReduction(image[indexa], false, hasA);
+ outb[index_out] = ApplyColorChannelReduction(image[indexb], true, hasA);
+ }
+ }
+ }
+
+ // 获取图像每个点的索引
+ ///
+ /// 获取每个点在高频低频颜色线性插值生成调色板中最适合的索引
+ ///
+ /// 图像指针
+ /// 图像宽度
+ /// 图像高度
+ /// 高频图指针
+ /// 低频图指针
+ /// 索引指针
+ static void Modulate_4BPP(YFColor* image, uint width, uint height, YFColor* imagea, YFColor* imageb, byte* mod, bool hasA)
+ {
+ for (uint y = 0; y < height; y++)
+ {
+ for (uint x = 0; x < width; x++)
+ {
+ YFColor colora = GetInterpolatedColor4BPP(imagea, width, height, x, y);
+ YFColor colorb = GetInterpolatedColor4BPP(imageb, width, height, x, y);
+ *mod++ = (byte)BestModulation_4BPP(*image++, colora, colorb, ModulationMode_4BPP.StandardBilinear, hasA);
+ }
+ }
+ }
+
+ // Given a source |image| and two low-resolution "a" and "b" images, creates a
+ // 2-bits-per-pixel "mod" image, i.e. values between 0 and 3 for each pixel in
+ // |image|. Each output pixel is stored in a byte in |mod|, which is assumed
+ // to be pre-allocated.
+ ///
+ /// 获取每个点在高频低频颜色线性插值生成调色板中最适合的索引
+ ///
+ /// 图像指针
+ /// 图像宽度
+ /// 图像高度
+ /// 高频图指针
+ /// 低频图指针
+ /// 索引指针
+ static void Modulate_2BPP(YFColor* image, uint width, uint height, YFColor* imagea, YFColor* imageb, byte* mod, bool hasA)
+ {
+ for (uint y = 0; y < height; y++)
+ {
+ for (uint x = 0; x < width; x++)
+ {
+ YFColor colora = GetInterpolatedColor2BPP(imagea, width, height, x, y);
+ YFColor colorb = GetInterpolatedColor2BPP(imageb, width, height, x, y);
+ *mod++ = (byte)BestModulation_2BPP(*image++, colora, colorb, hasA);
+ }
+ }
+ }
+
+ // 最后一步编码并写入
+ ///
+ /// 将所有信息编码并写入PVRTCWord指针
+ ///
+ /// 图像宽度
+ /// 图像高度
+ /// 高频信号指针
+ /// 低频信号指针
+ /// 索引指针
+ /// PVRTCWord指针
+ static void Encode_4BPP(uint width, uint height, YFColor* imagea, YFColor* imageb, byte* image_mod, byte* pvr, bool versionII, bool hasA)
+ {
+ uint times = width * height / (kBlockWidth_4BPP * kBlockHeight_4BPP);
+ // Loop through all output blocks.
+ for (uint i = 0; i < times; i++)
+ {
+ // The blocks are stored in Z-order; calculate the block x and y.
+ uint block_x = 0;
+ uint block_y = 0;
+ FromZOrder(i, &block_x, &block_y, width / kBlockWidth_4BPP, height / kBlockHeight_4BPP, versionII);
+
+ // Calculate which kind of encoding is worth doing for this block.
+ ModulationMode_4BPP mode = CalculateBlockModulationMode_4BPP(image_mod, width, height, block_x, block_y);
+
+ // Given this mode, calculate the 32 bits that represent the block's
+ // modulation information.
+ uint mod_data = CalculateBlockModulationData_4BPP(image_mod, width, height, block_x, block_y);
+
+ // The 32 bits that represent the 2-color palette for this block and mode.
+ uint color_data = EncodeColors_4BPP(imagea[block_y * (width / kBlockWidth_4BPP) + block_x], imageb[block_y * (width / kBlockWidth_4BPP) + block_x], mode, versionII, hasA);
+
+ // Write out this information.
+
+ pvr = Append32(mod_data, pvr);
+ pvr = Append32(color_data, pvr);
+ }
+ }
+
+ // Takes the calculated "A" and "B" images, and the modulation information, and
+ // writes out the data in PVRTC format. Though the input modulation information
+ // has 2 bits per pixel of the uncompressed original image, this function will
+ // choose on a per-block basis which of the 4 modulation modes to use.
+ // |width| and |height| image dimensions.
+ // |imagea| the "A" image as described in pvrtc_compressor.h.
+ // |imageb| the "B" image as also described.
+ // |imagemod| One byte per pixel modulation data, containing 2-bit values.
+ // |pvr| output pvrtc data, assumed preallocated.
+ ///
+ /// 将所有信息编码并写入PVRTCWord指针
+ ///
+ /// 图像宽度
+ /// 图像高度
+ /// 高频信号指针
+ /// 低频信号指针
+ /// 索引指针
+ /// PVRTCWord指针
+ static void Encode_2BPP(uint width, uint height, YFColor* imagea, YFColor* imageb, byte* image_mod, byte* pvr, bool versionII, bool hasA)
+ {
+ uint times = width * height / (kBlockWidth_2BPP * kBlockHeight_2BPP);
+ // Loop through all output blocks.
+ for (uint i = 0; i < times; i++)
+ {
+ // The blocks are stored in Z-order; calculate the block x and y.
+ uint block_x = 0;
+ uint block_y = 0;
+ FromZOrder(i, &block_x, &block_y, width / kBlockWidth_2BPP, height / kBlockHeight_2BPP, versionII);
+
+ // Calculate which kind of encoding is worth doing for this block.
+ ModulationMode_2BPP mode = CalculateBlockModulationMode_2BPP(image_mod, width, height,
+ block_x, block_y);
+
+ // Given this mode, calculate the 32 bits that represent the block's
+ // modulation information.
+ uint mod_data = CalculateBlockModulationData_2BPP(image_mod, width, height,
+ block_x, block_y, mode);
+
+ // The 32 bits that represent the 2-color palette for this block and mode.
+ uint color_data = EncodeColors_2BPP(
+ imagea[block_y * (width / kBlockWidth_2BPP) + block_x],
+ imageb[block_y * (width / kBlockWidth_2BPP) + block_x], mode, versionII, hasA);
+
+ // Write out this information.
+ pvr = Append32(mod_data, pvr);
+ pvr = Append32(color_data, pvr);
+ }
+ }
+
+ public static void CompressPVRTC(byte* pvr, YFColor* image, uint width, uint height, byte bpp, bool versionII, bool hasA)
+ {
+ if (bpp == 2)
+ {
+ uint low_image_size = (width * height) / (kBlockWidth_2BPP * kBlockHeight_2BPP);
+ int alloc_mem = (int)(2 * low_image_size * sizeof(YFColor) + width * height);
+ using (IMemoryOwner owner = MemoryPool.Shared.Rent(alloc_mem))
+ {
+ Span owner_span = owner.Memory.Span[..alloc_mem];
+ fixed (byte* owner_ptr = owner_span)
+ {
+ nuint owner_ptr_num = (nuint)owner_ptr;
+ YFColor* iap = (YFColor*)owner_ptr_num;
+ owner_ptr_num += low_image_size * (uint)sizeof(YFColor);
+ YFColor* ibp = (YFColor*)owner_ptr_num;
+ owner_ptr_num += low_image_size * (uint)sizeof(YFColor);
+ byte* im = (byte*)owner_ptr_num;
+ Morph_2BPP(image, width, height, iap, ibp, hasA);
+ Modulate_2BPP(image, width, height, iap, ibp, im, hasA);
+ Encode_2BPP(width, height, iap, ibp, im, pvr, versionII, hasA);
+ }
+ }
+ }
+ else if (bpp == 4)
+ {
+ uint low_image_size = (width * height) / (kBlockWidth_4BPP * kBlockHeight_4BPP);
+ int alloc_mem = (int)(2 * low_image_size * sizeof(YFColor) + width * height);
+ using (IMemoryOwner owner = MemoryPool.Shared.Rent(alloc_mem))
+ {
+ Span owner_span = owner.Memory.Span[..alloc_mem];
+ fixed (byte* owner_ptr = owner_span)
+ {
+ nuint owner_ptr_num = (nuint)owner_ptr;
+ YFColor* iap = (YFColor*)owner_ptr_num;
+ owner_ptr_num += low_image_size * (uint)sizeof(YFColor);
+ YFColor* ibp = (YFColor*)owner_ptr_num;
+ owner_ptr_num += low_image_size * (uint)sizeof(YFColor);
+ byte* im = (byte*)owner_ptr_num;
+ Morph_4BPP(image, width, height, iap, ibp, hasA);
+ Modulate_4BPP(image, width, height, iap, ibp, im, hasA);
+ Encode_4BPP(width, height, iap, ibp, im, pvr, versionII, hasA);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Shared/PVRTexLibHelper.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Shared/PVRTexLibHelper.cs
new file mode 100644
index 0000000..788de81
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/Shared/PVRTexLibHelper.cs
@@ -0,0 +1,135 @@
+// using LibWindPop.Utils.Graphics.Bitmap;
+// using PVRTexLib;
+// using System;
+// using System.Runtime.InteropServices;
+
+// namespace LibWindPop.Utils.Graphics.Texture.Shared
+// {
+// internal class PVRTexLibHelper
+// {
+// public static bool Encode(RefBitmap srcBitmap, PVRTexLibPixelFormat inFormat, ReadOnlySpan dstData)
+// {
+// if (!TextureSettings.UsePVRTexLib)
+// {
+// return false;
+// }
+// using (PVRTextureHeader header = new PVRTextureHeader(PVRDefine.PVRTGENPIXELID4('b', 'g', 'r', 'a', 8, 8, 8, 8), (uint)srcBitmap.Width, (uint)srcBitmap.Height, 1, 1, 1, 1, PVRTexLibColourSpace.sRGB, PVRTexLibVariableType.UnsignedByteNorm, false))
+// {
+// unsafe
+// {
+// fixed (byte* dstTexDataPtr = &dstData[0])
+// {
+// fixed (YFColor* bitmapDataPtr = &srcBitmap.AsSpan()[0])
+// {
+// using (PVRTexture tex = new PVRTexture(header, bitmapDataPtr))
+// {
+// if (tex.GetTextureDataSize() != 0)
+// {
+// if (tex.Transcode((ulong)inFormat, PVRTexLibVariableType.UnsignedByteNorm, PVRTexLibColourSpace.sRGB, GetQuality(inFormat), GetDither()))
+// {
+// NativeMemory.Copy(tex.GetTextureDataPointer(0), dstTexDataPtr, (nuint)tex.GetTextureDataSize(0));
+// }
+// }
+// }
+// }
+// }
+// }
+// }
+// return true;
+// }
+
+// public static bool Decode(ReadOnlySpan srcData, PVRTexLibPixelFormat inFormat, RefBitmap dstBitmap)
+// {
+// if (!TextureSettings.UsePVRTexLib)
+// {
+// return false;
+// }
+// using (PVRTextureHeader header = new PVRTextureHeader((ulong)inFormat, (uint)dstBitmap.Width, (uint)dstBitmap.Height, 1, 1, 1, 1, PVRTexLibColourSpace.sRGB, PVRTexLibVariableType.UnsignedByteNorm, false))
+// {
+// unsafe
+// {
+// fixed (byte* rawTexDataPtr = &srcData[0])
+// {
+// fixed (YFColor* bitmapDataPtr = &dstBitmap.AsSpan()[0])
+// {
+// using (PVRTexture tex = new PVRTexture(header, rawTexDataPtr))
+// {
+// if (tex.GetTextureDataSize() != 0)
+// {
+// if (tex.Transcode(PVRDefine.PVRTGENPIXELID4('b', 'g', 'r', 'a', 8, 8, 8, 8), PVRTexLibVariableType.UnsignedByteNorm, PVRTexLibColourSpace.sRGB, 0, GetDither()))
+// {
+// NativeMemory.Copy(tex.GetTextureDataPointer(0), bitmapDataPtr, (nuint)tex.GetTextureDataSize(0));
+// }
+// }
+// }
+// }
+// }
+// }
+// }
+// return true;
+// }
+
+// public static bool GetDither()
+// {
+// return TextureSettings.PVRTexLibDoDither;
+// }
+
+// public static PVRTexLibCompressorQuality GetQuality(PVRTexLibPixelFormat inFormat)
+// {
+// PVRTexLibCompressorQuality quality = 0;
+// if (inFormat == PVRTexLibPixelFormat.PVRTCI_2bpp_RGB
+// || inFormat == PVRTexLibPixelFormat.PVRTCI_4bpp_RGB
+// || inFormat == PVRTexLibPixelFormat.PVRTCI_2bpp_RGBA
+// || inFormat == PVRTexLibPixelFormat.PVRTCI_4bpp_RGBA
+// || inFormat == PVRTexLibPixelFormat.PVRTCII_2bpp
+// || inFormat == PVRTexLibPixelFormat.PVRTCII_4bpp
+// || inFormat == PVRTexLibPixelFormat.PVRTCI_HDR_6bpp
+// || inFormat == PVRTexLibPixelFormat.PVRTCI_HDR_8bpp
+// || inFormat == PVRTexLibPixelFormat.PVRTCII_HDR_6bpp
+// || inFormat == PVRTexLibPixelFormat.PVRTCII_HDR_8bpp)
+// {
+// if (TextureSettings.PVRTexLibQuality == TextureSettings.PVRTexLibTextureQuality.Fast)
+// {
+// quality = PVRTexLibCompressorQuality.PVRTCFastest;
+// }
+// else if (TextureSettings.PVRTexLibQuality == TextureSettings.PVRTexLibTextureQuality.Normal)
+// {
+// quality = PVRTexLibCompressorQuality.PVRTCNormal;
+// }
+// else if (TextureSettings.PVRTexLibQuality == TextureSettings.PVRTexLibTextureQuality.High)
+// {
+// quality = PVRTexLibCompressorQuality.PVRTCVeryHigh;
+// }
+// else if (TextureSettings.PVRTexLibQuality == TextureSettings.PVRTexLibTextureQuality.Best)
+// {
+// quality = PVRTexLibCompressorQuality.PVRTCBest;
+// }
+// }
+// else if (inFormat == PVRTexLibPixelFormat.ETC1
+// || inFormat == PVRTexLibPixelFormat.ETC2_RGB
+// || inFormat == PVRTexLibPixelFormat.ETC2_RGBA
+// || inFormat == PVRTexLibPixelFormat.ETC2_RGB_A1
+// || inFormat == PVRTexLibPixelFormat.EAC_R11
+// || inFormat == PVRTexLibPixelFormat.EAC_RG11)
+// {
+// if (TextureSettings.PVRTexLibQuality == TextureSettings.PVRTexLibTextureQuality.Fast)
+// {
+// quality = PVRTexLibCompressorQuality.ETCFast;
+// }
+// else if (TextureSettings.PVRTexLibQuality == TextureSettings.PVRTexLibTextureQuality.Normal)
+// {
+// quality = PVRTexLibCompressorQuality.ETCNormal;
+// }
+// else if (TextureSettings.PVRTexLibQuality == TextureSettings.PVRTexLibTextureQuality.High)
+// {
+// quality = PVRTexLibCompressorQuality.ETCSlow;
+// }
+// else if (TextureSettings.PVRTexLibQuality == TextureSettings.PVRTexLibTextureQuality.Best)
+// {
+// quality = PVRTexLibCompressorQuality.ETCSlow;
+// }
+// }
+// return quality;
+// }
+// }
+// }
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/TextureCoder.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/TextureCoder.cs
new file mode 100644
index 0000000..f292b0d
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/TextureCoder.cs
@@ -0,0 +1,32 @@
+using LibWindPop.Utils.Graphics.Bitmap;
+using System;
+
+namespace LibWindPop.Utils.Graphics.Texture
+{
+ public static class TextureCoder
+ {
+ public static void Decode(ReadOnlySpan src_data, int width, int height, int pitch, RefBitmap dst_bitmap)
+ where TCoder : IPitchableTextureCoder, new()
+ {
+ new TCoder().Decode(src_data, width, height, pitch, dst_bitmap);
+ }
+
+ public static void Decode(ReadOnlySpan src_data, int width, int height, RefBitmap dst_bitmap)
+ where TCoder : ITextureCoder, new()
+ {
+ new TCoder().Decode(src_data, width, height, dst_bitmap);
+ }
+
+ public static void Encode(RefBitmap src_bitmap, Span dst_data, int width, int height, int pitch)
+ where TCoder : IPitchableTextureCoder, new()
+ {
+ new TCoder().Encode(src_bitmap, dst_data, width, height, pitch);
+ }
+
+ public static void Encode(RefBitmap src_bitmap, Span dst_data, int width, int height)
+ where TCoder : ITextureCoder, new()
+ {
+ new TCoder().Encode(src_bitmap, dst_data, width, height);
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/TextureSettings.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/TextureSettings.cs
new file mode 100644
index 0000000..2c68824
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/Texture/TextureSettings.cs
@@ -0,0 +1,17 @@
+namespace LibWindPop.Utils.Graphics.Texture
+{
+ public static class TextureSettings
+ {
+ public static bool UsePVRTexLib { get; set; } = true;
+ public static bool PVRTexLibDoDither { get; set; } = false;
+ public static PVRTexLibTextureQuality PVRTexLibQuality { get; set; } = PVRTexLibTextureQuality.Best;
+
+ public enum PVRTexLibTextureQuality
+ {
+ Fast = 0,
+ Normal = 1,
+ High = 2,
+ Best = 3,
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/ThrowHelper.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/ThrowHelper.cs
new file mode 100644
index 0000000..581fbd8
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/ThrowHelper.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace LibWindPop.Utils
+{
+ internal static class ThrowHelper
+ {
+ public static void ThrowWhen(bool value)
+ {
+ if (value)
+ {
+ throw new Exception();
+ }
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/YFColor.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/YFColor.cs
new file mode 100644
index 0000000..4c2c639
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/Decoder/YFColor.cs
@@ -0,0 +1,104 @@
+using System.Runtime.InteropServices;
+
+namespace LibWindPop.Utils.Graphics
+{
+ [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 0x4)]
+ public unsafe struct YFColor
+ {
+ public static YFColor Transparent => new YFColor(0x0, 0x0, 0x0, 0x0);
+
+ public static YFColor Black => new YFColor(0x0, 0x0, 0x0, 0xFF);
+
+ public const int BLUE = 0;
+ public const int GREEN = 1;
+ public const int RED = 2;
+ public const int ALPHA = 3;
+
+ public byte this[int channel]
+ {
+ get => channel switch
+ {
+ BLUE => Blue,
+ GREEN => Green,
+ RED => Red,
+ ALPHA => Alpha,
+ _ => 0
+ };
+ set
+ {
+ switch (channel)
+ {
+ case BLUE: Blue = value; break;
+ case GREEN: Green = value; break;
+ case RED: Green = value; break;
+ case ALPHA: Alpha = value; break;
+ }
+ }
+ }
+
+ [FieldOffset(0x0)]
+ public byte Blue;
+
+ [FieldOffset(0x1)]
+ public byte Green;
+
+ [FieldOffset(0x2)]
+ public byte Red;
+
+ [FieldOffset(0x3)]
+ public byte Alpha;
+
+ public YFColor(byte red, byte green, byte blue, byte alpha)
+ {
+ Red = red;
+ Green = green;
+ Blue = blue;
+ Alpha = alpha;
+ }
+
+ public YFColor(byte red, byte green, byte blue)
+ {
+ Red = red;
+ Green = green;
+ Blue = blue;
+ Alpha = 0xFF;
+ }
+
+ public override string ToString()
+ {
+ return $"#{Alpha:x2}{Red:x2}{Green:x2}{Blue:x2}";
+ }
+
+ public static explicit operator YFColor(uint color)
+ {
+ return *(YFColor*)&color;
+ }
+
+ public static explicit operator uint(YFColor color)
+ {
+ return *(uint*)&color;
+ }
+
+ public void SwapRedBlue()
+ {
+ (Red, Blue) = (Blue, Red);
+ }
+
+ public void PremultiplyAlpha()
+ {
+ Red = (byte)(Red * Alpha / 255);
+ Green = (byte)(Green * Alpha / 255);
+ Blue = (byte)(Blue * Alpha / 255);
+ }
+
+ public void RepremultiplyAlpha()
+ {
+ if (Alpha != 0)
+ {
+ Red = (byte)(Red * 255 / Alpha);
+ Green = (byte)(Green * 255 / Alpha);
+ Blue = (byte)(Blue * 255 / Alpha);
+ }
+ }
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Graphics/ImageProcessing/NormalMapConvert.cs b/TelltaleTextureTool/TelltaleTextureTool/Graphics/ImageProcessing/NormalMapConvert.cs
index 117840e..d60fbf4 100644
--- a/TelltaleTextureTool/TelltaleTextureTool/Graphics/ImageProcessing/NormalMapConvert.cs
+++ b/TelltaleTextureTool/TelltaleTextureTool/Graphics/ImageProcessing/NormalMapConvert.cs
@@ -111,12 +111,12 @@ private static IImageEncoder GetEncoder(string fileType)
{
return fileType switch
{
- ".jpeg" => new JpegEncoder(),
- ".jpg" => new JpegEncoder(),
- ".png" => new PngEncoder(),
+ ".jpeg" => new JpegEncoder(), // Supported
+ ".jpg" => new JpegEncoder(), // Supported
+ ".png" => new PngEncoder(), // Supported
".tif" => new TiffEncoder(),
".tiff" => new TiffEncoder(),
- ".bmp" => new BmpEncoder(),
+ ".bmp" => new BmpEncoder(), // Supported
".tga" => new TgaEncoder(), // For the future
_ => throw new ArgumentException("File type is not valid!")
};
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Main/D3DTX_Master.cs b/TelltaleTextureTool/TelltaleTextureTool/Main/D3DTX_Master.cs
index a119c86..f83a330 100644
--- a/TelltaleTextureTool/TelltaleTextureTool/Main/D3DTX_Master.cs
+++ b/TelltaleTextureTool/TelltaleTextureTool/Main/D3DTX_Master.cs
@@ -14,6 +14,9 @@
using Decoders;
using TelltaleTextureTool.Telltale.FileTypes.D3DTX;
using TelltaleTextureTool.Telltale.Meta;
+using LibWindPop.Utils.Graphics.Texture.Coder;
+using LibWindPop.Utils.Graphics.Bitmap;
+using LibWindPop.Utils.Graphics.Texture;
namespace TelltaleTextureTool.Main
{
@@ -319,7 +322,7 @@ public D3DTXVersion TryToInitializeLegacyD3DTX(BinaryReader reader)
///
public void WriteFinalD3DTX(string destinationPath)
{
- using BinaryWriter writer = new(File.OpenWrite(destinationPath));
+ using BinaryWriter writer = new(File.Create(destinationPath));
metaHeaderObject.WriteToBinary(writer);
d3dtxObject.WriteToBinary(writer);
@@ -685,8 +688,12 @@ public byte[] DecodePixelDataByPlatform(byte[] pixelData, T3SurfaceFormat surfac
public byte[] DecodePixelDataByFormat(byte[] pixelData, T3SurfaceFormat surfaceFormat, int width, int height, T3PlatformType platformType)
{
+ IPitchableTextureCoder coder = null;
+
if (surfaceFormat == T3SurfaceFormat.eSurface_PVRTC4 || surfaceFormat == T3SurfaceFormat.eSurface_PVRTC4a)
{
+ // coder = new RGBA_PVRTCI_2BPP_UByte();
+
PvrtcDecoder pvrtcDecoder = new PvrtcDecoder();
pixelData = pvrtcDecoder.DecompressPVRTC(pixelData, width, height, false);
}
@@ -697,13 +704,65 @@ public byte[] DecodePixelDataByFormat(byte[] pixelData, T3SurfaceFormat surfaceF
}
else if (surfaceFormat == T3SurfaceFormat.eSurface_ATC_RGB)
{
- AtcDecoder atcDecoder = new AtcDecoder();
- pixelData = atcDecoder.DecompressAtcRgb4(pixelData, width, height);
+ coder = new RGB_ATC_UByte();
+ // AtcDecoder atcDecoder = new AtcDecoder();
+ // pixelData = atcDecoder.DecompressAtcRgb4(pixelData, width, height);
+ // int bytesRead = AssetRipper.TextureDecoder.Atc.AtcDecoder.DecompressAtcRgb4(pixelData, width, height, out byte[] decodedData);
+ // pixelData = decodedData;
}
- else if (surfaceFormat == T3SurfaceFormat.eSurface_ATC_RGBA || surfaceFormat == T3SurfaceFormat.eSurface_ATC_RGB1A)
+ else if (surfaceFormat == T3SurfaceFormat.eSurface_ATC_RGBA)
{
- AtcDecoder atcDecoder = new AtcDecoder();
- pixelData = atcDecoder.DecompressAtcRgba8(pixelData, width, height);
+ coder = new RGBA_ATC_Interpolated_UByte();
+ // AtcDecoder atcDecoder = new AtcDecoder();
+ // int bytesRead = AssetRipper.TextureDecoder.Atc.AtcDecoder.DecompressAtcRgba8(pixelData, width, height, out byte[] decodedData);
+ // pixelData = atcDecoder.DecompressAtcRgba8(pixelData, width, height);
+ // pixelData = decodedData;
+ }
+ else if (surfaceFormat == T3SurfaceFormat.eSurface_ATC_RGB1A)
+ {
+ coder = new RGBA_ATC_Explicit_UByte();
+ }
+
+ using (NativeBitmap bitmap = new NativeBitmap(width, height))
+ {
+ RefBitmap refBitmap = bitmap.AsRefBitmap();
+ //RefBitmap refBitmap1 = new RefBitmap(refBitmap, 0, 0, width, height);
+ ReadOnlySpan dataSpan = pixelData.AsSpan();
+ byte[] newPixelData = new byte[refBitmap.Area * 4];
+
+ if (coder != null)
+ {
+ coder.Decode(dataSpan, width, height, refBitmap);
+ int j = 0;
+
+ if (T3SurfaceFormat.eSurface_ATC_RGB1A != surfaceFormat)
+ {
+ for (int i = 0; i < refBitmap.Area; i++)
+ {
+ newPixelData[j] = refBitmap.Data[i].Red;
+ newPixelData[j + 1] = refBitmap.Data[i].Green;
+ newPixelData[j + 2] = refBitmap.Data[i].Blue;
+ newPixelData[j + 3] = refBitmap.Data[i].Alpha;
+
+ j += 4;
+ }
+ }
+ else
+ {
+ for (int i = 0; i < refBitmap.Area; i++)
+ {
+ newPixelData[j] = refBitmap.Data[i].Alpha;
+ newPixelData[j + 1] = refBitmap.Data[i].Red;
+ newPixelData[j + 2] = refBitmap.Data[i].Green;
+ newPixelData[j + 3] = refBitmap.Data[i].Blue;
+
+ j += 4;
+ }
+ }
+
+
+ pixelData = newPixelData;
+ }
}
return pixelData;
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Enums/T3PlatformType.cs b/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Enums/T3PlatformType.cs
index 5ecb69d..29bf640 100644
--- a/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Enums/T3PlatformType.cs
+++ b/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Enums/T3PlatformType.cs
@@ -5,17 +5,24 @@ public enum T3PlatformType
ePlatform_All = 1,
ePlatform_PC = 2,
ePlatform_Wii = 3,
+ // Requires swizzling
ePlatform_Xbox = 4,
+ // Requires swizzling
ePlatform_PS3 = 5,
ePlatform_Mac = 6,
+ // R and B channels are swapped
ePlatform_iPhone = 7,
ePlatform_Android = 8,
ePlatform_Vita = 9,
ePlatform_Linux = 10,
+ // Requires swizzling
ePlatform_PS4 = 11,
+ // Requires swizzling
ePlatform_XBOne = 12,
+ // Requires swizzling
ePlatform_WiiU = 13,
ePlatform_Win10 = 14,
+ // Probably requires swizzling
ePlatform_NX = 15,
ePlatform_Count = 16,
}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Enums/T3SurfaceFormat.cs b/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Enums/T3SurfaceFormat.cs
index b4b9260..afd221d 100644
--- a/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Enums/T3SurfaceFormat.cs
+++ b/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Enums/T3SurfaceFormat.cs
@@ -141,7 +141,7 @@ public enum T3SurfaceFormat
#endregion
- #region Texture Compression Formats
+ #region S3TC Formats
// Equivalent to DXGI_FORMAT_BC1_UNORM or Direct3D9 D3DFMT_DXT1
// In Telltale Tool it is named eSurface_DXT1
@@ -179,21 +179,9 @@ public enum T3SurfaceFormat
// Equivalent to DXGI_FORMAT_BC7_UNORM
eSurface_BC7 = 0x47,
- // The following formats are used for iOS/Android platforms only, which are not supported:
- // PVRTC2
- // PVRTC4
- // PVRTC2a
- // PVRTC4a
- // ATC_RGB
- // ATC_RGB1A
- // ATC_RGBA
- // ETC1_RGB
- // ETC2_RGB
- // ETC2_RGB1A
- // ETC2_RGBA
- // ETC2_R
- // ETC2_RG
- // ATSC_RGBA_4x4
+ #endregion
+
+ #region Mobile Formats
// PVRTC1 2bpp RGB
eSurface_PVRTC2 = 0x50, //50h
@@ -206,9 +194,9 @@ public enum T3SurfaceFormat
// PVRTC 1 or 2 4bpp RGBA
eSurface_PVRTC4a = 0x53, //53h
- eSurface_ATC_RGB = 0x60, //60h
- eSurface_ATC_RGB1A = 0x61, //61h
- eSurface_ATC_RGBA = 0x62, //62h
+ eSurface_ATC_RGB = 0x60, //60h ATC
+ eSurface_ATC_RGB1A = 0x61, //61h ATC Explicit Alpha
+ eSurface_ATC_RGBA = 0x62, //62h ATC Interpolated Alpha
eSurface_ETC1_RGB = 0x70, //70h
// VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK = 148,
@@ -224,8 +212,6 @@ public enum T3SurfaceFormat
// ETC2 EAC RG11
eSurface_ETC2_RG = 0x75, //75h
- // Presumably eSurface_ETC2_RGBM exists, but it is not used.
-
eSurface_ATSC_RGBA_4x4 = 0x80, //80h
#endregion
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Versions/D3DTX_CLV13.cs b/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Versions/D3DTX_CLV13.cs
new file mode 100644
index 0000000..d8a7924
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Versions/D3DTX_CLV13.cs
@@ -0,0 +1,397 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using TelltaleTextureTool.TelltaleEnums;
+using TelltaleTextureTool.TelltaleTypes;
+using TelltaleTextureTool.Utilities;
+using TelltaleTextureTool.DirectX;
+using TelltaleTextureTool.DirectX.Enums;
+using TelltaleTextureTool.Telltale.FileTypes.D3DTX;
+using System.Linq;
+
+/*
+ * NOTE:
+ *
+ * This version of D3DTX is COMPLETE.
+ *
+ * COMPLETE meaning that all of the data is known and getting identified.
+ * Just like the versions before and after, this D3DTX version derives from version 9 and has been 'stripped' or adjusted to suit this version of D3DTX.
+ * Also, Telltale uses Hungarian Notation for variable naming.
+*/
+
+/* - D3DTX Old Unknown Version games
+ * Telltale Texas Hold'em (UNTESTED)
+ * Bone: Out from Boneville (UNTESTED)
+ * CSI: 3 Dimensions of Murder (UNTESTED)
+ * Bone: The Great Cow Race (UNTESTED)
+ * Sam & Max Save the World (UNTESTED)
+ * CSI: Hard Evidence (UNTESTED)
+*/
+
+/* - D3DTX Legacy Version 9 games
+ * Sam & Max Beyond Time and Space (TESTED)
+*/
+
+namespace TelltaleTextureTool.TelltaleD3DTX;
+
+///
+/// This is a custom class that matches what is serialized in a legacy D3DTX version supporting the listed titles. (COMPLETE)
+///
+public class D3DTX_CLV13 : ID3DTX
+{
+ ///
+ /// [4 bytes] The mName block size in bytes.
+ ///
+ public int mName_BlockSize { get; set; }
+
+ ///
+ /// [mName_StringLength bytes] The string mName.
+ ///
+ public string mName { get; set; } = string.Empty;
+
+ ///
+ /// [4 bytes] The mImportName block size in bytes.
+ ///
+ public int mImportName_BlockSize { get; set; }
+
+ ///
+ /// [mImportName_StringLength bytes] The mImportName string.
+ ///
+ public string mImportName { get; set; } = string.Empty;
+
+ ///
+ /// [1 byte] Indicates whether or not the texture contains mips. (what? need further research)
+ ///
+ public TelltaleBoolean mbHasTextureData { get; set; }
+
+ ///
+ /// [1 byte] Indicates whether or not the texture contains mips.
+ ///
+ public TelltaleBoolean mbIsMipMapped { get; set; }
+
+ ///
+ /// [1 byte] Indicates whether or not the texture contains mips.
+ ///
+ public TelltaleBoolean mbIsWrapU { get; set; }
+
+ ///
+ /// [1 byte] Indicates whether or not the texture contains mips.
+ ///
+ public TelltaleBoolean mbIsWrapV { get; set; }
+
+ ///
+ /// [1 byte] Indicates whether or not the texture contains mips.
+ ///
+ public TelltaleBoolean mbIsFiltered { get; set; }
+
+ ///
+ /// [1 byte] No idea.
+ ///
+ public TelltaleBoolean mbEmbedMipMaps { get; set; }
+
+ ///
+ /// [4 bytes] Number of mips in the texture.
+ ///
+ public uint mNumMipLevels { get; set; }
+
+ ///
+ /// [4 bytes] The old T3SurfaceFormat. Makes use of FourCC but it can be an integer as well. Enums could not be found.
+ ///
+ public D3DFormat mD3DFormat { get; set; }
+
+ ///
+ /// [4 bytes] The pixel width of the texture.
+ ///
+ public uint mWidth { get; set; }
+
+ ///
+ /// [4 bytes] The pixel height of the texture.
+ ///
+ public uint mHeight { get; set; }
+
+ ///
+ /// [4 bytes] The pixel width of the texture when loaded on Wii platform.
+ ///
+ public uint mWiiForceWidth { get; set; }
+
+ ///
+ /// [4 bytes] The pixel height of the texture when loaded on Wii platform.
+ ///
+ public uint mWiiForceHeight { get; set; }
+
+ ///
+ /// [1 byte] Whether or not the texture is forced to compressed when on.
+ ///
+ public TelltaleBoolean mbWiiForceUncompressed { get; set; }
+
+ ///
+ /// [4 bytes] The texture data format. No enums were found, need more analyzing. Could be a flag.
+ ///
+ public uint mTextureDataFormats { get; set; }
+
+ ///
+ /// [4 bytes] The texture data size (tpl?).
+ ///
+ public uint mTplTextureDataSize { get; set; }
+
+ ///
+ /// [4 bytes] An enum, defines what kind of alpha the texture will have.
+ ///
+ public T3TextureAlphaMode mAlphaMode { get; set; }
+
+ ///
+ /// [4 bytes] The Wii texture format.
+ ///
+ public WiiTextureFormat mWiiTextureFormat { get; set; }
+
+ ///
+ /// [1 byte] Whether or not the texture encrypted.
+ ///
+ public TelltaleBoolean mbEncrypted { get; set; }
+
+ ///
+ /// [1 byte] Whether or not the texture has alpha HDR?
+ ///
+ public TelltaleBoolean mbAlphaHDR { get; set; }
+
+ ///
+ /// [1 byte] Whether or not the texture has alpha HDR?
+ ///
+ public TelltaleBoolean mbUsedAsBumpmap { get; set; }
+
+ ///
+ /// [1 byte] Whether or not the texture has alpha HDR?
+ ///
+ public TelltaleBoolean mbUsedAsDetailMap { get; set; }
+
+ ///
+ /// [4 bytes] Map brightness for the Detail map type.
+ ///
+ public float mDetailMapBrightness { get; set; }
+
+ ///
+ /// [4 bytes] Legacy console editions. It should be always zero.
+ ///
+ public int mEmptyBuffer { get; set; }
+
+ ///
+ /// A byte array of the pixel regions in a texture.
+ ///
+ public List mPixelData { get; set; } = [];
+
+ public D3DTX_CLV13() { }
+
+ public void WriteToBinary(BinaryWriter writer, bool printDebug = false)
+ {
+ writer.Write(mName_BlockSize); //mName Block Size [4 bytes] //mName block size (size + string len)
+ ByteFunctions.WriteString(writer, mName); //mName [x bytes]
+ writer.Write(mImportName_BlockSize); //mImportName Block Size [4 bytes] //mImportName block size (size + string len)
+ ByteFunctions.WriteString(writer, mImportName); //mImportName [x bytes] (this is always 0)
+ ByteFunctions.WriteBoolean(writer, mbHasTextureData.mbTelltaleBoolean); //mbHasTextureData [1 byte]
+ ByteFunctions.WriteBoolean(writer, mbIsMipMapped.mbTelltaleBoolean); //mbIsMipMapped [1 byte]
+ ByteFunctions.WriteBoolean(writer, mbIsWrapU.mbTelltaleBoolean); //mbEmbedMipMaps [1 byte]
+ ByteFunctions.WriteBoolean(writer, mbIsWrapV.mbTelltaleBoolean); //mbEmbedMipMaps [1 byte]
+ ByteFunctions.WriteBoolean(writer, mbIsFiltered.mbTelltaleBoolean); //mbEmbedMipMaps [1 byte]
+ ByteFunctions.WriteBoolean(writer, mbEmbedMipMaps.mbTelltaleBoolean); //mbEmbedMipMaps [1 byte]
+ writer.Write(mNumMipLevels); //mNumMipLevels [4 bytes]
+ writer.Write((int)mD3DFormat); //mD3DFormat [4 bytes]
+ writer.Write(mWidth); //mWidth [4 bytes]
+ writer.Write(mHeight); //mHeight [4 bytes]
+ writer.Write(mWiiForceWidth); //mWiiForceWidth [4 bytes]
+ writer.Write(mWiiForceHeight); //mWiiForceHeight [4 bytes]
+ ByteFunctions.WriteBoolean(writer, mbWiiForceUncompressed.mbTelltaleBoolean); //mbWiiForceUncompressed [1 byte]
+ writer.Write(mTextureDataFormats); //mTextureDataFormats [4 bytes]
+ writer.Write(mTplTextureDataSize); //mTplTextureDataSize [4 bytes]
+ writer.Write((int)mAlphaMode); //mAlphaMode [4 bytes]
+ writer.Write((int)mWiiTextureFormat); //mWiiTextureFormat [4 bytes]
+ ByteFunctions.WriteBoolean(writer, mbAlphaHDR.mbTelltaleBoolean); //mbAlphaHDR [1 byte]
+ ByteFunctions.WriteBoolean(writer, mbEncrypted.mbTelltaleBoolean); //mbEncrypted [1 byte]
+ writer.Write(mDetailMapBrightness); //mDetailMapBrightness [4 bytes]
+ writer.Write(mEmptyBuffer); //mEmptyBuffer [4 bytes]
+
+ for (int i = 0; i < mPixelData.Count; i++) //DDS file including header [mTextureDataSize bytes]
+ {
+ mPixelData[i].WriteBinaryData(writer);
+ }
+ }
+
+ public void ReadFromBinary(BinaryReader reader, bool printDebug = false)
+ {
+ bool read = true;
+ bool isValid = true;
+
+ while (read && isValid)
+ {
+ isValid = true;
+ mName_BlockSize = reader.ReadInt32();
+ mName = ByteFunctions.ReadString(reader);
+ mImportName_BlockSize = reader.ReadInt32();
+ mImportName = ByteFunctions.ReadString(reader);
+ mbHasTextureData = new TelltaleBoolean(reader);
+ mbIsMipMapped = new TelltaleBoolean(reader);
+ mbIsWrapU = new TelltaleBoolean(reader);
+ mbIsWrapV = new TelltaleBoolean(reader);
+ mbIsFiltered = new TelltaleBoolean(reader);
+ mbEmbedMipMaps = new TelltaleBoolean(reader);
+
+ mNumMipLevels = reader.ReadUInt32();
+ mD3DFormat = (D3DFormat)reader.ReadUInt32();
+ mWidth = reader.ReadUInt32();
+ mHeight = reader.ReadUInt32();
+ mWiiForceWidth = reader.ReadUInt32();
+ mWiiForceHeight = reader.ReadUInt32();
+ mbWiiForceUncompressed = new TelltaleBoolean(reader);
+ mTextureDataFormats = reader.ReadUInt32();
+ mTplTextureDataSize = reader.ReadUInt32();
+ mAlphaMode = (T3TextureAlphaMode)reader.ReadInt32();
+
+ mWiiTextureFormat = (WiiTextureFormat)reader.ReadInt32();
+ mbAlphaHDR = new TelltaleBoolean(reader);
+ mbUsedAsBumpmap = new TelltaleBoolean(reader);
+ mbUsedAsDetailMap = new TelltaleBoolean(reader);
+ mbEncrypted = new TelltaleBoolean(reader);
+ mDetailMapBrightness = reader.ReadSingle();
+ mEmptyBuffer = reader.ReadInt32();
+
+ if (!mbHasTextureData.mbTelltaleBoolean)
+ {
+ PrintConsole();
+ throw new PixelDataNotFoundException("The texture does not have any pixel data!");
+ }
+
+ uint mTextureDataSize = reader.ReadUInt32();
+
+ if (mTextureDataSize == 0 && mbHasTextureData.mbTelltaleBoolean)
+ {
+ continue;
+ }
+
+ if (reader.BaseStream.Position == reader.BaseStream.Length)
+ {
+ PrintConsole();
+ throw new Exception("Invalid DDS Header! The texture's header is corrupted!");
+ }
+
+ reader.BaseStream.Position -= 4;
+
+ mPixelData = [];
+
+ while (reader.BaseStream.Position < reader.BaseStream.Length)
+ {
+ int magic = reader.ReadInt32();
+ if (magic == 8 || magic == mName.Length + 8)
+ {
+ isValid = false;
+ reader.BaseStream.Position -= 4;
+ break;
+ }
+
+ reader.BaseStream.Position -= 4;
+ TelltalePixelData telltalePixelData = new(reader);
+ mPixelData.Add(telltalePixelData);
+ }
+
+ read = false;
+ }
+
+ if (printDebug)
+ PrintConsole();
+ }
+
+ public void ModifyD3DTX(D3DTXMetadata metadata, ImageSection[] imageSections, bool printDebug = false)
+ {
+ mWidth = metadata.Width;
+ mHeight = metadata.Height;
+ mD3DFormat = metadata.D3DFormat;
+ mNumMipLevels = metadata.MipLevels;
+ mbHasTextureData = new TelltaleBoolean(true);
+ mbIsMipMapped = new TelltaleBoolean(metadata.MipLevels > 1);
+ mbEmbedMipMaps = new TelltaleBoolean(metadata.MipLevels > 1);
+
+ var textureData = DDS_DirectXTexNet.GetPixelDataArrayFromSections(imageSections);
+
+ mPixelData.Clear();
+
+ TelltalePixelData telltalePixelData = new TelltalePixelData()
+ {
+ length = (uint)textureData.Length,
+ pixelData = textureData
+ };
+
+ mPixelData.Add(telltalePixelData);
+
+ PrintConsole();
+ }
+
+ public D3DTXMetadata GetD3DTXMetadata()
+ {
+ D3DTXMetadata metadata = new D3DTXMetadata()
+ {
+ TextureName = mName,
+ Width = mWidth,
+ Height = mHeight,
+ MipLevels = mNumMipLevels,
+ Dimension = T3TextureLayout.eTextureLayout_2D,
+ AlphaMode = mAlphaMode,
+ D3DFormat = mD3DFormat,
+ };
+
+ return metadata;
+ }
+
+ public List GetPixelData()
+ {
+ return mPixelData.Select(x => x.pixelData).ToList();
+ }
+
+ public string GetDebugInfo()
+ {
+ string d3dtxInfo = "";
+
+ d3dtxInfo += "||||||||||| D3DTX Legacy Version 9 Header |||||||||||" + Environment.NewLine;
+ d3dtxInfo += "mName_BlockSize = " + mName_BlockSize + Environment.NewLine;
+ d3dtxInfo += "mName = " + mName + Environment.NewLine;
+ d3dtxInfo += "mImportName_BlockSize = " + mImportName_BlockSize + Environment.NewLine;
+ d3dtxInfo += "mImportName = " + mImportName + Environment.NewLine;
+ d3dtxInfo += "mbHasTextureData = " + mbHasTextureData + Environment.NewLine;
+ d3dtxInfo += "mbIsMipMapped = " + mbIsMipMapped + Environment.NewLine;
+ d3dtxInfo += "mbIsWrapU = " + mbIsWrapU + Environment.NewLine;
+ d3dtxInfo += "mbIsWrapV = " + mbIsWrapV + Environment.NewLine;
+ d3dtxInfo += "mbIsFiltered = " + mbIsFiltered + Environment.NewLine;
+ d3dtxInfo += "mNumMipLevels = " + mNumMipLevels + Environment.NewLine;
+ d3dtxInfo += "mD3DFormat = " + mD3DFormat + Environment.NewLine;
+ d3dtxInfo += "mWidth = " + mWidth + Environment.NewLine;
+ d3dtxInfo += "mHeight = " + mHeight + Environment.NewLine;
+ d3dtxInfo += "mWiiForceWidth = " + mWiiForceWidth + Environment.NewLine;
+ d3dtxInfo += "mWiiForceHeight = " + mWiiForceHeight + Environment.NewLine;
+ d3dtxInfo += "mbWiiForceUncompressed = " + mbWiiForceUncompressed + Environment.NewLine;
+ d3dtxInfo += "mTextureDataFormats = " + mTextureDataFormats + Environment.NewLine;
+ d3dtxInfo += "mTplTextureDataSize = " + mTplTextureDataSize + Environment.NewLine;
+ d3dtxInfo += "mAlphaMode = " + Enum.GetName(typeof(T3TextureAlphaMode), (int)mAlphaMode) + " (" + mAlphaMode + ")" + Environment.NewLine;
+ d3dtxInfo += "mWiiTextureFormat = " + mWiiTextureFormat + Environment.NewLine;
+ d3dtxInfo += "mbAlphaHDR = " + mbAlphaHDR + Environment.NewLine;
+ d3dtxInfo += "mbUsedAsBumpmap = " + mbUsedAsBumpmap + Environment.NewLine;
+ d3dtxInfo += "mbUsedAsDetailMap = " + mbUsedAsDetailMap + Environment.NewLine;
+ d3dtxInfo += "mbEncrypted = " + mbEncrypted + Environment.NewLine;
+ d3dtxInfo += "mDetailMapBrightness = " + mDetailMapBrightness + Environment.NewLine;
+
+ for (int i = 0; i < mPixelData.Count; i++)
+ {
+ d3dtxInfo += "mPixelData[" + i + "] = " + mPixelData[i].ToString() + Environment.NewLine;
+ }
+
+ d3dtxInfo += "|||||||||||||||||||||||||||||||||||||||";
+
+ return d3dtxInfo;
+ }
+
+ public uint GetHeaderByteSize()
+ {
+ return 0;
+ }
+
+ public void PrintConsole()
+ {
+ Console.WriteLine(GetDebugInfo());
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Versions/D3DTX_CLV2.cs b/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Versions/D3DTX_CLV2.cs
index 7ec7e78..c46aea8 100644
--- a/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Versions/D3DTX_CLV2.cs
+++ b/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Versions/D3DTX_CLV2.cs
@@ -339,7 +339,7 @@ public void ReadFromBinary(BinaryReader reader, bool printDebug = false)
}
reader.BaseStream.Position -= 4;
- TelltalePixelData telltalePixelData = new(reader);
+ TelltalePixelData telltalePixelData = new(reader, 128);
mPixelData.Add(telltalePixelData);
}
@@ -399,7 +399,7 @@ public string GetDebugInfo()
{
string d3dtxInfo = "";
- d3dtxInfo += "||||||||||| D3DTX Legacy Version 2 Header |||||||||||" + Environment.NewLine;
+ d3dtxInfo += "||||||||||| D3DTX Console Legacy Version 2 Header |||||||||||" + Environment.NewLine;
d3dtxInfo += "mSamplerState_BlockSize = " + mSamplerState_BlockSize + Environment.NewLine;
d3dtxInfo += "mSamplerState = " + mSamplerState.ToString() + Environment.NewLine;
d3dtxInfo += "mName_BlockSize = " + mName_BlockSize + Environment.NewLine;
@@ -442,8 +442,7 @@ public string GetDebugInfo()
return d3dtxInfo;
}
-
-
+
public void PrintConsole()
{
Console.WriteLine(GetDebugInfo());
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Versions/D3DTX_CLV3.cs b/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Versions/D3DTX_CLV3.cs
index 0f53841..c5cceac 100644
--- a/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Versions/D3DTX_CLV3.cs
+++ b/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Versions/D3DTX_CLV3.cs
@@ -302,7 +302,7 @@ public void ReadFromBinary(BinaryReader reader, bool printDebug = false)
}
reader.BaseStream.Position -= 4;
- TelltalePixelData telltalePixelData = new(reader);
+ TelltalePixelData telltalePixelData = new(reader, 128);
mPixelData.Add(telltalePixelData);
}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Versions/D3DTX_LV12.cs b/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Versions/D3DTX_LV12.cs
index 23f12e3..0a30821 100644
--- a/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Versions/D3DTX_LV12.cs
+++ b/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Versions/D3DTX_LV12.cs
@@ -21,7 +21,7 @@
/* - D3DTX Old Unknown Version games
-
+ SAM AND MAX SEASON 1 OG USES MBIN
*/
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Versions/D3DTX_LV13.cs b/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Versions/D3DTX_LV13.cs
new file mode 100644
index 0000000..7e43964
--- /dev/null
+++ b/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Versions/D3DTX_LV13.cs
@@ -0,0 +1,308 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using TelltaleTextureTool.TelltaleEnums;
+using TelltaleTextureTool.TelltaleTypes;
+using TelltaleTextureTool.Utilities;
+using TelltaleTextureTool.DirectX;
+using TelltaleTextureTool.DirectX.Enums;
+using TelltaleTextureTool.Telltale.FileTypes.D3DTX;
+using System.Linq;
+
+/*
+ * NOTE:
+ *
+ * This version of D3DTX is COMPLETE.
+ *
+ * COMPLETE meaning that all of the data is known and getting identified.
+ * Just like the versions before and after, this D3DTX version derives from version 9 and has been 'stripped' or adjusted to suit this version of D3DTX.
+ * Also, Telltale uses Hungarian Notation for variable naming.
+*/
+
+/* - D3DTX Old Unknown Version games
+
+ SAM AND MAX SEASON 1 OG USES MBIN
+
+*/
+
+/* - D3DTX Legacy Version 12 games
+
+*/
+
+namespace TelltaleTextureTool.TelltaleD3DTX;
+
+///
+/// This is a custom class that matches what is serialized in a legacy D3DTX version supporting the listed titles. (COMPLETE)
+///
+public class D3DTX_LV13 : ID3DTX
+{
+ ///
+ /// [4 bytes] The mName block size in bytes.
+ ///
+ public int mName_BlockSize { get; set; }
+
+ ///
+ /// [mName_StringLength bytes] The string mName.
+ ///
+ public string mName { get; set; } = string.Empty;
+
+ ///
+ /// [4 bytes] The mImportName block size in bytes.
+ ///
+ public int mImportName_BlockSize { get; set; }
+
+ ///
+ /// [mImportName_StringLength bytes] The mImportName string.
+ ///
+ public string mImportName { get; set; } = string.Empty;
+
+ ///
+ /// [1 byte] Indicates whether or not the texture contains mips. (what? need further research)
+ ///
+ public TelltaleBoolean mbHasTextureData { get; set; }
+
+ ///
+ /// [1 byte] Indicates whether or not the texture contains mips.
+ ///
+ public TelltaleBoolean mbIsMipMapped { get; set; }
+
+ ///
+ /// [1 byte] Indicates whether or not the texture contains mips.
+ ///
+ public TelltaleBoolean mbIsWrapU { get; set; }
+
+ ///
+ /// [1 byte] Indicates whether or not the texture contains mips.
+ ///
+ public TelltaleBoolean mbIsWrapV { get; set; }
+
+ ///
+ /// [1 byte] Indicates whether or not the texture contains mips.
+ ///
+ public TelltaleBoolean mbIsFiltered { get; set; }
+
+ ///
+ /// [4 bytes] Number of mips in the texture.
+ ///
+ public uint mNumMipLevels { get; set; }
+
+ ///
+ /// [4 bytes] The old T3SurfaceFormat. Makes use of FourCC but it can be an integer as well. Enums could not be found.
+ ///
+ public D3DFormat mD3DFormat { get; set; }
+
+ ///
+ /// [4 bytes] The pixel width of the texture.
+ ///
+ public uint mWidth { get; set; }
+
+ ///
+ /// [4 bytes] The pixel height of the texture.
+ ///
+ public uint mHeight { get; set; }
+
+ ///
+ /// [4 bytes] The texture data format. No enums were found, need more analyzing. Could be a flag.
+ ///
+ public uint mType { get; set; }
+
+ ///
+ /// [1 byte] Indicates if the texture is encrypted.
+ ///
+ public TelltaleBoolean mbEncrypted { get; set; }
+
+ ///
+ /// A byte array of the pixel regions in a texture.
+ ///
+ public List mPixelData { get; set; } = [];
+
+ public D3DTX_LV13() { }
+
+ public void WriteToBinary(BinaryWriter writer, bool printDebug = false)
+ {
+ writer.Write(mName_BlockSize); //mName Block Size [4 bytes] //mName block size (size + string len)
+ ByteFunctions.WriteString(writer, mName); //mName [x bytes]
+ writer.Write(mImportName_BlockSize); //mImportName Block Size [4 bytes] //mImportName block size (size + string len)
+ ByteFunctions.WriteString(writer, mImportName); //mImportName [x bytes] (this is always 0)
+ ByteFunctions.WriteBoolean(writer, mbHasTextureData.mbTelltaleBoolean); //mbHasTextureData [1 byte]
+ ByteFunctions.WriteBoolean(writer, mbIsMipMapped.mbTelltaleBoolean); //mbIsMipMapped [1 byte]
+ ByteFunctions.WriteBoolean(writer, mbIsWrapU.mbTelltaleBoolean); //mbEmbedMipMaps [1 byte]
+ ByteFunctions.WriteBoolean(writer, mbIsWrapV.mbTelltaleBoolean); //mbEmbedMipMaps [1 byte]
+ ByteFunctions.WriteBoolean(writer, mbIsFiltered.mbTelltaleBoolean); //mbEmbedMipMaps [1 byte]
+ writer.Write(mNumMipLevels); //mNumMipLevels [4 bytes]
+ writer.Write((int)mD3DFormat); //mD3DFormat [4 bytes]
+ writer.Write(mWidth); //mWidth [4 bytes]
+ writer.Write(mHeight); //mHeight [4 bytes]
+ writer.Write(mType); //mType [4 bytes]
+ ByteFunctions.WriteBoolean(writer, mbEncrypted.mbTelltaleBoolean); //mbEncrypted [1 byte]
+
+ for (int i = 0; i < mPixelData.Count; i++) //DDS file including header [mTextureDataSize bytes]
+ {
+ mPixelData[i].WriteBinaryData(writer);
+ }
+ }
+
+ public void ReadFromBinary(BinaryReader reader, bool printDebug = false)
+ {
+ bool read = true;
+ bool isValid = true;
+
+ while (read && isValid)
+ {
+ isValid = true;
+ mName_BlockSize = reader.ReadInt32();
+ mName = ByteFunctions.ReadString(reader);
+ mImportName_BlockSize = reader.ReadInt32();
+ mImportName = ByteFunctions.ReadString(reader);
+ mbHasTextureData = new TelltaleBoolean(reader);
+ mbIsMipMapped = new TelltaleBoolean(reader);
+ mbIsWrapU = new TelltaleBoolean(reader);
+ mbIsWrapV = new TelltaleBoolean(reader);
+ mbIsFiltered = new TelltaleBoolean(reader);
+ mNumMipLevels = reader.ReadUInt32();
+ mD3DFormat = (D3DFormat)reader.ReadUInt32();
+ mWidth = reader.ReadUInt32();
+ mHeight = reader.ReadUInt32();
+ mType = reader.ReadUInt32();
+
+ mbEncrypted = new TelltaleBoolean(reader);
+
+ if (!mbHasTextureData.mbTelltaleBoolean)
+ {
+ PrintConsole();
+ throw new PixelDataNotFoundException("The texture does not have any pixel data!");
+ }
+
+ uint mTextureDataSize = reader.ReadUInt32();
+
+ if (mTextureDataSize == 0 && mbHasTextureData.mbTelltaleBoolean)
+ {
+ continue;
+ }
+
+ if (mTextureDataSize > reader.BaseStream.Length - reader.BaseStream.Position || reader.BaseStream.Position == reader.BaseStream.Length)
+ {
+ PrintConsole();
+ throw new Exception("Invalid DDS Header! The texture's header is corrupted!");
+ }
+
+ reader.BaseStream.Position -= 4;
+
+ mPixelData = [];
+
+ TelltalePixelData telltalePixelData = new(reader);
+ mPixelData.Add(telltalePixelData);
+
+ // for (int i = 0; i < mTplTextureDataSize; i++)
+ // {
+ // telltalePixelData = new(reader);
+ // mPixelData.Add(telltalePixelData);
+ // }
+
+ // while (reader.BaseStream.Position < reader.BaseStream.Length)
+ // {
+ // int magic = reader.ReadInt32();
+ // if (magic == 8 || magic == mName.Length + 8)
+ // {
+ // isValid = false;
+ // reader.BaseStream.Position -= 4;
+ // break;
+ // }
+
+ // reader.BaseStream.Position -= 4;
+ // // TelltalePixelData telltalePixelData = new(reader);
+ // // mPixelData.Add(telltalePixelData);
+ // }
+
+ read = false;
+ }
+
+ if (printDebug)
+ PrintConsole();
+ }
+
+ public void ModifyD3DTX(D3DTXMetadata metadata, ImageSection[] imageSections, bool printDebug = false)
+ {
+ mWidth = metadata.Width;
+ mHeight = metadata.Height;
+ mD3DFormat = metadata.D3DFormat;
+ mNumMipLevels = metadata.MipLevels;
+ mbHasTextureData = new TelltaleBoolean(true);
+ mbIsMipMapped = new TelltaleBoolean(metadata.MipLevels > 1);
+
+ var textureData = DDS_DirectXTexNet.GetPixelDataArrayFromSections(imageSections);
+
+ mPixelData.Clear();
+
+ TelltalePixelData telltalePixelData = new TelltalePixelData()
+ {
+ length = (uint)textureData.Length,
+ pixelData = textureData
+ };
+
+ mPixelData.Add(telltalePixelData);
+
+ PrintConsole();
+ }
+
+ public D3DTXMetadata GetD3DTXMetadata()
+ {
+ D3DTXMetadata metadata = new D3DTXMetadata()
+ {
+ TextureName = mName,
+ Width = mWidth,
+ Height = mHeight,
+ MipLevels = mNumMipLevels,
+ Dimension = T3TextureLayout.eTextureLayout_2D,
+ D3DFormat = mD3DFormat,
+ };
+
+ return metadata;
+ }
+
+ public List GetPixelData()
+ {
+ return [mPixelData[0].pixelData];
+ }
+
+ public string GetDebugInfo()
+ {
+ string d3dtxInfo = "";
+
+ d3dtxInfo += "||||||||||| D3DTX Legacy Version 12 Header |||||||||||" + Environment.NewLine;
+ d3dtxInfo += "mName_BlockSize = " + mName_BlockSize + Environment.NewLine;
+ d3dtxInfo += "mName = " + mName + Environment.NewLine;
+ d3dtxInfo += "mImportName_BlockSize = " + mImportName_BlockSize + Environment.NewLine;
+ d3dtxInfo += "mImportName = " + mImportName + Environment.NewLine;
+ d3dtxInfo += "mbHasTextureData = " + mbHasTextureData + Environment.NewLine;
+ d3dtxInfo += "mbIsMipMapped = " + mbIsMipMapped + Environment.NewLine;
+ d3dtxInfo += "mbIsWrapU = " + mbIsWrapU + Environment.NewLine;
+ d3dtxInfo += "mbIsWrapV = " + mbIsWrapV + Environment.NewLine;
+ d3dtxInfo += "mbIsFiltered = " + mbIsFiltered + Environment.NewLine;
+ d3dtxInfo += "mNumMipLevels = " + mNumMipLevels + Environment.NewLine;
+ d3dtxInfo += "mD3DFormat = " + mD3DFormat + Environment.NewLine;
+ d3dtxInfo += "mWidth = " + mWidth + Environment.NewLine;
+ d3dtxInfo += "mHeight = " + mHeight + Environment.NewLine;
+ d3dtxInfo += "mType = " + mType + Environment.NewLine;
+ d3dtxInfo += "mbEncrypted = " + mbEncrypted + Environment.NewLine;
+
+ for (int i = 0; i < mPixelData.Count; i++)
+ {
+ d3dtxInfo += "mPixelData[" + i + "] = " + mPixelData[i].ToString() + Environment.NewLine;
+ }
+
+ d3dtxInfo += "|||||||||||||||||||||||||||||||||||||||";
+
+ return d3dtxInfo;
+ }
+
+ public uint GetHeaderByteSize()
+ {
+ return 0;
+ }
+
+ public void PrintConsole()
+ {
+ Console.WriteLine(GetDebugInfo());
+ }
+}
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Versions/D3DTX_LV9.cs b/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Versions/D3DTX_LV9.cs
index 84ffb81..9037433 100644
--- a/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Versions/D3DTX_LV9.cs
+++ b/TelltaleTextureTool/TelltaleTextureTool/Telltale/FileTypes/D3DTX/Versions/D3DTX_LV9.cs
@@ -20,6 +20,7 @@
*/
/* - D3DTX Legacy Version 9 games
+ * Telltale Texas Hold'em (UNTESTED)
* Sam & Max Beyond Time and Space (TESTED)
*/
diff --git a/TelltaleTextureTool/TelltaleTextureTool/Telltale/Structs/TelltalePixelData.cs b/TelltaleTextureTool/TelltaleTextureTool/Telltale/Structs/TelltalePixelData.cs
index 54f1027..4d15418 100644
--- a/TelltaleTextureTool/TelltaleTextureTool/Telltale/Structs/TelltalePixelData.cs
+++ b/TelltaleTextureTool/TelltaleTextureTool/Telltale/Structs/TelltalePixelData.cs
@@ -13,6 +13,13 @@ public TelltalePixelData(BinaryReader reader)
pixelData = reader.ReadBytes((int)length);
}
+ public TelltalePixelData(BinaryReader reader, int skip)
+ {
+ length = reader.ReadUInt32();
+ reader.BaseStream.Position += skip;
+ pixelData = reader.ReadBytes((int)length - skip);
+ }
+
public void WriteBinaryData(BinaryWriter writer)
{
writer.Write(length);
diff --git a/TelltaleTextureTool/TelltaleTextureTool/TelltaleTextureTool.csproj b/TelltaleTextureTool/TelltaleTextureTool/TelltaleTextureTool.csproj
index d104161..1b77dfc 100644
--- a/TelltaleTextureTool/TelltaleTextureTool/TelltaleTextureTool.csproj
+++ b/TelltaleTextureTool/TelltaleTextureTool/TelltaleTextureTool.csproj
@@ -38,15 +38,16 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
@@ -57,7 +58,7 @@
-
+