refactor Texture2D convert

This commit is contained in:
Perfare 2020-03-24 06:41:58 +08:00
parent 5b96a29cca
commit f3a0bf505e
57 changed files with 15416 additions and 1333 deletions

View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27130.2024
# Visual Studio Version 16
VisualStudioVersion = 16.0.29806.167
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssetStudioGUI", "AssetStudioGUI\AssetStudioGUI.csproj", "{24551E2D-E9B6-4CD6-8F2A-D9F4A13E7853}"
EndProject
@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssetStudioUtility", "Asset
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssetStudio", "AssetStudio\AssetStudio.csproj", "{AF56B63C-1764-41B7-9E60-8D485422AC3B}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Texture2DDecoder", "Texture2DDecoder\Texture2DDecoder.vcxproj", "{57CFF625-57AB-424A-9B6B-B5ED01282E92}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@ -51,6 +53,14 @@ Global
{AF56B63C-1764-41B7-9E60-8D485422AC3B}.Release|x64.Build.0 = Release|Any CPU
{AF56B63C-1764-41B7-9E60-8D485422AC3B}.Release|x86.ActiveCfg = Release|Any CPU
{AF56B63C-1764-41B7-9E60-8D485422AC3B}.Release|x86.Build.0 = Release|Any CPU
{57CFF625-57AB-424A-9B6B-B5ED01282E92}.Debug|x64.ActiveCfg = Debug|x64
{57CFF625-57AB-424A-9B6B-B5ED01282E92}.Debug|x64.Build.0 = Debug|x64
{57CFF625-57AB-424A-9B6B-B5ED01282E92}.Debug|x86.ActiveCfg = Debug|Win32
{57CFF625-57AB-424A-9B6B-B5ED01282E92}.Debug|x86.Build.0 = Debug|Win32
{57CFF625-57AB-424A-9B6B-B5ED01282E92}.Release|x64.ActiveCfg = Release|x64
{57CFF625-57AB-424A-9B6B-B5ED01282E92}.Release|x64.Build.0 = Release|x64
{57CFF625-57AB-424A-9B6B-B5ED01282E92}.Release|x86.ActiveCfg = Release|Win32
{57CFF625-57AB-424A-9B6B-B5ED01282E92}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -170,5 +170,11 @@ namespace AssetStudio
R8,
ETC_RGB4Crunched,
ETC2_RGBA8Crunched,
ASTC_HDR_4x4,
ASTC_HDR_5x5,
ASTC_HDR_6x6,
ASTC_HDR_8x8,
ASTC_HDR_10x10,
ASTC_HDR_12x12,
}
}

View File

@ -12,11 +12,11 @@ namespace AssetStudioGUI
{
public static bool ExportTexture2D(AssetItem item, string exportPathName)
{
var converter = new Texture2DConverter((Texture2D)item.Asset);
var m_Texture2D = (Texture2D)item.Asset;
var convertTexture = (bool)Properties.Settings.Default["convertTexture"];
if (convertTexture)
{
var bitmap = converter.ConvertToBitmap(true);
var bitmap = m_Texture2D.ConvertToBitmap(true);
if (bitmap == null)
return false;
ImageFormat format = null;
@ -52,10 +52,10 @@ namespace AssetStudioGUI
}
else
{
var exportFullName = exportPathName + item.Text + converter.GetExtensionName();
var exportFullName = exportPathName + item.Text + ".tex";
if (ExportFileExists(exportFullName))
return false;
File.WriteAllBytes(exportFullName, converter.ConvertToContainer());
File.WriteAllBytes(exportFullName, m_Texture2D.image_data.Value);
return true;
}
}

View File

@ -1,151 +0,0 @@
FMOD, FMOD Ex, FMOD Designer and FMOD Studio are
Copyright © 2005-2016 Firelight Technologies Pty, Ltd.
GRANT OF LICENSE
----------------
THIS END USER LICENSE AGREEMENT GRANTS THE USER, THE RIGHT TO USE FMOD,
IN ITS LIBRARY AND TOOL FORM, IN THEIR OWN PRODUCTS, BE THEY FOR PERSONAL,
EDUCATIONAL OR COMMERCIAL USE.
THE USER MUST ADHERE TO THE LICENSING MODEL PROVIDED BY FIRELIGHT
TECHNOLOGIES, AND MUST APPLY FOR A LICENSE IF NECESSARY. THE FOLLOWING
LICENSES ARE AVAILABLE.
FMOD NON-COMMERCIAL LICENSE
------------------------------------
IF YOUR PRODUCT IS NOT INTENDED FOR COMMERCIAL GAIN AND DOES NOT
INCLUDE THE FMOD LIBRARY FOR RESALE, LICENSE OR OTHER COMMERCIAL
DISTRIBUTION, THEN USE OF FMOD IS FREE OF CHARGE. THERE ARE NO
LICENSE FEES FOR NON-COMMERCIAL APPLICATIONS.
THE USER MAY USE THIS EULA AS EVIDENCE OF THEIR LICENSE WITHOUT
CONTACTING FIRELIGHT TECHNOLOGIES.
CONDITIONS/LIMITATIONS:
- WHEN USING THIS LICENSE, THE FMOD LIBRARY CANNOT BE USED FOR
RESALE OR OTHER COMMERCIAL DISTRIBUTION
- THIS LICENSE CANNOT BE USED FOR PRODUCTS WHICH DO NOT MAKE
PROFIT BUT ARE STILL COMMERCIALLY RELEASED
- THIS LICENSE CANNOT BE USED FOR COMMERCIAL SERVICES, WHERE THE
EXECUTABLE CONTAINING FMOD IS NOT SOLD, BUT THE DATA IS.
- WHEN USING FMOD, A CREDIT LINE IS REQUIRED IN EITHER DOCUMENTATION,
OR 'ON SCREEN' FORMAT (IF POSSIBLE). IT SHOULD CONTAIN AT LEAST
THE WORDS "FMOD" (OR "FMOD STUDIO" IF APPLICABLE) AND
"FIRELIGHT TECHNOLOGIES."
LOGOS ARE AVAILABLE FOR BOX OR MANUAL ART, BUT ARE NOT MANDATORY.
AN EXAMPLE CREDIT COULD BE:
FMOD Sound System, copyright © Firelight Technologies Pty, Ltd., 1994-2016.
OR
FMOD Studio, copyright © Firelight Technologies Pty, Ltd., 1994-2016.
OR
Audio Engine supplied by FMOD by Firelight Technologies.
NOTE THIS IN ADVANCE, AS IT MUST BE DONE BEFORE SHIPPING YOUR
PRODUCT WITH FMOD.
FMOD FREE FOR INDIES LICENSE (FMOD STUDIO ONLY)
------------------------------------------------
INDIE DEVELOPERS ARE CONSIDERED BY OUR LICENSING MODEL, DEVELOPERS THAT DEVELOP
A TITLE FOR UNDER $100K USD (TYPICALLY CONSIDERED AN 'INDIE' TITLE) TOTAL
BUDGET, MEANING YOUR TOTAL COSTS ARE LESS THAN $100K USD AT TIME OF SHIPPING,
YOU CAN USE FMOD FOR FREE.
CONDITIONS/LIMITATIONS
- PLEASE WRITE TO SALES@FMOD.COM WITH THE NAME OF YOUR TITLE, RELEASE DATE
AND PLATFORMS SO WE CAN REGISTER YOU IN OUR SYSTEM.
- THERE IS NO RESTRICTION ON PLATFORM, ANY PLATFORM COMBINATION MAY BE USED.
- INCOME IS NOT RELEVANT TO THE BUDGET LEVEL, IT MUST BE EXPENSE RELATED.
- WHEN USING FMOD, A CREDIT LINE IS REQUIRED IN EITHER DOCUMENTATION,
OR 'ON SCREEN' FORMAT (IF POSSIBLE). IT SHOULD CONTAIN AT LEAST
THE WORDS FMOD STUDIO AND FIRELIGHT TECHNOLOGIES.
LOGOS ARE AVAILABLE FOR BOX OR MANUAL ART, BUT ARE NOT MANDATORY.
AN EXAMPLE CREDIT COULD BE:
FMOD STUDIO, COPYRIGHT © FIRELIGHT TECHNOLOGIES PTY, LTD., 1994-2016.
COMMERCIAL USAGE (FMOD EX AND FMOD STUDIO)
------------------------------------------
IF THE PRODUCT THAT USES FMOD IS INTENDED TO GENERATE INCOME, VIA DIRECT SALES
OR INDIRECT REVENUE (SUCH AS ADVERTISING, DONATIONS, CONTRACT FEE) THEN THE
DEVELOPER MUST APPLY TO FIRELIGHT TECHNOLOGIES FOR A COMMERCIAL LICENSE (UNLESS
THE USER QUALIFIES FOR AN FMOD STUDIO 'INDIE LICENSE').
TO APPLY FOR THIS LICENSE WRITE TO SALES@FMOD.COM WITH THE RELEVANT DETAILS.
REDISTRIBUTION LICENSE (FMOD EX AND FMOD STUDIO)
------------------------------------------------
IF THE USER WISHES TO REDISTRIBUTE FMOD AS PART OF AN ENGINE OR TOOL SOLUTION,
THE USER MUST APPLY TO FIRELIGHT TECHNOLOGIES TO BE GRANTED A 'REDISTRIBUTION
LICENSE'.
TO APPLY FOR THIS LICENSE WRITE TO SALES@FMOD.COM WITH THE RELEVANT DETAILS.
WARRANTY AND LIMITATION OF LIABILITY
------------------------------------
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
FMOD Uses Ogg Vorbis codec. BSD license.
-----------------------------------------
Copyright (c) 2002, Xiph.org Foundation
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the Xiph.org Foundation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
For Android platform code.
--------------------------
Copyright (C) 2010 The Android Open Source Project
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

View File

@ -1,19 +0,0 @@
Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,22 +0,0 @@
crunch/crnlib uses the ZLIB license:
http://opensource.org/licenses/Zlib
Copyright (c) 2010-2016 Richard Geldreich, Jr. and Binomial LLC
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

View File

@ -1,13 +0,0 @@
Copyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@ -73,6 +73,7 @@
<Compile Include="ShaderConverter.cs" />
<Compile Include="SpriteHelper.cs" />
<Compile Include="Texture2DConverter.cs" />
<Compile Include="Texture2DExtensions.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AssetStudioFBX\AssetStudioFBX.vcxproj">
@ -83,6 +84,10 @@
<Project>{af56b63c-1764-41b7-9e60-8d485422ac3b}</Project>
<Name>AssetStudio</Name>
</ProjectReference>
<ProjectReference Include="..\Texture2DDecoder\Texture2DDecoder.vcxproj">
<Project>{57cff625-57ab-424a-9b6b-b5ed01282e92}</Project>
<Name>Texture2DDecoder</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
using System.Drawing;
namespace AssetStudio
{
public static class Texture2DExtensions
{
public static Bitmap ConvertToBitmap(this Texture2D m_Texture2D, bool flip)
{
var converter = new Texture2DConverter(m_Texture2D);
return converter.ConvertToBitmap(flip);
}
}
}

View File

@ -0,0 +1,20 @@
using namespace System;
using namespace System::Reflection;
using namespace System::Runtime::CompilerServices;
using namespace System::Runtime::InteropServices;
using namespace System::Security::Permissions;
[assembly:AssemblyTitleAttribute(L"Texture2DDecoder")];
[assembly:AssemblyDescriptionAttribute(L"")];
[assembly:AssemblyConfigurationAttribute(L"")];
[assembly:AssemblyCompanyAttribute(L"")];
[assembly:AssemblyProductAttribute(L"Texture2DDecoder")];
[assembly:AssemblyCopyrightAttribute(L"Copyright © Perfare 2020")];
[assembly:AssemblyTrademarkAttribute(L"")];
[assembly:AssemblyCultureAttribute(L"")];
[assembly:AssemblyVersionAttribute("1.0.*")];
[assembly:ComVisible(false)];
[assembly:CLSCompliantAttribute(true)];

View File

@ -0,0 +1,148 @@
#include <string.h>
#include "Texture2DDecoder.h"
#include "bcn.h"
#include "pvrtc.h"
#include "etc.h"
#include "atc.h"
#include "astc.h"
#include "crunch.h"
#include "unitycrunch.h"
namespace Texture2DDecoder {
bool TextureDecoder::DecodeDXT1(array<Byte>^ data, long w, long h, array<Byte>^ image) {
pin_ptr<unsigned char> dataPin = &data[0];
pin_ptr<unsigned char> imagePin = &image[0];
return decode_bc1(dataPin, w, h, reinterpret_cast<uint32_t*>(imagePin));
}
bool TextureDecoder::DecodeDXT5(array<Byte>^ data, long w, long h, array<Byte>^ image) {
pin_ptr<unsigned char> dataPin = &data[0];
pin_ptr<unsigned char> imagePin = &image[0];
return decode_bc3(dataPin, w, h, reinterpret_cast<uint32_t*>(imagePin));
}
bool TextureDecoder::DecodePVRTC(array<Byte>^ data, long w, long h, array<Byte>^ image, bool is2bpp) {
pin_ptr<unsigned char> dataPin = &data[0];
pin_ptr<unsigned char> imagePin = &image[0];
return decode_pvrtc(dataPin, w, h, reinterpret_cast<uint32_t*>(imagePin), is2bpp ? 1 : 0);
}
bool TextureDecoder::DecodeETC1(array<Byte>^ data, long w, long h, array<Byte>^ image) {
pin_ptr<unsigned char> dataPin = &data[0];
pin_ptr<unsigned char> imagePin = &image[0];
return decode_etc1(dataPin, w, h, reinterpret_cast<uint32_t*>(imagePin));
}
bool TextureDecoder::DecodeETC2(array<Byte>^ data, long w, long h, array<Byte>^ image) {
pin_ptr<unsigned char> dataPin = &data[0];
pin_ptr<unsigned char> imagePin = &image[0];
return decode_etc2(dataPin, w, h, reinterpret_cast<uint32_t*>(imagePin));
}
bool TextureDecoder::DecodeETC2A1(array<Byte>^ data, long w, long h, array<Byte>^ image) {
pin_ptr<unsigned char> dataPin = &data[0];
pin_ptr<unsigned char> imagePin = &image[0];
return decode_etc2a1(dataPin, w, h, reinterpret_cast<uint32_t*>(imagePin));
}
bool TextureDecoder::DecodeETC2A8(array<Byte>^ data, long w, long h, array<Byte>^ image) {
pin_ptr<unsigned char> dataPin = &data[0];
pin_ptr<unsigned char> imagePin = &image[0];
return decode_etc2a8(dataPin, w, h, reinterpret_cast<uint32_t*>(imagePin));
}
bool TextureDecoder::DecodeEACR(array<Byte>^ data, long w, long h, array<Byte>^ image) {
pin_ptr<unsigned char> dataPin = &data[0];
pin_ptr<unsigned char> imagePin = &image[0];
return decode_eacr(dataPin, w, h, reinterpret_cast<uint32_t*>(imagePin));
}
bool TextureDecoder::DecodeEACRSigned(array<Byte>^ data, long w, long h, array<Byte>^ image) {
pin_ptr<unsigned char> dataPin = &data[0];
pin_ptr<unsigned char> imagePin = &image[0];
return decode_eacr_signed(dataPin, w, h, reinterpret_cast<uint32_t*>(imagePin));
}
bool TextureDecoder::DecodeEACRG(array<Byte>^ data, long w, long h, array<Byte>^ image) {
pin_ptr<unsigned char> dataPin = &data[0];
pin_ptr<unsigned char> imagePin = &image[0];
return decode_eacrg(dataPin, w, h, reinterpret_cast<uint32_t*>(imagePin));
}
bool TextureDecoder::DecodeEACRGSigned(array<Byte>^ data, long w, long h, array<Byte>^ image) {
pin_ptr<unsigned char> dataPin = &data[0];
pin_ptr<unsigned char> imagePin = &image[0];
return decode_eacrg_signed(dataPin, w, h, reinterpret_cast<uint32_t*>(imagePin));
}
bool TextureDecoder::DecodeBC4(array<Byte>^ data, long w, long h, array<Byte>^ image) {
pin_ptr<unsigned char> dataPin = &data[0];
pin_ptr<unsigned char> imagePin = &image[0];
return decode_bc4(dataPin, w, h, reinterpret_cast<uint32_t*>(imagePin));
}
bool TextureDecoder::DecodeBC5(array<Byte>^ data, long w, long h, array<Byte>^ image) {
pin_ptr<unsigned char> dataPin = &data[0];
pin_ptr<unsigned char> imagePin = &image[0];
return decode_bc5(dataPin, w, h, reinterpret_cast<uint32_t*>(imagePin));
}
bool TextureDecoder::DecodeBC6(array<Byte>^ data, long w, long h, array<Byte>^ image) {
pin_ptr<unsigned char> dataPin = &data[0];
pin_ptr<unsigned char> imagePin = &image[0];
return decode_bc6(dataPin, w, h, reinterpret_cast<uint32_t*>(imagePin));
}
bool TextureDecoder::DecodeBC7(array<Byte>^ data, long w, long h, array<Byte>^ image) {
pin_ptr<unsigned char> dataPin = &data[0];
pin_ptr<unsigned char> imagePin = &image[0];
return decode_bc7(dataPin, w, h, reinterpret_cast<uint32_t*>(imagePin));
}
bool TextureDecoder::DecodeATCRGB4(array<Byte>^ data, long w, long h, array<Byte>^ image) {
pin_ptr<unsigned char> dataPin = &data[0];
pin_ptr<unsigned char> imagePin = &image[0];
return decode_atc_rgb4(dataPin, w, h, reinterpret_cast<uint32_t*>(imagePin));
}
bool TextureDecoder::DecodeATCRGBA8(array<Byte>^ data, long w, long h, array<Byte>^ image) {
pin_ptr<unsigned char> dataPin = &data[0];
pin_ptr<unsigned char> imagePin = &image[0];
return decode_atc_rgba8(dataPin, w, h, reinterpret_cast<uint32_t*>(imagePin));
}
bool TextureDecoder::DecodeASTC(array<Byte>^ data, long w, long h, int bw, int bh, array<Byte>^ image) {
pin_ptr<unsigned char> dataPin = &data[0];
pin_ptr<unsigned char> imagePin = &image[0];
return decode_astc(dataPin, w, h, bw, bh, reinterpret_cast<uint32_t*>(imagePin));
}
array<Byte>^ TextureDecoder::UnpackCrunch(array<Byte>^ data) {
pin_ptr<unsigned char> dataPin = &data[0];
void* ret;
uint32_t retSize;
if (!crunch_unpack_level(dataPin, data->Length, 0, &ret, &retSize)) {
return nullptr;
}
auto buff = gcnew array<Byte>(retSize);
pin_ptr<unsigned char> buffPin = &buff[0];
memcpy(buffPin, ret, retSize);
delete ret;
return buff;
}
array<Byte>^ TextureDecoder::UnpackUnityCrunch(array<Byte>^ data) {
pin_ptr<unsigned char> dataPin = &data[0];
void* ret;
uint32_t retSize;
if (!unity_crunch_unpack_level(dataPin, data->Length, 0, &ret, &retSize)) {
return nullptr;
}
auto buff = gcnew array<Byte>(retSize);
pin_ptr<unsigned char> buffPin = &buff[0];
memcpy(buffPin, ret, retSize);
delete ret;
return buff;
}
}

View File

@ -0,0 +1,30 @@
#pragma once
using namespace System;
namespace Texture2DDecoder {
public ref class TextureDecoder
{
public:
static bool DecodeDXT1(array<Byte>^ data, long w, long h, array<Byte>^ image);
static bool DecodeDXT5(array<Byte>^ data, long w, long h, array<Byte>^ image);
static bool DecodePVRTC(array<Byte>^ data, long w, long h, array<Byte>^ image, bool is2bpp);
static bool DecodeETC1(array<Byte>^ data, long w, long h, array<Byte>^ image);
static bool DecodeETC2(array<Byte>^ data, long w, long h, array<Byte>^ image);
static bool DecodeETC2A1(array<Byte>^ data, long w, long h, array<Byte>^ image);
static bool DecodeETC2A8(array<Byte>^ data, long w, long h, array<Byte>^ image);
static bool DecodeEACR(array<Byte>^ data, long w, long h, array<Byte>^ image);
static bool DecodeEACRSigned(array<Byte>^ data, long w, long h, array<Byte>^ image);
static bool DecodeEACRG(array<Byte>^ data, long w, long h, array<Byte>^ image);
static bool DecodeEACRGSigned(array<Byte>^ data, long w, long h, array<Byte>^ image);
static bool DecodeBC4(array<Byte>^ data, long w, long h, array<Byte>^ image);
static bool DecodeBC5(array<Byte>^ data, long w, long h, array<Byte>^ image);
static bool DecodeBC6(array<Byte>^ data, long w, long h, array<Byte>^ image);
static bool DecodeBC7(array<Byte>^ data, long w, long h, array<Byte>^ image);
static bool DecodeATCRGB4(array<Byte>^ data, long w, long h, array<Byte>^ image);
static bool DecodeATCRGBA8(array<Byte>^ data, long w, long h, array<Byte>^ image);
static bool DecodeASTC(array<Byte>^ data, long w, long h, int bw, int bh, array<Byte>^ image);
static array<Byte>^ UnpackCrunch(array<Byte>^ data);
static array<Byte>^ UnpackUnityCrunch(array<Byte>^ data);
};
}

View File

@ -0,0 +1,147 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{57CFF625-57AB-424A-9B6B-B5ED01282E92}</ProjectGuid>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<Keyword>ManagedCProj</Keyword>
<RootNamespace>Texture2DDecoder</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CLRSupport>true</CLRSupport>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CLRSupport>true</CLRSupport>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CLRSupport>true</CLRSupport>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CLRSupport>true</CLRSupport>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<AdditionalDependencies />
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PreprocessorDefinitions>WIN32;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<AdditionalDependencies />
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PreprocessorDefinitions>WIN32;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<AdditionalDependencies />
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<AdditionalDependencies />
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="AssemblyInfo.cpp" />
<ClCompile Include="astc.cpp" />
<ClCompile Include="atc.cpp" />
<ClCompile Include="bcn.cpp" />
<ClCompile Include="crunch.cpp" />
<ClCompile Include="unitycrunch.cpp" />
<ClCompile Include="etc.cpp" />
<ClCompile Include="pvrtc.cpp" />
<ClCompile Include="Texture2DDecoder.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="astc.h" />
<ClInclude Include="atc.h" />
<ClInclude Include="bcn.h" />
<ClInclude Include="color.h" />
<ClInclude Include="crunch.h" />
<ClInclude Include="crunch\crnlib.h" />
<ClInclude Include="crunch\crn_decomp.h" />
<ClInclude Include="unitycrunch.h" />
<ClInclude Include="unitycrunch\crnlib.h" />
<ClInclude Include="unitycrunch\crn_decomp.h" />
<ClInclude Include="unitycrunch\crn_defs.h" />
<ClInclude Include="endianness.h" />
<ClInclude Include="etc.h" />
<ClInclude Include="fp16.h" />
<ClInclude Include="fp16\bitcasts.h" />
<ClInclude Include="fp16\fp16.h" />
<ClInclude Include="pvrtc.h" />
<ClInclude Include="Texture2DDecoder.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="源文件">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="头文件">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Texture2DDecoder.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="astc.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="pvrtc.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="etc.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="crunch.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="unitycrunch.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="bcn.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="atc.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="AssemblyInfo.cpp">
<Filter>源文件</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Texture2DDecoder.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="astc.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="color.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="endianness.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="fp16.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="fp16\fp16.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="pvrtc.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="etc.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="unitycrunch\crn_decomp.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="unitycrunch\crn_defs.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="unitycrunch\crnlib.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="crunch.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="unitycrunch.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="crunch\crn_decomp.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="crunch\crnlib.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="fp16\bitcasts.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="bcn.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="atc.h">
<Filter>头文件</Filter>
</ClInclude>
</ItemGroup>
</Project>

1148
Texture2DDecoder/astc.cpp Normal file

File diff suppressed because it is too large Load Diff

8
Texture2DDecoder/astc.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef ASTC_H
#define ASTC_H
#include <stdint.h>
int decode_astc(const uint8_t *, const long, const long, const int, const int, uint32_t *);
#endif /* end of include guard: ASTC_H */

91
Texture2DDecoder/atc.cpp Normal file
View File

@ -0,0 +1,91 @@
#include "bcn.h"
#include "atc.h"
#include "color.h"
#include <algorithm>
static uint8_t expand_quantized(uint8_t v, int bits) {
v = v << (8 - bits);
return v | (v >> bits);
}
void decode_atc_block(const uint8_t* _src, uint32_t* _dst)
{
uint8_t colors[4 * 4];
uint32_t c0 = _src[0] | (_src[1] << 8);
uint32_t c1 = _src[2] | (_src[3] << 8);
if (0 == (c0 & 0x8000))
{
colors[0] = expand_quantized((c0 >> 0) & 0x1f, 5);
colors[1] = expand_quantized((c0 >> 5) & 0x1f, 5);
colors[2] = expand_quantized((c0 >> 10) & 0x1f, 5);
colors[12] = expand_quantized((c1 >> 0) & 0x1f, 5);
colors[13] = expand_quantized((c1 >> 5) & 0x3f, 6);
colors[14] = expand_quantized((c1 >> 11) & 0x1f, 5);
colors[4] = (5 * colors[0] + 3 * colors[12]) / 8;
colors[5] = (5 * colors[1] + 3 * colors[13]) / 8;
colors[6] = (5 * colors[2] + 3 * colors[14]) / 8;
colors[8] = (3 * colors[0] + 5 * colors[12]) / 8;
colors[9] = (3 * colors[1] + 5 * colors[13]) / 8;
colors[10] = (3 * colors[2] + 5 * colors[14]) / 8;
}
else
{
colors[0] = 0;
colors[1] = 0;
colors[2] = 0;
colors[8] = expand_quantized((c0 >> 0) & 0x1f, 5);
colors[9] = expand_quantized((c0 >> 5) & 0x1f, 5);
colors[10] = expand_quantized((c0 >> 10) & 0x1f, 5);
colors[12] = expand_quantized((c1 >> 0) & 0x1f, 5);
colors[13] = expand_quantized((c1 >> 5) & 0x3f, 6);
colors[14] = expand_quantized((c1 >> 11) & 0x1f, 5);
colors[4] = std::max(0, colors[8] - colors[12] / 4);
colors[5] = std::max(0, colors[9] - colors[13] / 4);
colors[6] = std::max(0, colors[10] - colors[14] / 4);
}
for (uint32_t i = 0, next = 8 * 4; i < 16; i += 1, next += 2)
{
int32_t idx = ((_src[next >> 3] >> (next & 7)) & 3) * 4;
_dst[i] = color(colors[idx + 2], colors[idx + 1], colors[idx + 0], 255);
}
}
int decode_atc_rgb4(const uint8_t* data, uint32_t m_width, uint32_t m_height, uint32_t* image) {
uint32_t m_block_width = 4;
uint32_t m_block_height = 4;
uint32_t m_blocks_x = (m_width + m_block_width - 1) / m_block_width;
uint32_t m_blocks_y = (m_height + m_block_height - 1) / m_block_height;
uint32_t buffer[16];
for (uint32_t by = 0; by < m_blocks_y; by++) {
for (uint32_t bx = 0; bx < m_blocks_x; bx++, data += 8) {
decode_atc_block(data, buffer);
copy_block_buffer(bx, by, m_width, m_height, m_block_width, m_block_height, buffer, image);
}
}
return 1;
}
int decode_atc_rgba8(const uint8_t* data, uint32_t m_width, uint32_t m_height, uint32_t* image) {
uint32_t m_block_width = 4;
uint32_t m_block_height = 4;
uint32_t m_blocks_x = (m_width + m_block_width - 1) / m_block_width;
uint32_t m_blocks_y = (m_height + m_block_height - 1) / m_block_height;
uint32_t buffer[16];
for (uint32_t by = 0; by < m_blocks_y; by++) {
for (uint32_t bx = 0; bx < m_blocks_x; bx++, data += 16) {
decode_atc_block(data + 8, buffer);
decode_bc3_alpha(data, buffer, 3);
copy_block_buffer(bx, by, m_width, m_height, m_block_width, m_block_height, buffer, image);
}
}
return 1;
}

5
Texture2DDecoder/atc.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
#include <stdint.h>
int decode_atc_rgb4(const uint8_t* data, uint32_t m_width, uint32_t m_height, uint32_t* image);
int decode_atc_rgba8(const uint8_t* data, uint32_t m_width, uint32_t m_height, uint32_t* image);

1135
Texture2DDecoder/bcn.cpp Normal file

File diff suppressed because it is too large Load Diff

20
Texture2DDecoder/bcn.h Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include <stdint.h>
struct color_bgra
{
uint8_t b;
uint8_t g;
uint8_t r;
uint8_t a;
};
const color_bgra g_black_color{ 0, 0, 0, 255 };
int decode_bc1(const uint8_t* data, const long w, const long h, uint32_t* image);
void decode_bc3_alpha(const uint8_t* data, uint32_t* outbuf, int channel);
int decode_bc3(const uint8_t* data, const long w, const long h, uint32_t* image);
int decode_bc4(const uint8_t* data, uint32_t m_width, uint32_t m_height, uint32_t* image);
int decode_bc5(const uint8_t* data, uint32_t m_width, uint32_t m_height, uint32_t* image);
int decode_bc6(const uint8_t* data, uint32_t m_width, uint32_t m_height, uint32_t* image);
int decode_bc7(const uint8_t* data, uint32_t m_width, uint32_t m_height, uint32_t* image);

87
Texture2DDecoder/color.h Normal file
View File

@ -0,0 +1,87 @@
#ifndef COLOR_H
#define COLOR_H
#include <stdint.h>
#include <string.h>
#include "endianness.h"
#ifdef __LITTLE_ENDIAN__
static const uint_fast32_t TRANSPARENT_MASK = 0x00ffffff;
#else
static const uint_fast32_t TRANSPARENT_MASK = 0xffffff00;
#endif
static inline uint_fast32_t color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
#ifdef __LITTLE_ENDIAN__
return b | g << 8 | r << 16 | a << 24;
#else
return a | r << 8 | g << 16 | b << 24;
#endif
}
static inline uint_fast32_t alpha_mask(uint8_t a) {
#ifdef __LITTLE_ENDIAN__
return TRANSPARENT_MASK | a << 24;
#else
return TRANSPARENT_MASK | a;
#endif
}
static inline void rgb565_le(const uint16_t d, uint8_t *r, uint8_t *g, uint8_t *b) {
#ifdef __LITTLE_ENDIAN__
*r = (d >> 8 & 0xf8) | (d >> 13);
*g = (d >> 3 & 0xfc) | (d >> 9 & 3);
*b = (d << 3) | (d >> 2 & 7);
#else
*r = (d & 0xf8) | (d >> 5 & 7);
*g = (d << 5 & 0xe0) | (d >> 11 & 0x1c) | (d >> 1 & 3);
*b = (d >> 5 & 0xf8) | (d >> 10 & 0x7);
#endif
}
static inline void rgb565_be(const uint16_t d, uint8_t *r, uint8_t *g, uint8_t *b) {
#ifdef __BIG_ENDIAN__
*r = (d >> 8 & 0xf8) | (d >> 13);
*g = (d >> 3 & 0xfc) | (d >> 9 & 3);
*b = (d << 3) | (d >> 2 & 7);
#else
*r = (d & 0xf8) | (d >> 5 & 7);
*g = (d << 5 & 0xe0) | (d >> 11 & 0x1c) | (d >> 1 & 3);
*b = (d >> 5 & 0xf8) | (d >> 10 & 0x7);
#endif
}
static inline void rgb565_lep(const uint16_t d, uint8_t *c) {
#ifdef __LITTLE_ENDIAN__
*(c++) = (d >> 8 & 0xf8) | (d >> 13);
*(c++) = (d >> 3 & 0xfc) | (d >> 9 & 3);
*(c++) = (d << 3) | (d >> 2 & 7);
#else
*(c++) = (d & 0xf8) | (d >> 5 & 7);
*(c++) = (d << 5 & 0xe0) | (d >> 11 & 0x1c) | (d >> 1 & 3);
*(c++) = (d >> 5 & 0xf8) | (d >> 10 & 0x7);
#endif
}
static inline void rgb565_bep(const uint16_t d, uint8_t *c) {
#ifdef __BIG_ENDIAN__
*(c++) = (d >> 8 & 0xf8) | (d >> 13);
*(c++) = (d >> 3 & 0xfc) | (d >> 9 & 3);
*(c++) = (d << 3) | (d >> 2 & 7);
#else
*(c++) = (d & 0xf8) | (d >> 5 & 7);
*(c++) = (d << 5 & 0xe0) | (d >> 11 & 0x1c) | (d >> 1 & 3);
*(c++) = (d >> 5 & 0xf8) | (d >> 10 & 0x7);
#endif
}
static inline void copy_block_buffer(const long bx, const long by, const long w, const long h, const long bw,
const long bh, const uint32_t *buffer, uint32_t *image) {
long x = bw * bx;
long xl = (bw * (bx + 1) > w ? w - bw * bx : bw) * 4;
const uint32_t *buffer_end = buffer + bw * bh;
for (long y = by * bh; buffer < buffer_end && y < h; buffer += bw, y++)
memcpy(image + y * w + x, buffer, xl);
}
#endif /* end of include guard: COLOR_H */

View File

@ -0,0 +1,34 @@
#include "crunch.h"
#include <stdint.h>
#include <algorithm>
#include "crunch/crn_decomp.h"
bool crunch_unpack_level(const uint8_t* data, uint32_t data_size, uint32_t level_index, void** ret, uint32_t* ret_size) {
crnd::crn_texture_info tex_info;
if (!crnd::crnd_get_texture_info(data, data_size, &tex_info))
{
return false;
}
crnd::crnd_unpack_context pContext = crnd::crnd_unpack_begin(data, data_size);
if (!pContext)
{
return false;
}
const crn_uint32 width = std::max(1U, tex_info.m_width >> level_index);
const crn_uint32 height = std::max(1U, tex_info.m_height >> level_index);
const crn_uint32 blocks_x = std::max(1U, (width + 3) >> 2);
const crn_uint32 blocks_y = std::max(1U, (height + 3) >> 2);
const crn_uint32 row_pitch = blocks_x * crnd::crnd_get_bytes_per_dxt_block(tex_info.m_format);
const crn_uint32 total_face_size = row_pitch * blocks_y;
*ret = new uint8_t[total_face_size];
*ret_size = total_face_size;
if (!crnd::crnd_unpack_level(pContext, ret, total_face_size, row_pitch, level_index))
{
crnd::crnd_unpack_end(pContext);
return false;
}
crnd::crnd_unpack_end(pContext);
return true;
}

View File

@ -0,0 +1,5 @@
#pragma once
#include <stdint.h>
bool crunch_unpack_level(const uint8_t* data, uint32_t data_size, uint32_t level_index, void** ret, uint32_t* ret_size);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,645 @@
// File: crnlib.h - Advanced DXTn texture compression library.
// Copyright (c) 2010-2016 Richard Geldreich, Jr. All rights reserved.
// See copyright notice and license at the end of this file.
//
// This header file contains the public crnlib declarations for DXTn,
// clustered DXTn, and CRN compression/decompression.
//
// Note: This library does NOT need to be linked into your game executable if
// all you want to do is transcode .CRN files to raw DXTn bits at run-time.
// The crn_decomp.h header file library contains all the code necessary for
// decompression.
//
// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing
#ifndef CRNLIB_H
#define CRNLIB_H
#ifdef _MSC_VER
#pragma warning (disable: 4127) // conditional expression is constant
#endif
#define CRNLIB_VERSION 104
#define CRNLIB_SUPPORT_ATI_COMPRESS 0
#define CRNLIB_SUPPORT_SQUISH 0
typedef unsigned char crn_uint8;
typedef unsigned short crn_uint16;
typedef unsigned int crn_uint32;
typedef signed char crn_int8;
typedef signed short crn_int16;
typedef signed int crn_int32;
typedef unsigned int crn_bool;
// crnlib can compress to these file types.
enum crn_file_type
{
// .CRN
cCRNFileTypeCRN = 0,
// .DDS using regular DXT or clustered DXT
cCRNFileTypeDDS,
cCRNFileTypeForceDWORD = 0xFFFFFFFF
};
// Supported compressed pixel formats.
// Basically all the standard DX9 formats, with some swizzled DXT5 formats
// (most of them supported by ATI's Compressonator), along with some ATI/X360 GPU specific formats.
enum crn_format
{
cCRNFmtInvalid = -1,
cCRNFmtDXT1 = 0,
cCRNFmtFirstValid = cCRNFmtDXT1,
// cCRNFmtDXT3 is not currently supported when writing to CRN - only DDS.
cCRNFmtDXT3,
cCRNFmtDXT5,
// Various DXT5 derivatives
cCRNFmtDXT5_CCxY, // Luma-chroma
cCRNFmtDXT5_xGxR, // Swizzled 2-component
cCRNFmtDXT5_xGBR, // Swizzled 3-component
cCRNFmtDXT5_AGBR, // Swizzled 4-component
// ATI 3DC and X360 DXN
cCRNFmtDXN_XY,
cCRNFmtDXN_YX,
// DXT5 alpha blocks only
cCRNFmtDXT5A,
cCRNFmtETC1,
cCRNFmtTotal,
cCRNFmtForceDWORD = 0xFFFFFFFF
};
// Various library/file format limits.
enum crn_limits
{
// Max. mipmap level resolution on any axis.
cCRNMaxLevelResolution = 4096,
cCRNMinPaletteSize = 8,
cCRNMaxPaletteSize = 8192,
cCRNMaxFaces = 6,
cCRNMaxLevels = 16,
cCRNMaxHelperThreads = 16,
cCRNMinQualityLevel = 0,
cCRNMaxQualityLevel = 255
};
// CRN/DDS compression flags.
// See the m_flags member in the crn_comp_params struct, below.
enum crn_comp_flags
{
// Enables perceptual colorspace distance metrics if set.
// Important: Be sure to disable this when compressing non-sRGB colorspace images, like normal maps!
// Default: Set
cCRNCompFlagPerceptual = 1,
// Enables (up to) 8x8 macroblock usage if set. If disabled, only 4x4 blocks are allowed.
// Compression ratio will be lower when disabled, but may cut down on blocky artifacts because the process used to determine
// where large macroblocks can be used without artifacts isn't perfect.
// Default: Set.
cCRNCompFlagHierarchical = 2,
// cCRNCompFlagQuick disables several output file optimizations - intended for things like quicker previews.
// Default: Not set.
cCRNCompFlagQuick = 4,
// DXT1: OK to use DXT1 alpha blocks for better quality or DXT1A transparency.
// DXT5: OK to use both DXT5 block types.
// Currently only used when writing to .DDS files, as .CRN uses only a subset of the possible DXTn block types.
// Default: Set.
cCRNCompFlagUseBothBlockTypes = 8,
// OK to use DXT1A transparent indices to encode black (assumes pixel shader ignores fetched alpha).
// Currently only used when writing to .DDS files, .CRN never uses alpha blocks.
// Default: Not set.
cCRNCompFlagUseTransparentIndicesForBlack = 16,
// Disables endpoint caching, for more deterministic output.
// Currently only used when writing to .DDS files.
// Default: Not set.
cCRNCompFlagDisableEndpointCaching = 32,
// If enabled, use the cCRNColorEndpointPaletteSize, etc. params to control the CRN palette sizes. Only useful when writing to .CRN files.
// Default: Not set.
cCRNCompFlagManualPaletteSizes = 64,
// If enabled, DXT1A alpha blocks are used to encode single bit transparency.
// Default: Not set.
cCRNCompFlagDXT1AForTransparency = 128,
// If enabled, the DXT1 compressor's color distance metric assumes the pixel shader will be converting the fetched RGB results to luma (Y part of YCbCr).
// This increases quality when compressing grayscale images, because the compressor can spread the luma error amoung all three channels (i.e. it can generate blocks
// with some chroma present if doing so will ultimately lead to lower luma error).
// Only enable on grayscale source images.
// Default: Not set.
cCRNCompFlagGrayscaleSampling = 256,
// If enabled, debug information will be output during compression.
// Default: Not set.
cCRNCompFlagDebugging = 0x80000000,
cCRNCompFlagForceDWORD = 0xFFFFFFFF
};
// Controls DXTn quality vs. speed control - only used when compressing to .DDS.
enum crn_dxt_quality
{
cCRNDXTQualitySuperFast,
cCRNDXTQualityFast,
cCRNDXTQualityNormal,
cCRNDXTQualityBetter,
cCRNDXTQualityUber,
cCRNDXTQualityTotal,
cCRNDXTQualityForceDWORD = 0xFFFFFFFF
};
// Which DXTn compressor to use when compressing to plain (non-clustered) .DDS.
enum crn_dxt_compressor_type
{
cCRNDXTCompressorCRN, // Use crnlib's ETC1 or DXTc block compressor (default, highest quality, comparable or better than ati_compress or squish, and crnlib's ETC1 is a lot fasterw with similiar quality to Erricson's)
cCRNDXTCompressorCRNF, // Use crnlib's "fast" DXTc block compressor
cCRNDXTCompressorRYG, // Use RYG's DXTc block compressor (low quality, but very fast)
#if CRNLIB_SUPPORT_ATI_COMPRESS
cCRNDXTCompressorATI,
#endif
#if CRNLIB_SUPPORT_SQUISH
cCRNDXTCompressorSquish,
#endif
cCRNTotalDXTCompressors,
cCRNDXTCompressorForceDWORD = 0xFFFFFFFF
};
// Progress callback function.
// Processing will stop prematurely (and fail) if the callback returns false.
// phase_index, total_phases - high level progress
// subphase_index, total_subphases - progress within current phase
typedef crn_bool (*crn_progress_callback_func)(crn_uint32 phase_index, crn_uint32 total_phases, crn_uint32 subphase_index, crn_uint32 total_subphases, void* pUser_data_ptr);
// CRN/DDS compression parameters struct.
struct crn_comp_params
{
inline crn_comp_params() { clear(); }
// Clear struct to default parameters.
inline void clear()
{
m_size_of_obj = sizeof(*this);
m_file_type = cCRNFileTypeCRN;
m_faces = 1;
m_width = 0;
m_height = 0;
m_levels = 1;
m_format = cCRNFmtDXT1;
m_flags = cCRNCompFlagPerceptual | cCRNCompFlagHierarchical | cCRNCompFlagUseBothBlockTypes;
for (crn_uint32 f = 0; f < cCRNMaxFaces; f++)
for (crn_uint32 l = 0; l < cCRNMaxLevels; l++)
m_pImages[f][l] = NULL;
m_target_bitrate = 0.0f;
m_quality_level = cCRNMaxQualityLevel;
m_dxt1a_alpha_threshold = 128;
m_dxt_quality = cCRNDXTQualityUber;
m_dxt_compressor_type = cCRNDXTCompressorCRN;
m_alpha_component = 3;
m_crn_adaptive_tile_color_psnr_derating = 2.0f;
m_crn_adaptive_tile_alpha_psnr_derating = 2.0f;
m_crn_color_endpoint_palette_size = 0;
m_crn_color_selector_palette_size = 0;
m_crn_alpha_endpoint_palette_size = 0;
m_crn_alpha_selector_palette_size = 0;
m_num_helper_threads = 0;
m_userdata0 = 0;
m_userdata1 = 0;
m_pProgress_func = NULL;
m_pProgress_func_data = NULL;
}
inline bool operator== (const crn_comp_params& rhs) const
{
#define CRNLIB_COMP(x) do { if ((x) != (rhs.x)) return false; } while(0)
CRNLIB_COMP(m_size_of_obj);
CRNLIB_COMP(m_file_type);
CRNLIB_COMP(m_faces);
CRNLIB_COMP(m_width);
CRNLIB_COMP(m_height);
CRNLIB_COMP(m_levels);
CRNLIB_COMP(m_format);
CRNLIB_COMP(m_flags);
CRNLIB_COMP(m_target_bitrate);
CRNLIB_COMP(m_quality_level);
CRNLIB_COMP(m_dxt1a_alpha_threshold);
CRNLIB_COMP(m_dxt_quality);
CRNLIB_COMP(m_dxt_compressor_type);
CRNLIB_COMP(m_alpha_component);
CRNLIB_COMP(m_crn_adaptive_tile_color_psnr_derating);
CRNLIB_COMP(m_crn_adaptive_tile_alpha_psnr_derating);
CRNLIB_COMP(m_crn_color_endpoint_palette_size);
CRNLIB_COMP(m_crn_color_selector_palette_size);
CRNLIB_COMP(m_crn_alpha_endpoint_palette_size);
CRNLIB_COMP(m_crn_alpha_selector_palette_size);
CRNLIB_COMP(m_num_helper_threads);
CRNLIB_COMP(m_userdata0);
CRNLIB_COMP(m_userdata1);
CRNLIB_COMP(m_pProgress_func);
CRNLIB_COMP(m_pProgress_func_data);
for (crn_uint32 f = 0; f < cCRNMaxFaces; f++)
for (crn_uint32 l = 0; l < cCRNMaxLevels; l++)
CRNLIB_COMP(m_pImages[f][l]);
#undef CRNLIB_COMP
return true;
}
// Returns true if the input parameters are reasonable.
inline bool check() const
{
if ( (m_file_type > cCRNFileTypeDDS) ||
(((int)m_quality_level < (int)cCRNMinQualityLevel) || ((int)m_quality_level > (int)cCRNMaxQualityLevel)) ||
(m_dxt1a_alpha_threshold > 255) ||
((m_faces != 1) && (m_faces != 6)) ||
((m_width < 1) || (m_width > cCRNMaxLevelResolution)) ||
((m_height < 1) || (m_height > cCRNMaxLevelResolution)) ||
((m_levels < 1) || (m_levels > cCRNMaxLevels)) ||
((m_format < cCRNFmtDXT1) || (m_format >= cCRNFmtTotal)) ||
((m_crn_color_endpoint_palette_size) && ((m_crn_color_endpoint_palette_size < cCRNMinPaletteSize) || (m_crn_color_endpoint_palette_size > cCRNMaxPaletteSize))) ||
((m_crn_color_selector_palette_size) && ((m_crn_color_selector_palette_size < cCRNMinPaletteSize) || (m_crn_color_selector_palette_size > cCRNMaxPaletteSize))) ||
((m_crn_alpha_endpoint_palette_size) && ((m_crn_alpha_endpoint_palette_size < cCRNMinPaletteSize) || (m_crn_alpha_endpoint_palette_size > cCRNMaxPaletteSize))) ||
((m_crn_alpha_selector_palette_size) && ((m_crn_alpha_selector_palette_size < cCRNMinPaletteSize) || (m_crn_alpha_selector_palette_size > cCRNMaxPaletteSize))) ||
(m_alpha_component > 3) ||
(m_num_helper_threads > cCRNMaxHelperThreads) ||
(m_dxt_quality > cCRNDXTQualityUber) ||
(m_dxt_compressor_type >= cCRNTotalDXTCompressors) )
{
return false;
}
return true;
}
// Helper to set/get flags from m_flags member.
inline bool get_flag(crn_comp_flags flag) const { return (m_flags & flag) != 0; }
inline void set_flag(crn_comp_flags flag, bool val) { m_flags &= ~flag; if (val) m_flags |= flag; }
crn_uint32 m_size_of_obj;
crn_file_type m_file_type; // Output file type: cCRNFileTypeCRN or cCRNFileTypeDDS.
crn_uint32 m_faces; // 1 (2D map) or 6 (cubemap)
crn_uint32 m_width; // [1,cCRNMaxLevelResolution], non-power of 2 OK, non-square OK
crn_uint32 m_height; // [1,cCRNMaxLevelResolution], non-power of 2 OK, non-square OK
crn_uint32 m_levels; // [1,cCRNMaxLevelResolution], non-power of 2 OK, non-square OK
crn_format m_format; // Output pixel format.
crn_uint32 m_flags; // see crn_comp_flags enum
// Array of pointers to 32bpp input images.
const crn_uint32* m_pImages[cCRNMaxFaces][cCRNMaxLevels];
// Target bitrate - if non-zero, the compressor will use an interpolative search to find the
// highest quality level that is <= the target bitrate. If it fails to find a bitrate high enough, it'll
// try disabling adaptive block sizes (cCRNCompFlagHierarchical flag) and redo the search. This process can be pretty slow.
float m_target_bitrate;
// Desired quality level.
// Currently, CRN and DDS quality levels are not compatible with eachother from an image quality standpoint.
crn_uint32 m_quality_level; // [cCRNMinQualityLevel, cCRNMaxQualityLevel]
// DXTn compression parameters.
crn_uint32 m_dxt1a_alpha_threshold;
crn_dxt_quality m_dxt_quality;
crn_dxt_compressor_type m_dxt_compressor_type;
// Alpha channel's component. Defaults to 3.
crn_uint32 m_alpha_component;
// Various low-level CRN specific parameters.
float m_crn_adaptive_tile_color_psnr_derating;
float m_crn_adaptive_tile_alpha_psnr_derating;
crn_uint32 m_crn_color_endpoint_palette_size; // [cCRNMinPaletteSize,cCRNMaxPaletteSize]
crn_uint32 m_crn_color_selector_palette_size; // [cCRNMinPaletteSize,cCRNMaxPaletteSize]
crn_uint32 m_crn_alpha_endpoint_palette_size; // [cCRNMinPaletteSize,cCRNMaxPaletteSize]
crn_uint32 m_crn_alpha_selector_palette_size; // [cCRNMinPaletteSize,cCRNMaxPaletteSize]
// Number of helper threads to create during compression. 0=no threading.
crn_uint32 m_num_helper_threads;
// CRN userdata0 and userdata1 members, which are written directly to the header of the output file.
crn_uint32 m_userdata0;
crn_uint32 m_userdata1;
// User provided progress callback.
crn_progress_callback_func m_pProgress_func;
void* m_pProgress_func_data;
};
// Mipmap generator's mode.
enum crn_mip_mode
{
cCRNMipModeUseSourceOrGenerateMips, // Use source texture's mipmaps if it has any, otherwise generate new mipmaps
cCRNMipModeUseSourceMips, // Use source texture's mipmaps if it has any, otherwise the output has no mipmaps
cCRNMipModeGenerateMips, // Always generate new mipmaps
cCRNMipModeNoMips, // Output texture has no mipmaps
cCRNMipModeTotal,
cCRNModeForceDWORD = 0xFFFFFFFF
};
const char* crn_get_mip_mode_desc(crn_mip_mode m);
const char* crn_get_mip_mode_name(crn_mip_mode m);
// Mipmap generator's filter kernel.
enum crn_mip_filter
{
cCRNMipFilterBox,
cCRNMipFilterTent,
cCRNMipFilterLanczos4,
cCRNMipFilterMitchell,
cCRNMipFilterKaiser, // Kaiser=default mipmap filter
cCRNMipFilterTotal,
cCRNMipFilterForceDWORD = 0xFFFFFFFF
};
const char* crn_get_mip_filter_name(crn_mip_filter f);
// Mipmap generator's scale mode.
enum crn_scale_mode
{
cCRNSMDisabled,
cCRNSMAbsolute,
cCRNSMRelative,
cCRNSMLowerPow2,
cCRNSMNearestPow2,
cCRNSMNextPow2,
cCRNSMTotal,
cCRNSMForceDWORD = 0xFFFFFFFF
};
const char* crn_get_scale_mode_desc(crn_scale_mode sm);
// Mipmap generator parameters.
struct crn_mipmap_params
{
inline crn_mipmap_params() { clear(); }
inline void clear()
{
m_size_of_obj = sizeof(*this);
m_mode = cCRNMipModeUseSourceOrGenerateMips;
m_filter = cCRNMipFilterKaiser;
m_gamma_filtering = true;
m_gamma = 2.2f;
// Default "blurriness" factor of .9 actually sharpens the output a little.
m_blurriness = .9f;
m_renormalize = false;
m_tiled = false;
m_max_levels = cCRNMaxLevels;
m_min_mip_size = 1;
m_scale_mode = cCRNSMDisabled;
m_scale_x = 1.0f;
m_scale_y = 1.0f;
m_window_left = 0;
m_window_top = 0;
m_window_right = 0;
m_window_bottom = 0;
m_clamp_scale = false;
m_clamp_width = 0;
m_clamp_height = 0;
}
inline bool check() const { return true; }
inline bool operator== (const crn_mipmap_params& rhs) const
{
#define CRNLIB_COMP(x) do { if ((x) != (rhs.x)) return false; } while(0)
CRNLIB_COMP(m_size_of_obj);
CRNLIB_COMP(m_mode);
CRNLIB_COMP(m_filter);
CRNLIB_COMP(m_gamma_filtering);
CRNLIB_COMP(m_gamma);
CRNLIB_COMP(m_blurriness);
CRNLIB_COMP(m_renormalize);
CRNLIB_COMP(m_tiled);
CRNLIB_COMP(m_max_levels);
CRNLIB_COMP(m_min_mip_size);
CRNLIB_COMP(m_scale_mode);
CRNLIB_COMP(m_scale_x);
CRNLIB_COMP(m_scale_y);
CRNLIB_COMP(m_window_left);
CRNLIB_COMP(m_window_top);
CRNLIB_COMP(m_window_right);
CRNLIB_COMP(m_window_bottom);
CRNLIB_COMP(m_clamp_scale);
CRNLIB_COMP(m_clamp_width);
CRNLIB_COMP(m_clamp_height);
return true;
#undef CRNLIB_COMP
}
crn_uint32 m_size_of_obj;
crn_mip_mode m_mode;
crn_mip_filter m_filter;
crn_bool m_gamma_filtering;
float m_gamma;
float m_blurriness;
crn_uint32 m_max_levels;
crn_uint32 m_min_mip_size;
crn_bool m_renormalize;
crn_bool m_tiled;
crn_scale_mode m_scale_mode;
float m_scale_x;
float m_scale_y;
crn_uint32 m_window_left;
crn_uint32 m_window_top;
crn_uint32 m_window_right;
crn_uint32 m_window_bottom;
crn_bool m_clamp_scale;
crn_uint32 m_clamp_width;
crn_uint32 m_clamp_height;
};
// -------- High-level helper function definitions for CDN/DDS compression.
#ifndef CRNLIB_MIN_ALLOC_ALIGNMENT
#define CRNLIB_MIN_ALLOC_ALIGNMENT sizeof(size_t) * 2
#endif
// Function to set an optional user provided memory allocation/reallocation/msize routines.
// By default, crnlib just uses malloc(), free(), etc. for all allocations.
typedef void* (*crn_realloc_func)(void* p, size_t size, size_t* pActual_size, bool movable, void* pUser_data);
typedef size_t (*crn_msize_func)(void* p, void* pUser_data);
void crn_set_memory_callbacks(crn_realloc_func pRealloc, crn_msize_func pMSize, void* pUser_data);
// Frees memory blocks allocated by crn_compress(), crn_decompress_crn_to_dds(), or crn_decompress_dds_to_images().
void crn_free_block(void *pBlock);
// Compresses a 32-bit/pixel texture to either: a regular DX9 DDS file, a "clustered" (or reduced entropy) DX9 DDS file, or a CRN file in memory.
// Input parameters:
// comp_params is the compression parameters struct, defined above.
// compressed_size will be set to the size of the returned memory block containing the output file.
// The returned block must be freed by calling crn_free_block().
// *pActual_quality_level will be set to the actual quality level used to compress the image. May be NULL.
// *pActual_bitrate will be set to the output file's effective bitrate, possibly taking into account LZMA compression. May be NULL.
// Return value:
// The compressed file data, or NULL on failure.
// compressed_size will be set to the size of the returned memory buffer.
// Notes:
// A "regular" DDS file is compressed using normal DXTn compression at the specified DXT quality level.
// A "clustered" DDS file is compressed using clustered DXTn compression to either the target bitrate or the specified integer quality factor.
// The output file is a standard DX9 format DDS file, except the compressor assumes you will be later losslessly compressing the DDS output file using the LZMA algorithm.
// A texture is defined as an array of 1 or 6 "faces" (6 faces=cubemap), where each "face" consists of between [1,cCRNMaxLevels] mipmap levels.
// Mipmap levels are simple 32-bit 2D images with a pitch of width*sizeof(uint32), arranged in the usual raster order (top scanline first).
// The image pixels may be grayscale (YYYX bytes in memory), grayscale/alpha (YYYA in memory), 24-bit (RGBX in memory), or 32-bit (RGBA) colors (where "X"=don't care).
// RGB color data is generally assumed to be in the sRGB colorspace. If not, be sure to clear the "cCRNCompFlagPerceptual" in the crn_comp_params struct!
void *crn_compress(const crn_comp_params &comp_params, crn_uint32 &compressed_size, crn_uint32 *pActual_quality_level = NULL, float *pActual_bitrate = NULL);
// Like the above function, except this function can also do things like generate mipmaps, and resize or crop the input texture before compression.
// The actual operations performed are controlled by the crn_mipmap_params struct members.
// Be sure to set the "m_gamma_filtering" member of crn_mipmap_params to false if the input texture is not sRGB.
void *crn_compress(const crn_comp_params &comp_params, const crn_mipmap_params &mip_params, crn_uint32 &compressed_size, crn_uint32 *pActual_quality_level = NULL, float *pActual_bitrate = NULL);
// Transcodes an entire CRN file to DDS using the crn_decomp.h header file library to do most of the heavy lifting.
// The output DDS file's format is guaranteed to be one of the DXTn formats in the crn_format enum.
// This is a fast operation, because the CRN format is explicitly designed to be efficiently transcodable to DXTn.
// For more control over decompression, see the lower-level helper functions in crn_decomp.h, which do not depend at all on crnlib.
void *crn_decompress_crn_to_dds(const void *pCRN_file_data, crn_uint32 &file_size);
// Decompresses an entire DDS file in any supported format to uncompressed 32-bit/pixel image(s).
// See the crnlib::pixel_format enum in inc/dds_defs.h for a list of the supported DDS formats.
// You are responsible for freeing each image block, either by calling crn_free_all_images() or manually calling crn_free_block() on each image pointer.
struct crn_texture_desc
{
crn_uint32 m_faces;
crn_uint32 m_width;
crn_uint32 m_height;
crn_uint32 m_levels;
crn_uint32 m_fmt_fourcc; // Same as crnlib::pixel_format
};
bool crn_decompress_dds_to_images(const void *pDDS_file_data, crn_uint32 dds_file_size, crn_uint32 **ppImages, crn_texture_desc &tex_desc);
// Frees all images allocated by crn_decompress_dds_to_images().
void crn_free_all_images(crn_uint32 **ppImages, const crn_texture_desc &desc);
// -------- crn_format related helpers functions.
// Returns the FOURCC format equivalent to the specified crn_format.
crn_uint32 crn_get_format_fourcc(crn_format fmt);
// Returns the crn_format's bits per texel.
crn_uint32 crn_get_format_bits_per_texel(crn_format fmt);
// Returns the crn_format's number of bytes per block.
crn_uint32 crn_get_bytes_per_dxt_block(crn_format fmt);
// Returns the non-swizzled, basic DXTn version of the specified crn_format.
// This is the format you would supply D3D or OpenGL.
crn_format crn_get_fundamental_dxt_format(crn_format fmt);
// -------- String helpers.
// Converts a crn_file_type to a string.
const char* crn_get_file_type_ext(crn_file_type file_type);
// Converts a crn_format to a string.
const char* crn_get_format_string(crn_format fmt);
// Converts a crn_dxt_quality to a string.
const char* crn_get_dxt_quality_string(crn_dxt_quality q);
// -------- Low-level DXTn 4x4 block compressor API
// crnlib's DXTn endpoint optimizer actually supports any number of source pixels (i.e. from 1 to thousands, not just 16),
// but for simplicity this API only supports 4x4 texel blocks.
typedef void *crn_block_compressor_context_t;
// Create a DXTn block compressor.
// This function only supports the basic/nonswizzled "fundamental" formats: DXT1, DXT3, DXT5, DXT5A, DXN_XY and DXN_YX.
// Avoid calling this multiple times if you intend on compressing many blocks, because it allocates some memory.
crn_block_compressor_context_t crn_create_block_compressor(const crn_comp_params &params);
// Compresses a block of 16 pixels to the destination DXTn block.
// pDst_block should be 8 (for DXT1/DXT5A) or 16 bytes (all the others).
// pPixels should be an array of 16 crn_uint32's. Each crn_uint32 must be r,g,b,a (r is always first) in memory.
void crn_compress_block(crn_block_compressor_context_t pContext, const crn_uint32 *pPixels, void *pDst_block);
// Frees a DXTn block compressor.
void crn_free_block_compressor(crn_block_compressor_context_t pContext);
// Unpacks a compressed block to pDst_pixels.
// pSrc_block should be 8 (for DXT1/DXT5A) or 16 bytes (all the others).
// pDst_pixel should be an array of 16 crn_uint32's. Each uint32 will be r,g,b,a (r is always first) in memory.
// crn_fmt should be one of the "fundamental" formats: DXT1, DXT3, DXT5, DXT5A, DXN_XY and DXN_YX.
// The various swizzled DXT5 formats (such as cCRNFmtDXT5_xGBR, etc.) will be unpacked as if they where plain DXT5.
// Returns false if the crn_fmt is invalid.
bool crn_decompress_block(const void *pSrc_block, crn_uint32 *pDst_pixels, crn_format crn_fmt);
#endif // CRNLIB_H
//------------------------------------------------------------------------------
//
// crunch/crnlib uses a modified ZLIB license. Specifically, it's the same as zlib except that
// public credits for using the library are *required*.
//
// Copyright (c) 2010-2016 Richard Geldreich, Jr. All rights reserved.
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
// 2. If you use this software in a product, this acknowledgment in the product
// documentation or credits is required:
//
// "Crunch Library Copyright (c) 2010-2016 Richard Geldreich, Jr."
//
// 3. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 4. This notice may not be removed or altered from any source distribution.
//
//------------------------------------------------------------------------------

View File

@ -0,0 +1,180 @@
/*
*
* License Information
*
* endianness.h is derived from https://gist.github.com/jtbr/7a43e6281e6cca353b33ee501421860c
* The file is licensed under the MIT License shown below.
*
*
* The MIT License (MIT)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#ifndef _ENDIANNESS_H
#define _ENDIANNESS_H
#include <stdlib.h>
#include <stdint.h>
/* Detect platform endianness at compile time */
// If boost were available on all platforms, could use this instead to detect endianness
// #include <boost/predef/endian.h>
// When available, these headers can improve platform endianness detection
#ifdef __has_include // C++17, supported as extension to C++11 in clang, GCC 5+, vs2015
#if __has_include(<endian.h>)
#include <endian.h> // gnu libc normally provides, linux
#elif __has_include(<machine/endian.h>)
#include <machine/endian.h> //open bsd, macos
#elif __has_include(<sys/param.h>)
#include <sys/param.h> // mingw, some bsd (not open/macos)
#elif __has_include(<sys/isadefs.h>)
#include <sys/isadefs.h> // solaris
#endif
#endif
#if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)
#if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \
(defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN) || (defined(_BYTE_ORDER) && _BYTE_ORDER == _BIG_ENDIAN) || \
(defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN) || (defined(__sun) && defined(__SVR4) && defined(_BIG_ENDIAN)) || \
defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || defined(_MIBSEB) || defined(__MIBSEB) || \
defined(__MIBSEB__) || defined(_M_PPC)
#define __BIG_ENDIAN__
#elif (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || /* gcc */ \
(defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN) /* linux header */ || \
(defined(_BYTE_ORDER) && _BYTE_ORDER == _LITTLE_ENDIAN) || \
(defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN) /* mingw header */ || \
(defined(__sun) && defined(__SVR4) && defined(_LITTLE_ENDIAN)) || /* solaris */ \
defined(__ARMEL__) || defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || defined(__MIPSEL) || \
defined(__MIPSEL__) || defined(_M_IX86) || defined(_M_X64) || defined(_M_IA64) || /* msvc for intel processors */ \
defined(_M_ARM) /* msvc code on arm executes in little endian mode */
#define __LITTLE_ENDIAN__
#endif
#endif
#ifdef bswap16
#undef bswap16
#endif
#ifdef bswap32
#undef bswap32
#endif
#ifdef bswap64
#undef bswap64
#endif
/* Define byte-swap functions, using fast processor-native built-ins where possible */
// needs to be first because msvc doesn't short-circuit after failing defined(__has_builtin)
#if defined(_MSC_VER)
#define bswap16(x) _byteswap_ushort((x))
#define bswap32(x) _byteswap_ulong((x))
#define bswap64(x) _byteswap_uint64((x))
#elif (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
#define bswap16(x) __builtin_bswap16((x))
#define bswap32(x) __builtin_bswap32((x))
#define bswap64(x) __builtin_bswap64((x))
#elif defined(__has_builtin) && __has_builtin(__builtin_bswap64)
/* for clang; gcc 5 fails on this and && shortcircuit fails; must be after GCC check */
#define bswap16(x) __builtin_bswap16((x))
#define bswap32(x) __builtin_bswap32((x))
#define bswap64(x) __builtin_bswap64((x))
#else
/* even in this case, compilers often optimize by using native instructions */
static inline uint16_t bswap16(uint16_t x) {
return (((x >> 8) & 0xffu) | ((x & 0xffu) << 8));
}
static inline uint32_t bswap32(uint32_t x) {
return (((x & 0xff000000u) >> 24) | ((x & 0x00ff0000u) >> 8) | ((x & 0x0000ff00u) << 8) |
((x & 0x000000ffu) << 24));
}
static inline uint64_t bswap64(uint64_t x) {
return (((x & 0xff00000000000000ull) >> 56) | ((x & 0x00ff000000000000ull) >> 40) |
((x & 0x0000ff0000000000ull) >> 24) | ((x & 0x000000ff00000000ull) >> 8) |
((x & 0x00000000ff000000ull) << 8) | ((x & 0x0000000000ff0000ull) << 24) |
((x & 0x000000000000ff00ull) << 40) | ((x & 0x00000000000000ffull) << 56));
}
#endif
/* Defines network - host byte swaps as needed depending upon platform endianness */
// note that network order is big endian)
#if defined(__LITTLE_ENDIAN__)
#define ntoh16(x) bswap16((x))
#define hton16(x) bswap16((x))
#define ntoh32(x) bswap32((x))
#define hton32(x) bswap32((x))
#define ntoh64(x) bswap64((x))
#define hton64(x) bswap64((x))
#define lton16(x) (x)
#define lton32(x) (x)
#define lton64(x) (x)
#define ltonf(x) (x)
#define ltond(x) (x)
#define bton16(x) bswap16((x))
#define bton32(x) bswap32((x))
#define bton64(x) bswap64((x))
#define btonf(x) htonf((x))
#define btond(x) htond((x))
#elif defined(__BIG_ENDIAN__)
#define ntoh16(x) (x)
#define hton16(x) (x)
#define ntoh32(x) (x)
#define hton32(x) (x)
#define ntoh64(x) (x)
#define hton64(x) (x)
#define bton16(x) (x)
#define bton32(x) (x)
#define bton64(x) (x)
#define btonf(x) (x)
#define btond(x) (x)
#define lton16(x) bswap16((x))
#define lton32(x) bswap32((x))
#define lton64(x) bswap64((x))
#define ltonf(x) htonf((x))
#define ltond(x) htond((x))
#else
#warning "UNKNOWN Platform / endianness; network / host byte swaps not defined."
#endif
//! Convert 32-bit float from host to network byte order
static inline float htonf(float f) {
#ifdef __cplusplus
static_assert(sizeof(float) == sizeof(uint32_t), "Unexpected float format");
uint32_t val = hton32(*(reinterpret_cast<const uint32_t *>(&f)));
return *(reinterpret_cast<float *>(&val));
#else
uint32_t val = hton32(*(const uint32_t *)(&f));
return *((float *)(&val));
#endif
}
#define ntohf(x) htonf((x))
//! Convert 64-bit double from host to network byte order
static inline double htond(double f) {
#ifdef __cplusplus
static_assert(sizeof(double) == sizeof(uint64_t), "Unexpected double format");
uint64_t val = hton64(*(reinterpret_cast<const uint64_t *>(&f)));
return *(reinterpret_cast<double *>(&val));
#else
uint64_t val = hton64(*(const uint64_t *)(&f));
return *((double *)(&val));
#endif
}
#define ntohd(x) htond((x))
#endif //_ENDIANNESS_H

443
Texture2DDecoder/etc.cpp Normal file
View File

@ -0,0 +1,443 @@
#include "etc.h"
#include <stdint.h>
#include <string.h>
#include "color.h"
const uint_fast8_t WriteOrderTable[16] = {0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15};
const uint_fast8_t WriteOrderTableRev[16] = {15, 11, 7, 3, 14, 10, 6, 2, 13, 9, 5, 1, 12, 8, 4, 0};
const uint_fast8_t Etc1ModifierTable[8][2] = {{2, 8}, {5, 17}, {9, 29}, {13, 42},
{18, 60}, {24, 80}, {33, 106}, {47, 183}};
const uint_fast8_t Etc2aModifierTable[2][8][2] = {
{{0, 8}, {0, 17}, {0, 29}, {0, 42}, {0, 60}, {0, 80}, {0, 106}, {0, 183}},
{{2, 8}, {5, 17}, {9, 29}, {13, 42}, {18, 60}, {24, 80}, {33, 106}, {47, 183}}};
const uint_fast8_t Etc1SubblockTable[2][16] = {{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1},
{0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1}};
const uint_fast8_t Etc2DistanceTable[8] = {3, 6, 11, 16, 23, 32, 41, 64};
const int_fast8_t Etc2AlphaModTable[16][8] = {
{-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}};
static inline uint_fast8_t clamp(const int n) {
return n < 0 ? 0 : n > 255 ? 255 : n;
}
static inline uint32_t applicate_color(uint_fast8_t c[3], int_fast16_t m) {
return color(clamp(c[0] + m), clamp(c[1] + m), clamp(c[2] + m), 255);
}
static inline uint32_t applicate_color_alpha(uint_fast8_t c[3], int_fast16_t m, int transparent) {
return color(clamp(c[0] + m), clamp(c[1] + m), clamp(c[2] + m), transparent ? 0 : 255);
}
static inline uint32_t applicate_color_raw(uint_fast8_t c[3]) {
return color(c[0], c[1], c[2], 255);
}
static void decode_etc1_block(const uint8_t *data, uint32_t *outbuf) {
const uint_fast8_t code[2] = {data[3] >> 5, data[3] >> 2 & 7}; // Table codewords
const uint_fast8_t *table = Etc1SubblockTable[data[3] & 1];
uint_fast8_t c[2][3];
if (data[3] & 2) {
// diff bit == 1
c[0][0] = data[0] & 0xf8;
c[0][1] = data[1] & 0xf8;
c[0][2] = data[2] & 0xf8;
c[1][0] = c[0][0] + (data[0] << 3 & 0x18) - (data[0] << 3 & 0x20);
c[1][1] = c[0][1] + (data[1] << 3 & 0x18) - (data[1] << 3 & 0x20);
c[1][2] = c[0][2] + (data[2] << 3 & 0x18) - (data[2] << 3 & 0x20);
c[0][0] |= c[0][0] >> 5;
c[0][1] |= c[0][1] >> 5;
c[0][2] |= c[0][2] >> 5;
c[1][0] |= c[1][0] >> 5;
c[1][1] |= c[1][1] >> 5;
c[1][2] |= c[1][2] >> 5;
} else {
// diff bit == 0
c[0][0] = (data[0] & 0xf0) | data[0] >> 4;
c[1][0] = (data[0] & 0x0f) | data[0] << 4;
c[0][1] = (data[1] & 0xf0) | data[1] >> 4;
c[1][1] = (data[1] & 0x0f) | data[1] << 4;
c[0][2] = (data[2] & 0xf0) | data[2] >> 4;
c[1][2] = (data[2] & 0x0f) | data[2] << 4;
}
uint_fast16_t j = data[6] << 8 | data[7]; // less significant pixel index bits
uint_fast16_t k = data[4] << 8 | data[5]; // more significant pixel index bits
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1) {
uint_fast8_t s = table[i];
uint_fast8_t m = Etc1ModifierTable[code[s]][j & 1];
outbuf[WriteOrderTable[i]] = applicate_color(c[s], k & 1 ? -m : m);
}
}
static void decode_etc2_block(const uint8_t *data, uint32_t *outbuf) {
uint_fast16_t j = data[6] << 8 | data[7]; // 15 -> 0
uint_fast32_t k = data[4] << 8 | data[5]; // 31 -> 16
uint_fast8_t c[3][3] = {};
if (data[3] & 2) {
// diff bit == 1
uint_fast8_t r = data[0] & 0xf8;
int_fast16_t dr = (data[0] << 3 & 0x18) - (data[0] << 3 & 0x20);
uint_fast8_t g = data[1] & 0xf8;
int_fast16_t dg = (data[1] << 3 & 0x18) - (data[1] << 3 & 0x20);
uint_fast8_t b = data[2] & 0xf8;
int_fast16_t db = (data[2] << 3 & 0x18) - (data[2] << 3 & 0x20);
if (r + dr < 0 || r + dr > 255) {
// T
c[0][0] = (data[0] << 3 & 0xc0) | (data[0] << 4 & 0x30) | (data[0] >> 1 & 0xc) | (data[0] & 3);
c[0][1] = (data[1] & 0xf0) | data[1] >> 4;
c[0][2] = (data[1] & 0x0f) | data[1] << 4;
c[1][0] = (data[2] & 0xf0) | data[2] >> 4;
c[1][1] = (data[2] & 0x0f) | data[2] << 4;
c[1][2] = (data[3] & 0xf0) | data[3] >> 4;
const uint_fast8_t d = Etc2DistanceTable[(data[3] >> 1 & 6) | (data[3] & 1)];
uint_fast32_t color_set[4] = {applicate_color_raw(c[0]), applicate_color(c[1], d),
applicate_color_raw(c[1]), applicate_color(c[1], -d)};
k <<= 1;
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1)
outbuf[WriteOrderTable[i]] = color_set[(k & 2) | (j & 1)];
} else if (g + dg < 0 || g + dg > 255) {
// H
c[0][0] = (data[0] << 1 & 0xf0) | (data[0] >> 3 & 0xf);
c[0][1] = (data[0] << 5 & 0xe0) | (data[1] & 0x10);
c[0][1] |= c[0][1] >> 4;
c[0][2] = (data[1] & 8) | (data[1] << 1 & 6) | data[2] >> 7;
c[0][2] |= c[0][2] << 4;
c[1][0] = (data[2] << 1 & 0xf0) | (data[2] >> 3 & 0xf);
c[1][1] = (data[2] << 5 & 0xe0) | (data[3] >> 3 & 0x10);
c[1][1] |= c[1][1] >> 4;
c[1][2] = (data[3] << 1 & 0xf0) | (data[3] >> 3 & 0xf);
uint_fast8_t d = (data[3] & 4) | (data[3] << 1 & 2);
if (c[0][0] > c[1][0] ||
(c[0][0] == c[1][0] && (c[0][1] > c[1][1] || (c[0][1] == c[1][1] && c[0][2] >= c[1][2]))))
++d;
d = Etc2DistanceTable[d];
uint_fast32_t color_set[4] = {applicate_color(c[0], d), applicate_color(c[0], -d), applicate_color(c[1], d),
applicate_color(c[1], -d)};
k <<= 1;
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1)
outbuf[WriteOrderTable[i]] = color_set[(k & 2) | (j & 1)];
} else if (b + db < 0 || b + db > 255) {
// planar
c[0][0] = (data[0] << 1 & 0xfc) | (data[0] >> 5 & 3);
c[0][1] = (data[0] << 7 & 0x80) | (data[1] & 0x7e) | (data[0] & 1);
c[0][2] = (data[1] << 7 & 0x80) | (data[2] << 2 & 0x60) | (data[2] << 3 & 0x18) | (data[3] >> 5 & 4);
c[0][2] |= c[0][2] >> 6;
c[1][0] = (data[3] << 1 & 0xf8) | (data[3] << 2 & 4) | (data[3] >> 5 & 3);
c[1][1] = (data[4] & 0xfe) | data[4] >> 7;
c[1][2] = (data[4] << 7 & 0x80) | (data[5] >> 1 & 0x7c);
c[1][2] |= c[1][2] >> 6;
c[2][0] = (data[5] << 5 & 0xe0) | (data[6] >> 3 & 0x1c) | (data[5] >> 1 & 3);
c[2][1] = (data[6] << 3 & 0xf8) | (data[7] >> 5 & 0x6) | (data[6] >> 4 & 1);
c[2][2] = data[7] << 2 | (data[7] >> 4 & 3);
for (int y = 0, i = 0; y < 4; y++) {
for (int x = 0; x < 4; x++, i++) {
uint8_t r = clamp((x * (c[1][0] - c[0][0]) + y * (c[2][0] - c[0][0]) + 4 * c[0][0] + 2) >> 2);
uint8_t g = clamp((x * (c[1][1] - c[0][1]) + y * (c[2][1] - c[0][1]) + 4 * c[0][1] + 2) >> 2);
uint8_t b = clamp((x * (c[1][2] - c[0][2]) + y * (c[2][2] - c[0][2]) + 4 * c[0][2] + 2) >> 2);
outbuf[i] = color(r, g, b, 255);
}
}
} else {
// differential
const uint_fast8_t code[2] = {data[3] >> 5, data[3] >> 2 & 7};
const uint_fast8_t *table = Etc1SubblockTable[data[3] & 1];
c[0][0] = r | r >> 5;
c[0][1] = g | g >> 5;
c[0][2] = b | b >> 5;
c[1][0] = r + dr;
c[1][1] = g + dg;
c[1][2] = b + db;
c[1][0] |= c[1][0] >> 5;
c[1][1] |= c[1][1] >> 5;
c[1][2] |= c[1][2] >> 5;
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1) {
uint_fast8_t s = table[i];
uint_fast8_t m = Etc1ModifierTable[code[s]][j & 1];
outbuf[WriteOrderTable[i]] = applicate_color(c[s], k & 1 ? -m : m);
}
}
} else {
// individual (diff bit == 0)
const uint_fast8_t code[2] = {data[3] >> 5, data[3] >> 2 & 7};
const uint_fast8_t *table = Etc1SubblockTable[data[3] & 1];
c[0][0] = (data[0] & 0xf0) | data[0] >> 4;
c[1][0] = (data[0] & 0x0f) | data[0] << 4;
c[0][1] = (data[1] & 0xf0) | data[1] >> 4;
c[1][1] = (data[1] & 0x0f) | data[1] << 4;
c[0][2] = (data[2] & 0xf0) | data[2] >> 4;
c[1][2] = (data[2] & 0x0f) | data[2] << 4;
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1) {
uint_fast8_t s = table[i];
uint_fast8_t m = Etc1ModifierTable[code[s]][j & 1];
outbuf[WriteOrderTable[i]] = applicate_color(c[s], k & 1 ? -m : m);
}
}
}
static void decode_etc2a1_block(const uint8_t *data, uint32_t *outbuf) {
uint_fast16_t j = data[6] << 8 | data[7]; // 15 -> 0
uint_fast32_t k = data[4] << 8 | data[5]; // 31 -> 16
uint_fast8_t c[3][3] = {};
int obaq = data[3] >> 1 & 1;
// diff bit == 1
uint_fast8_t r = data[0] & 0xf8;
int_fast16_t dr = (data[0] << 3 & 0x18) - (data[0] << 3 & 0x20);
uint_fast8_t g = data[1] & 0xf8;
int_fast16_t dg = (data[1] << 3 & 0x18) - (data[1] << 3 & 0x20);
uint_fast8_t b = data[2] & 0xf8;
int_fast16_t db = (data[2] << 3 & 0x18) - (data[2] << 3 & 0x20);
if (r + dr < 0 || r + dr > 255) {
// T
c[0][0] = (data[0] << 3 & 0xc0) | (data[0] << 4 & 0x30) | (data[0] >> 1 & 0xc) | (data[0] & 3);
c[0][1] = (data[1] & 0xf0) | data[1] >> 4;
c[0][2] = (data[1] & 0x0f) | data[1] << 4;
c[1][0] = (data[2] & 0xf0) | data[2] >> 4;
c[1][1] = (data[2] & 0x0f) | data[2] << 4;
c[1][2] = (data[3] & 0xf0) | data[3] >> 4;
const uint_fast8_t d = Etc2DistanceTable[(data[3] >> 1 & 6) | (data[3] & 1)];
uint_fast32_t color_set[4] = {applicate_color_raw(c[0]), applicate_color(c[1], d), applicate_color_raw(c[1]),
applicate_color(c[1], -d)};
k <<= 1;
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1) {
int index = (k & 2) | (j & 1);
outbuf[WriteOrderTable[i]] = color_set[index];
if (!obaq && index == 2)
outbuf[WriteOrderTable[i]] &= TRANSPARENT_MASK;
}
} else if (g + dg < 0 || g + dg > 255) {
// H
c[0][0] = (data[0] << 1 & 0xf0) | (data[0] >> 3 & 0xf);
c[0][1] = (data[0] << 5 & 0xe0) | (data[1] & 0x10);
c[0][1] |= c[0][1] >> 4;
c[0][2] = (data[1] & 8) | (data[1] << 1 & 6) | data[2] >> 7;
c[0][2] |= c[0][2] << 4;
c[1][0] = (data[2] << 1 & 0xf0) | (data[2] >> 3 & 0xf);
c[1][1] = (data[2] << 5 & 0xe0) | (data[3] >> 3 & 0x10);
c[1][1] |= c[1][1] >> 4;
c[1][2] = (data[3] << 1 & 0xf0) | (data[3] >> 3 & 0xf);
uint_fast8_t d = (data[3] & 4) | (data[3] << 1 & 2);
if (c[0][0] > c[1][0] ||
(c[0][0] == c[1][0] && (c[0][1] > c[1][1] || (c[0][1] == c[1][1] && c[0][2] >= c[1][2]))))
++d;
d = Etc2DistanceTable[d];
uint_fast32_t color_set[4] = {applicate_color(c[0], d), applicate_color(c[0], -d), applicate_color(c[1], d),
applicate_color(c[1], -d)};
k <<= 1;
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1) {
int index = (k & 2) | (j & 1);
outbuf[WriteOrderTable[i]] = color_set[index];
if (!obaq && index == 2)
outbuf[WriteOrderTable[i]] &= TRANSPARENT_MASK;
}
} else if (b + db < 0 || b + db > 255) {
// planar
c[0][0] = (data[0] << 1 & 0xfc) | (data[0] >> 5 & 3);
c[0][1] = (data[0] << 7 & 0x80) | (data[1] & 0x7e) | (data[0] & 1);
c[0][2] = (data[1] << 7 & 0x80) | (data[2] << 2 & 0x60) | (data[2] << 3 & 0x18) | (data[3] >> 5 & 4);
c[0][2] |= c[0][2] >> 6;
c[1][0] = (data[3] << 1 & 0xf8) | (data[3] << 2 & 4) | (data[3] >> 5 & 3);
c[1][1] = (data[4] & 0xfe) | data[4] >> 7;
c[1][2] = (data[4] << 7 & 0x80) | (data[5] >> 1 & 0x7c);
c[1][2] |= c[1][2] >> 6;
c[2][0] = (data[5] << 5 & 0xe0) | (data[6] >> 3 & 0x1c) | (data[5] >> 1 & 3);
c[2][1] = (data[6] << 3 & 0xf8) | (data[7] >> 5 & 0x6) | (data[6] >> 4 & 1);
c[2][2] = data[7] << 2 | (data[7] >> 4 & 3);
for (int y = 0, i = 0; y < 4; y++) {
for (int x = 0; x < 4; x++, i++) {
uint8_t r = clamp((x * (c[1][0] - c[0][0]) + y * (c[2][0] - c[0][0]) + 4 * c[0][0] + 2) >> 2);
uint8_t g = clamp((x * (c[1][1] - c[0][1]) + y * (c[2][1] - c[0][1]) + 4 * c[0][1] + 2) >> 2);
uint8_t b = clamp((x * (c[1][2] - c[0][2]) + y * (c[2][2] - c[0][2]) + 4 * c[0][2] + 2) >> 2);
outbuf[i] = color(r, g, b, 255);
}
}
} else {
// differential
const uint_fast8_t code[2] = {data[3] >> 5, data[3] >> 2 & 7};
const uint_fast8_t *table = Etc1SubblockTable[data[3] & 1];
c[0][0] = r | r >> 5;
c[0][1] = g | g >> 5;
c[0][2] = b | b >> 5;
c[1][0] = r + dr;
c[1][1] = g + dg;
c[1][2] = b + db;
c[1][0] |= c[1][0] >> 5;
c[1][1] |= c[1][1] >> 5;
c[1][2] |= c[1][2] >> 5;
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1) {
uint_fast8_t s = table[i];
uint_fast8_t m = Etc2aModifierTable[obaq][code[s]][j & 1];
outbuf[WriteOrderTable[i]] = applicate_color_alpha(c[s], k & 1 ? -m : m, !obaq && (k & 1) && !(j & 1));
}
}
}
static void decode_etc2a8_block(const uint8_t *data, uint32_t *outbuf) {
if (data[1] & 0xf0) {
// multiplier != 0
const uint_fast8_t multiplier = data[1] >> 4;
const int_fast8_t *table = Etc2AlphaModTable[data[1] & 0xf];
uint_fast64_t l = bton64(*(uint64_t*)data);
for (int i = 0; i < 16; i++, l >>= 3)
((uint8_t *)(outbuf + WriteOrderTableRev[i]))[3] = clamp(data[0] + multiplier * table[l & 7]);
} else {
// multiplier == 0 (always same as base codeword)
for (int i = 0; i < 16; i++, outbuf++)
((uint8_t *)outbuf)[3] = data[0];
}
}
static void decode_eac_block(const uint8_t *data, int color, uint32_t *outbuf) {
uint_fast8_t multiplier = data[1] >> 1 & 0x78;
if (multiplier == 0)
multiplier = 1;
const int_fast8_t *table = Etc2AlphaModTable[data[1] & 0xf];
uint_fast64_t l = bton64(*(uint64_t*)data);
for (int i = 0; i < 16; i++, l >>= 3) {
int_fast16_t val = data[0] * 8 + multiplier * table[l & 7] + 4;
((uint8_t *)(outbuf + WriteOrderTableRev[i]))[color] = val < 0 ? 0 : val >= 2048 ? 0xff : val >> 3;
}
}
static void decode_eac_signed_block(const uint8_t *data, int color, uint32_t *outbuf) {
int8_t base = (int8_t)data[0];
uint_fast8_t multiplier = data[1] >> 1 & 0x78;
if (multiplier == 0)
multiplier = 1;
const int_fast8_t *table = Etc2AlphaModTable[data[1] & 0xf];
uint_fast64_t l = bton64(*(uint64_t*)data);
for (int i = 0; i < 16; i++, l >>= 3) {
int_fast16_t val = base * 8 + multiplier * table[l & 7] + 1023;
((uint8_t *)(outbuf + WriteOrderTableRev[i]))[color] = val < 0 ? 0 : val >= 2048 ? 0xff : val >> 3;
}
}
int decode_etc1(const uint8_t *data, const long w, const long h, uint32_t *image) {
long num_blocks_x = (w + 3) / 4;
long num_blocks_y = (h + 3) / 4;
uint32_t buffer[16];
for (long by = 0; by < num_blocks_y; by++) {
for (long bx = 0; bx < num_blocks_x; bx++, data += 8) {
decode_etc1_block(data, buffer);
copy_block_buffer(bx, by, w, h, 4, 4, buffer, image);
}
}
return 1;
}
int decode_etc2(const uint8_t *data, const long w, const long h, uint32_t *image) {
long num_blocks_x = (w + 3) / 4;
long num_blocks_y = (h + 3) / 4;
uint32_t buffer[16];
for (long by = 0; by < num_blocks_y; by++) {
for (long bx = 0; bx < num_blocks_x; bx++, data += 8) {
decode_etc2_block(data, buffer);
copy_block_buffer(bx, by, w, h, 4, 4, buffer, image);
}
}
return 1;
}
int decode_etc2a1(const uint8_t *data, const long w, const long h, uint32_t *image) {
long num_blocks_x = (w + 3) / 4;
long num_blocks_y = (h + 3) / 4;
uint32_t buffer[16];
for (long by = 0; by < num_blocks_y; by++) {
for (long bx = 0; bx < num_blocks_x; bx++, data += 8) {
decode_etc2a1_block(data, buffer);
copy_block_buffer(bx, by, w, h, 4, 4, buffer, image);
}
}
return 1;
}
int decode_etc2a8(const uint8_t *data, const long w, const long h, uint32_t *image) {
long num_blocks_x = (w + 3) / 4;
long num_blocks_y = (h + 3) / 4;
uint32_t buffer[16];
for (long by = 0; by < num_blocks_y; by++) {
for (long bx = 0; bx < num_blocks_x; bx++, data += 16) {
decode_etc2_block(data + 8, buffer);
decode_etc2a8_block(data, buffer);
copy_block_buffer(bx, by, w, h, 4, 4, buffer, image);
}
}
return 1;
}
int decode_eacr(const uint8_t *data, const long w, const long h, uint32_t *image) {
long num_blocks_x = (w + 3) / 4;
long num_blocks_y = (h + 3) / 4;
uint32_t buffer[16];
uint32_t base_buffer[16];
for (int i = 0; i < 16; i++)
base_buffer[i] = color(0, 0, 0, 255);
for (long by = 0; by < num_blocks_y; by++) {
for (long bx = 0; bx < num_blocks_x; bx++, data += 8) {
memcpy(buffer, base_buffer, sizeof(buffer));
decode_eac_block(data, 2, buffer);
copy_block_buffer(bx, by, w, h, 4, 4, buffer, image);
}
}
return 1;
}
int decode_eacr_signed(const uint8_t *data, const long w, const long h, uint32_t *image) {
long num_blocks_x = (w + 3) / 4;
long num_blocks_y = (h + 3) / 4;
uint32_t buffer[16];
uint32_t base_buffer[16];
for (int i = 0; i < 16; i++)
base_buffer[i] = color(0, 0, 0, 255);
for (long by = 0; by < num_blocks_y; by++) {
for (long bx = 0; bx < num_blocks_x; bx++, data += 8) {
memcpy(buffer, base_buffer, sizeof(buffer));
decode_eac_signed_block(data, 2, buffer);
copy_block_buffer(bx, by, w, h, 4, 4, buffer, image);
}
}
return 1;
}
int decode_eacrg(const uint8_t *data, const long w, const long h, uint32_t *image) {
long num_blocks_x = (w + 3) / 4;
long num_blocks_y = (h + 3) / 4;
uint32_t buffer[16];
uint32_t base_buffer[16];
for (int i = 0; i < 16; i++)
base_buffer[i] = color(0, 0, 0, 255);
for (long by = 0; by < num_blocks_y; by++) {
for (long bx = 0; bx < num_blocks_x; bx++, data += 16) {
memcpy(buffer, base_buffer, sizeof(buffer));
decode_eac_block(data, 2, buffer);
decode_eac_block(data + 8, 1, buffer);
copy_block_buffer(bx, by, w, h, 4, 4, buffer, image);
}
}
return 1;
}
int decode_eacrg_signed(const uint8_t *data, const long w, const long h, uint32_t *image) {
long num_blocks_x = (w + 3) / 4;
long num_blocks_y = (h + 3) / 4;
uint32_t buffer[16];
uint32_t base_buffer[16];
for (int i = 0; i < 16; i++)
base_buffer[i] = color(0, 0, 0, 255);
for (long by = 0; by < num_blocks_y; by++) {
for (long bx = 0; bx < num_blocks_x; bx++, data += 16) {
memcpy(buffer, base_buffer, sizeof(buffer));
decode_eac_signed_block(data, 2, buffer);
decode_eac_signed_block(data + 8, 1, buffer);
copy_block_buffer(bx, by, w, h, 4, 4, buffer, image);
}
}
return 1;
}

15
Texture2DDecoder/etc.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef ETC_H
#define ETC_H
#include <stdint.h>
int decode_etc1(const uint8_t *, const long, const long, uint32_t *);
int decode_etc2(const uint8_t *, const long, const long, uint32_t *);
int decode_etc2a1(const uint8_t *, const long, const long, uint32_t *);
int decode_etc2a8(const uint8_t *, const long, const long, uint32_t *);
int decode_eacr(const uint8_t *, const long, const long, uint32_t *);
int decode_eacr_signed(const uint8_t *, const long, const long, uint32_t *);
int decode_eacrg(const uint8_t *, const long, const long, uint32_t *);
int decode_eacrg_signed(const uint8_t *, const long, const long, uint32_t *);
#endif /* end of include guard: ETC_H */

36
Texture2DDecoder/fp16.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#ifndef FP16_H
#define FP16_H
#include "fp16/fp16.h"
#endif /* FP16_H */
/*
*
* License Information
*
* FP16 library is derived from https://github.com/Maratyszcza/FP16.
* The library is licensed under the MIT License shown below.
*
*
* The MIT License (MIT)
*
* Copyright (c) 2017 Facebook Inc.
* Copyright (c) 2017 Georgia Institute of Technology
* Copyright 2019 Google LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/

View File

@ -0,0 +1,76 @@
#pragma once
#ifndef FP16_BITCASTS_H
#define FP16_BITCASTS_H
#if defined(__cplusplus) && (__cplusplus >= 201103L)
#include <cstdint>
#elif !defined(__OPENCL_VERSION__)
#include <stdint.h>
#endif
static inline float fp32_from_bits(uint32_t w) {
#if defined(__OPENCL_VERSION__)
return as_float(w);
#elif defined(__CUDA_ARCH__)
return __uint_as_float((unsigned int) w);
#elif defined(__INTEL_COMPILER)
return _castu32_f32(w);
#else
union {
uint32_t as_bits;
float as_value;
} fp32 = { w };
return fp32.as_value;
#endif
}
static inline uint32_t fp32_to_bits(float f) {
#if defined(__OPENCL_VERSION__)
return as_uint(f);
#elif defined(__CUDA_ARCH__)
return (uint32_t) __float_as_uint(f);
#elif defined(__INTEL_COMPILER)
return _castf32_u32(f);
#else
union {
float as_value;
uint32_t as_bits;
} fp32 = { f };
return fp32.as_bits;
#endif
}
static inline double fp64_from_bits(uint64_t w) {
#if defined(__OPENCL_VERSION__)
return as_double(w);
#elif defined(__CUDA_ARCH__)
return __longlong_as_double((long long) w);
#elif defined(__INTEL_COMPILER)
return _castu64_f64(w);
#else
union {
uint64_t as_bits;
double as_value;
} fp64 = { w };
return fp64.as_value;
#endif
}
static inline uint64_t fp64_to_bits(double f) {
#if defined(__OPENCL_VERSION__)
return as_ulong(f);
#elif defined(__CUDA_ARCH__)
return (uint64_t) __double_as_longlong(f);
#elif defined(__INTEL_COMPILER)
return _castf64_u64(f);
#else
union {
double as_value;
uint64_t as_bits;
} fp64 = { f };
return fp64.as_bits;
#endif
}
#endif /* FP16_BITCASTS_H */

View File

@ -0,0 +1,451 @@
#pragma once
#ifndef FP16_FP16_H
#define FP16_FP16_H
#if defined(__cplusplus) && (__cplusplus >= 201103L)
#include <cstdint>
#include <cmath>
#elif !defined(__OPENCL_VERSION__)
#include <stdint.h>
#include <math.h>
#endif
#ifdef _MSC_VER
#include <intrin.h>
#endif
#include "fp16/bitcasts.h"
/*
* Convert a 16-bit floating-point number in IEEE half-precision format, in bit representation, to
* a 32-bit floating-point number in IEEE single-precision format, in bit representation.
*
* @note The implementation doesn't use any floating-point operations.
*/
static inline uint32_t fp16_ieee_to_fp32_bits(uint16_t h) {
/*
* Extend the half-precision floating-point number to 32 bits and shift to the upper part of the 32-bit word:
* +---+-----+------------+-------------------+
* | S |EEEEE|MM MMMM MMMM|0000 0000 0000 0000|
* +---+-----+------------+-------------------+
* Bits 31 26-30 16-25 0-15
*
* S - sign bit, E - bits of the biased exponent, M - bits of the mantissa, 0 - zero bits.
*/
const uint32_t w = (uint32_t) h << 16;
/*
* Extract the sign of the input number into the high bit of the 32-bit word:
*
* +---+----------------------------------+
* | S |0000000 00000000 00000000 00000000|
* +---+----------------------------------+
* Bits 31 0-31
*/
const uint32_t sign = w & UINT32_C(0x80000000);
/*
* Extract mantissa and biased exponent of the input number into the bits 0-30 of the 32-bit word:
*
* +---+-----+------------+-------------------+
* | 0 |EEEEE|MM MMMM MMMM|0000 0000 0000 0000|
* +---+-----+------------+-------------------+
* Bits 30 27-31 17-26 0-16
*/
const uint32_t nonsign = w & UINT32_C(0x7FFFFFFF);
/*
* Renorm shift is the number of bits to shift mantissa left to make the half-precision number normalized.
* If the initial number is normalized, some of its high 6 bits (sign == 0 and 5-bit exponent) equals one.
* In this case renorm_shift == 0. If the number is denormalize, renorm_shift > 0. Note that if we shift
* denormalized nonsign by renorm_shift, the unit bit of mantissa will shift into exponent, turning the
* biased exponent into 1, and making mantissa normalized (i.e. without leading 1).
*/
#ifdef _MSC_VER
unsigned long nonsign_bsr;
_BitScanReverse(&nonsign_bsr, (unsigned long) nonsign);
uint32_t renorm_shift = (uint32_t) nonsign_bsr ^ 31;
#else
uint32_t renorm_shift = __builtin_clz(nonsign);
#endif
renorm_shift = renorm_shift > 5 ? renorm_shift - 5 : 0;
/*
* Iff half-precision number has exponent of 15, the addition overflows it into bit 31,
* and the subsequent shift turns the high 9 bits into 1. Thus
* inf_nan_mask ==
* 0x7F800000 if the half-precision number had exponent of 15 (i.e. was NaN or infinity)
* 0x00000000 otherwise
*/
const int32_t inf_nan_mask = ((int32_t) (nonsign + 0x04000000) >> 8) & INT32_C(0x7F800000);
/*
* Iff nonsign is 0, it overflows into 0xFFFFFFFF, turning bit 31 into 1. Otherwise, bit 31 remains 0.
* The signed shift right by 31 broadcasts bit 31 into all bits of the zero_mask. Thus
* zero_mask ==
* 0xFFFFFFFF if the half-precision number was zero (+0.0h or -0.0h)
* 0x00000000 otherwise
*/
const int32_t zero_mask = (int32_t) (nonsign - 1) >> 31;
/*
* 1. Shift nonsign left by renorm_shift to normalize it (if the input was denormal)
* 2. Shift nonsign right by 3 so the exponent (5 bits originally) becomes an 8-bit field and 10-bit mantissa
* shifts into the 10 high bits of the 23-bit mantissa of IEEE single-precision number.
* 3. Add 0x70 to the exponent (starting at bit 23) to compensate the different in exponent bias
* (0x7F for single-precision number less 0xF for half-precision number).
* 4. Subtract renorm_shift from the exponent (starting at bit 23) to account for renormalization. As renorm_shift
* is less than 0x70, this can be combined with step 3.
* 5. Binary OR with inf_nan_mask to turn the exponent into 0xFF if the input was NaN or infinity.
* 6. Binary ANDNOT with zero_mask to turn the mantissa and exponent into zero if the input was zero.
* 7. Combine with the sign of the input number.
*/
return sign | ((((nonsign << renorm_shift >> 3) + ((0x70 - renorm_shift) << 23)) | inf_nan_mask) & ~zero_mask);
}
/*
* Convert a 16-bit floating-point number in IEEE half-precision format, in bit representation, to
* a 32-bit floating-point number in IEEE single-precision format.
*
* @note The implementation relies on IEEE-like (no assumption about rounding mode and no operations on denormals)
* floating-point operations and bitcasts between integer and floating-point variables.
*/
static inline float fp16_ieee_to_fp32_value(uint16_t h) {
/*
* Extend the half-precision floating-point number to 32 bits and shift to the upper part of the 32-bit word:
* +---+-----+------------+-------------------+
* | S |EEEEE|MM MMMM MMMM|0000 0000 0000 0000|
* +---+-----+------------+-------------------+
* Bits 31 26-30 16-25 0-15
*
* S - sign bit, E - bits of the biased exponent, M - bits of the mantissa, 0 - zero bits.
*/
const uint32_t w = (uint32_t) h << 16;
/*
* Extract the sign of the input number into the high bit of the 32-bit word:
*
* +---+----------------------------------+
* | S |0000000 00000000 00000000 00000000|
* +---+----------------------------------+
* Bits 31 0-31
*/
const uint32_t sign = w & UINT32_C(0x80000000);
/*
* Extract mantissa and biased exponent of the input number into the high bits of the 32-bit word:
*
* +-----+------------+---------------------+
* |EEEEE|MM MMMM MMMM|0 0000 0000 0000 0000|
* +-----+------------+---------------------+
* Bits 27-31 17-26 0-16
*/
const uint32_t two_w = w + w;
/*
* Shift mantissa and exponent into bits 23-28 and bits 13-22 so they become mantissa and exponent
* of a single-precision floating-point number:
*
* S|Exponent | Mantissa
* +-+---+-----+------------+----------------+
* |0|000|EEEEE|MM MMMM MMMM|0 0000 0000 0000|
* +-+---+-----+------------+----------------+
* Bits | 23-31 | 0-22
*
* Next, there are some adjustments to the exponent:
* - The exponent needs to be corrected by the difference in exponent bias between single-precision and half-precision
* formats (0x7F - 0xF = 0x70)
* - Inf and NaN values in the inputs should become Inf and NaN values after conversion to the single-precision number.
* Therefore, if the biased exponent of the half-precision input was 0x1F (max possible value), the biased exponent
* of the single-precision output must be 0xFF (max possible value). We do this correction in two steps:
* - First, we adjust the exponent by (0xFF - 0x1F) = 0xE0 (see exp_offset below) rather than by 0x70 suggested
* by the difference in the exponent bias (see above).
* - Then we multiply the single-precision result of exponent adjustment by 2**(-112) to reverse the effect of
* exponent adjustment by 0xE0 less the necessary exponent adjustment by 0x70 due to difference in exponent bias.
* The floating-point multiplication hardware would ensure than Inf and NaN would retain their value on at least
* partially IEEE754-compliant implementations.
*
* Note that the above operations do not handle denormal inputs (where biased exponent == 0). However, they also do not
* operate on denormal inputs, and do not produce denormal results.
*/
const uint32_t exp_offset = UINT32_C(0xE0) << 23;
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) || defined(__GNUC__) && !defined(__STRICT_ANSI__)
const float exp_scale = 0x1.0p-112f;
#else
const float exp_scale = fp32_from_bits(UINT32_C(0x7800000));
#endif
const float normalized_value = fp32_from_bits((two_w >> 4) + exp_offset) * exp_scale;
/*
* Convert denormalized half-precision inputs into single-precision results (always normalized).
* Zero inputs are also handled here.
*
* In a denormalized number the biased exponent is zero, and mantissa has on-zero bits.
* First, we shift mantissa into bits 0-9 of the 32-bit word.
*
* zeros | mantissa
* +---------------------------+------------+
* |0000 0000 0000 0000 0000 00|MM MMMM MMMM|
* +---------------------------+------------+
* Bits 10-31 0-9
*
* Now, remember that denormalized half-precision numbers are represented as:
* FP16 = mantissa * 2**(-24).
* The trick is to construct a normalized single-precision number with the same mantissa and thehalf-precision input
* and with an exponent which would scale the corresponding mantissa bits to 2**(-24).
* A normalized single-precision floating-point number is represented as:
* FP32 = (1 + mantissa * 2**(-23)) * 2**(exponent - 127)
* Therefore, when the biased exponent is 126, a unit change in the mantissa of the input denormalized half-precision
* number causes a change of the constructud single-precision number by 2**(-24), i.e. the same ammount.
*
* The last step is to adjust the bias of the constructed single-precision number. When the input half-precision number
* is zero, the constructed single-precision number has the value of
* FP32 = 1 * 2**(126 - 127) = 2**(-1) = 0.5
* Therefore, we need to subtract 0.5 from the constructed single-precision number to get the numerical equivalent of
* the input half-precision number.
*/
const uint32_t magic_mask = UINT32_C(126) << 23;
const float magic_bias = 0.5f;
const float denormalized_value = fp32_from_bits((two_w >> 17) | magic_mask) - magic_bias;
/*
* - Choose either results of conversion of input as a normalized number, or as a denormalized number, depending on the
* input exponent. The variable two_w contains input exponent in bits 27-31, therefore if its smaller than 2**27, the
* input is either a denormal number, or zero.
* - Combine the result of conversion of exponent and mantissa with the sign of the input number.
*/
const uint32_t denormalized_cutoff = UINT32_C(1) << 27;
const uint32_t result = sign |
(two_w < denormalized_cutoff ? fp32_to_bits(denormalized_value) : fp32_to_bits(normalized_value));
return fp32_from_bits(result);
}
/*
* Convert a 32-bit floating-point number in IEEE single-precision format to a 16-bit floating-point number in
* IEEE half-precision format, in bit representation.
*
* @note The implementation relies on IEEE-like (no assumption about rounding mode and no operations on denormals)
* floating-point operations and bitcasts between integer and floating-point variables.
*/
static inline uint16_t fp16_ieee_from_fp32_value(float f) {
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) || defined(__GNUC__) && !defined(__STRICT_ANSI__)
const float scale_to_inf = 0x1.0p+112f;
const float scale_to_zero = 0x1.0p-110f;
#else
const float scale_to_inf = fp32_from_bits(UINT32_C(0x77800000));
const float scale_to_zero = fp32_from_bits(UINT32_C(0x08800000));
#endif
float base = (fabsf(f) * scale_to_inf) * scale_to_zero;
const uint32_t w = fp32_to_bits(f);
const uint32_t shl1_w = w + w;
const uint32_t sign = w & UINT32_C(0x80000000);
uint32_t bias = shl1_w & UINT32_C(0xFF000000);
if (bias < UINT32_C(0x71000000)) {
bias = UINT32_C(0x71000000);
}
base = fp32_from_bits((bias >> 1) + UINT32_C(0x07800000)) + base;
const uint32_t bits = fp32_to_bits(base);
const uint32_t exp_bits = (bits >> 13) & UINT32_C(0x00007C00);
const uint32_t mantissa_bits = bits & UINT32_C(0x00000FFF);
const uint32_t nonsign = exp_bits + mantissa_bits;
return (sign >> 16) | (shl1_w > UINT32_C(0xFF000000) ? UINT16_C(0x7E00) : nonsign);
}
/*
* Convert a 16-bit floating-point number in ARM alternative half-precision format, in bit representation, to
* a 32-bit floating-point number in IEEE single-precision format, in bit representation.
*
* @note The implementation doesn't use any floating-point operations.
*/
static inline uint32_t fp16_alt_to_fp32_bits(uint16_t h) {
/*
* Extend the half-precision floating-point number to 32 bits and shift to the upper part of the 32-bit word:
* +---+-----+------------+-------------------+
* | S |EEEEE|MM MMMM MMMM|0000 0000 0000 0000|
* +---+-----+------------+-------------------+
* Bits 31 26-30 16-25 0-15
*
* S - sign bit, E - bits of the biased exponent, M - bits of the mantissa, 0 - zero bits.
*/
const uint32_t w = (uint32_t) h << 16;
/*
* Extract the sign of the input number into the high bit of the 32-bit word:
*
* +---+----------------------------------+
* | S |0000000 00000000 00000000 00000000|
* +---+----------------------------------+
* Bits 31 0-31
*/
const uint32_t sign = w & UINT32_C(0x80000000);
/*
* Extract mantissa and biased exponent of the input number into the bits 0-30 of the 32-bit word:
*
* +---+-----+------------+-------------------+
* | 0 |EEEEE|MM MMMM MMMM|0000 0000 0000 0000|
* +---+-----+------------+-------------------+
* Bits 30 27-31 17-26 0-16
*/
const uint32_t nonsign = w & UINT32_C(0x7FFFFFFF);
/*
* Renorm shift is the number of bits to shift mantissa left to make the half-precision number normalized.
* If the initial number is normalized, some of its high 6 bits (sign == 0 and 5-bit exponent) equals one.
* In this case renorm_shift == 0. If the number is denormalize, renorm_shift > 0. Note that if we shift
* denormalized nonsign by renorm_shift, the unit bit of mantissa will shift into exponent, turning the
* biased exponent into 1, and making mantissa normalized (i.e. without leading 1).
*/
#ifdef _MSC_VER
unsigned long nonsign_bsr;
_BitScanReverse(&nonsign_bsr, (unsigned long) nonsign);
uint32_t renorm_shift = (uint32_t) nonsign_bsr ^ 31;
#else
uint32_t renorm_shift = __builtin_clz(nonsign);
#endif
renorm_shift = renorm_shift > 5 ? renorm_shift - 5 : 0;
/*
* Iff nonsign is 0, it overflows into 0xFFFFFFFF, turning bit 31 into 1. Otherwise, bit 31 remains 0.
* The signed shift right by 31 broadcasts bit 31 into all bits of the zero_mask. Thus
* zero_mask ==
* 0xFFFFFFFF if the half-precision number was zero (+0.0h or -0.0h)
* 0x00000000 otherwise
*/
const int32_t zero_mask = (int32_t) (nonsign - 1) >> 31;
/*
* 1. Shift nonsign left by renorm_shift to normalize it (if the input was denormal)
* 2. Shift nonsign right by 3 so the exponent (5 bits originally) becomes an 8-bit field and 10-bit mantissa
* shifts into the 10 high bits of the 23-bit mantissa of IEEE single-precision number.
* 3. Add 0x70 to the exponent (starting at bit 23) to compensate the different in exponent bias
* (0x7F for single-precision number less 0xF for half-precision number).
* 4. Subtract renorm_shift from the exponent (starting at bit 23) to account for renormalization. As renorm_shift
* is less than 0x70, this can be combined with step 3.
* 5. Binary ANDNOT with zero_mask to turn the mantissa and exponent into zero if the input was zero.
* 6. Combine with the sign of the input number.
*/
return sign | (((nonsign << renorm_shift >> 3) + ((0x70 - renorm_shift) << 23)) & ~zero_mask);
}
/*
* Convert a 16-bit floating-point number in ARM alternative half-precision format, in bit representation, to
* a 32-bit floating-point number in IEEE single-precision format.
*
* @note The implementation relies on IEEE-like (no assumption about rounding mode and no operations on denormals)
* floating-point operations and bitcasts between integer and floating-point variables.
*/
static inline float fp16_alt_to_fp32_value(uint16_t h) {
/*
* Extend the half-precision floating-point number to 32 bits and shift to the upper part of the 32-bit word:
* +---+-----+------------+-------------------+
* | S |EEEEE|MM MMMM MMMM|0000 0000 0000 0000|
* +---+-----+------------+-------------------+
* Bits 31 26-30 16-25 0-15
*
* S - sign bit, E - bits of the biased exponent, M - bits of the mantissa, 0 - zero bits.
*/
const uint32_t w = (uint32_t) h << 16;
/*
* Extract the sign of the input number into the high bit of the 32-bit word:
*
* +---+----------------------------------+
* | S |0000000 00000000 00000000 00000000|
* +---+----------------------------------+
* Bits 31 0-31
*/
const uint32_t sign = w & UINT32_C(0x80000000);
/*
* Extract mantissa and biased exponent of the input number into the high bits of the 32-bit word:
*
* +-----+------------+---------------------+
* |EEEEE|MM MMMM MMMM|0 0000 0000 0000 0000|
* +-----+------------+---------------------+
* Bits 27-31 17-26 0-16
*/
const uint32_t two_w = w + w;
/*
* Shift mantissa and exponent into bits 23-28 and bits 13-22 so they become mantissa and exponent
* of a single-precision floating-point number:
*
* S|Exponent | Mantissa
* +-+---+-----+------------+----------------+
* |0|000|EEEEE|MM MMMM MMMM|0 0000 0000 0000|
* +-+---+-----+------------+----------------+
* Bits | 23-31 | 0-22
*
* Next, the exponent is adjusted for the difference in exponent bias between single-precision and half-precision
* formats (0x7F - 0xF = 0x70). This operation never overflows or generates non-finite values, as the largest
* half-precision exponent is 0x1F and after the adjustment is can not exceed 0x8F < 0xFE (largest single-precision
* exponent for non-finite values).
*
* Note that this operation does not handle denormal inputs (where biased exponent == 0). However, they also do not
* operate on denormal inputs, and do not produce denormal results.
*/
const float exp_offset = UINT32_C(0x70) << 23;
const float normalized_value = fp32_from_bits((two_w >> 4) + exp_offset);
/*
* Convert denormalized half-precision inputs into single-precision results (always normalized).
* Zero inputs are also handled here.
*
* In a denormalized number the biased exponent is zero, and mantissa has on-zero bits.
* First, we shift mantissa into bits 0-9 of the 32-bit word.
*
* zeros | mantissa
* +---------------------------+------------+
* |0000 0000 0000 0000 0000 00|MM MMMM MMMM|
* +---------------------------+------------+
* Bits 10-31 0-9
*
* Now, remember that denormalized half-precision numbers are represented as:
* FP16 = mantissa * 2**(-24).
* The trick is to construct a normalized single-precision number with the same mantissa and thehalf-precision input
* and with an exponent which would scale the corresponding mantissa bits to 2**(-24).
* A normalized single-precision floating-point number is represented as:
* FP32 = (1 + mantissa * 2**(-23)) * 2**(exponent - 127)
* Therefore, when the biased exponent is 126, a unit change in the mantissa of the input denormalized half-precision
* number causes a change of the constructud single-precision number by 2**(-24), i.e. the same ammount.
*
* The last step is to adjust the bias of the constructed single-precision number. When the input half-precision number
* is zero, the constructed single-precision number has the value of
* FP32 = 1 * 2**(126 - 127) = 2**(-1) = 0.5
* Therefore, we need to subtract 0.5 from the constructed single-precision number to get the numerical equivalent of
* the input half-precision number.
*/
const uint32_t magic_mask = UINT32_C(126) << 23;
const float magic_bias = 0.5f;
const float denormalized_value = fp32_from_bits((two_w >> 17) | magic_mask) - magic_bias;
/*
* - Choose either results of conversion of input as a normalized number, or as a denormalized number, depending on the
* input exponent. The variable two_w contains input exponent in bits 27-31, therefore if its smaller than 2**27, the
* input is either a denormal number, or zero.
* - Combine the result of conversion of exponent and mantissa with the sign of the input number.
*/
const uint32_t denormalized_cutoff = UINT32_C(1) << 27;
const uint32_t result = sign |
(two_w < denormalized_cutoff ? fp32_to_bits(denormalized_value) : fp32_to_bits(normalized_value));
return fp32_from_bits(result);
}
/*
* Convert a 32-bit floating-point number in IEEE single-precision format to a 16-bit floating-point number in
* ARM alternative half-precision format, in bit representation.
*
* @note The implementation relies on IEEE-like (no assumption about rounding mode and no operations on denormals)
* floating-point operations and bitcasts between integer and floating-point variables.
*/
static inline uint16_t fp16_alt_from_fp32_value(float f) {
const uint32_t w = fp32_to_bits(f);
const uint32_t sign = w & UINT32_C(0x80000000);
const uint32_t shl1_w = w + w;
const uint32_t shl1_max_fp16_fp32 = UINT32_C(0x8FFFC000);
const uint32_t shl1_base = shl1_w > shl1_max_fp16_fp32 ? shl1_max_fp16_fp32 : shl1_w;
uint32_t shl1_bias = shl1_base & UINT32_C(0xFF000000);
const uint32_t exp_difference = 23 - 10;
const uint32_t shl1_bias_min = (127 - 1 - exp_difference) << 24;
if (shl1_bias < shl1_bias_min) {
shl1_bias = shl1_bias_min;
}
const float bias = fp32_from_bits((shl1_bias >> 1) + ((exp_difference + 2) << 23));
const float base = fp32_from_bits((shl1_base >> 1) + (2 << 23)) + bias;
const uint32_t exp_f = fp32_to_bits(base) >> 13;
return (sign >> 16) | ((exp_f & UINT32_C(0x00007C00)) + (fp32_to_bits(base) & UINT32_C(0x00000FFF)));
}
#endif /* FP16_FP16_H */

248
Texture2DDecoder/pvrtc.cpp Normal file
View File

@ -0,0 +1,248 @@
#include "pvrtc.h"
#include <stdint.h>
#include <string.h>
#include "color.h"
#include "endianness.h"
static const int PVRTC1_STANDARD_WEIGHT[] = {0, 3, 5, 8};
static const int PVRTC1_PUNCHTHROUGH_WEIGHT[] = {0, 4, 4, 8};
static inline long morton_index(const long x, const long y, const long min_dim) {
long offset = 0, shift = 0;
for (long mask = 1; mask < min_dim; mask <<= 1, shift++)
offset |= (((y & mask) | ((x & mask) << 1))) << shift;
offset |= ((x | y) >> shift) << (shift * 2);
return offset;
}
static void get_texel_colors(const uint8_t *data, PVRTCTexelInfo *info) {
uint16_t ca = lton16(*(uint16_t *)(data + 4));
uint16_t cb = lton16(*(uint16_t *)(data + 6));
if (ca & 0x8000) {
info->a.r = ca >> 10 & 0x1f;
info->a.g = ca >> 5 & 0x1f;
info->a.b = (ca & 0x1e) | (ca >> 4 & 1);
info->a.a = 0xf;
} else {
info->a.r = (ca >> 7 & 0x1e) | (ca >> 11 & 1);
info->a.g = (ca >> 3 & 0x1e) | (ca >> 7 & 1);
info->a.b = (ca << 1 & 0x1c) | (ca >> 2 & 3);
info->a.a = ca >> 11 & 0xe;
}
if (cb & 0x8000) {
info->b.r = cb >> 10 & 0x1f;
info->b.g = cb >> 5 & 0x1f;
info->b.b = cb & 0x1f;
info->b.a = 0xf;
} else {
info->b.r = (cb >> 7 & 0x1e) | (cb >> 11 & 1);
info->b.g = (cb >> 3 & 0x1e) | (cb >> 7 & 1);
info->b.b = (cb << 1 & 0x1e) | (cb >> 3 & 1);
info->b.a = cb >> 11 & 0xe;
}
}
static void get_texel_weights_4bpp(const uint8_t *data, PVRTCTexelInfo *info) {
info->punch_through_flag = 0;
int mod_mode = data[4] & 1;
uint32_t mod_bits = lton32(*(uint32_t *)data);
if (mod_mode) {
for (int i = 0; i < 16; i++, mod_bits >>= 2) {
info->weight[i] = PVRTC1_PUNCHTHROUGH_WEIGHT[mod_bits & 3];
if ((mod_bits & 3) == 2)
info->punch_through_flag |= 1 << i;
}
} else {
for (int i = 0; i < 16; i++, mod_bits >>= 2)
info->weight[i] = PVRTC1_STANDARD_WEIGHT[mod_bits & 3];
}
}
static void get_texel_weights_2bpp(const uint8_t *data, PVRTCTexelInfo *info) {
info->punch_through_flag = 0;
int mod_mode = data[4] & 1;
uint32_t mod_bits = lton32(*(uint32_t *)data);
if (mod_mode) {
int fillflag = data[0] & 1 ? (data[2] & 0x10 ? -1 : -2) : -3;
for (int y = 0, i = 1; y < 4; ++y & 1 ? --i : ++i)
for (int x = 0; x < 4; x++, i += 2)
info->weight[i] = fillflag;
for (int y = 0, i = 0; y < 4; ++y & 1 ? ++i : --i)
for (int x = 0; x < 4; x++, i += 2, mod_bits >>= 2)
info->weight[i] = PVRTC1_STANDARD_WEIGHT[mod_bits & 3];
info->weight[0] = (info->weight[0] + 3) & 8;
if (data[0] & 1)
info->weight[20] = (info->weight[20] + 3) & 8;
} else {
for (int i = 0; i < 32; i++, mod_bits >>= 1)
info->weight[i] = mod_bits & 1 ? 8 : 0;
}
}
static void applicate_color_4bpp(const uint8_t *data, PVRTCTexelInfo *const info[9], uint32_t buf[32]) {
static const int INTERP_WEIGHT[4][3] = {{2, 2, 0}, {1, 3, 0}, {0, 4, 0}, {0, 3, 1}};
PVRTCTexelColorInt clr_a[16] = {}, clr_b[16] = {};
for (int y = 0, i = 0; y < 4; y++) {
for (int x = 0; x < 4; x++, i++) {
for (int acy = 0, ac = 0; acy < 3; acy++) {
for (int acx = 0; acx < 3; acx++, ac++) {
int interp_weight = INTERP_WEIGHT[x][acx] * INTERP_WEIGHT[y][acy];
clr_a[i].r += info[ac]->a.r * interp_weight;
clr_a[i].g += info[ac]->a.g * interp_weight;
clr_a[i].b += info[ac]->a.b * interp_weight;
clr_a[i].a += info[ac]->a.a * interp_weight;
clr_b[i].r += info[ac]->b.r * interp_weight;
clr_b[i].g += info[ac]->b.g * interp_weight;
clr_b[i].b += info[ac]->b.b * interp_weight;
clr_b[i].a += info[ac]->b.a * interp_weight;
}
}
clr_a[i].r = (clr_a[i].r >> 1) + (clr_a[i].r >> 6);
clr_a[i].g = (clr_a[i].g >> 1) + (clr_a[i].g >> 6);
clr_a[i].b = (clr_a[i].b >> 1) + (clr_a[i].b >> 6);
clr_a[i].a = (clr_a[i].a) + (clr_a[i].a >> 4);
clr_b[i].r = (clr_b[i].r >> 1) + (clr_b[i].r >> 6);
clr_b[i].g = (clr_b[i].g >> 1) + (clr_b[i].g >> 6);
clr_b[i].b = (clr_b[i].b >> 1) + (clr_b[i].b >> 6);
clr_b[i].a = (clr_b[i].a) + (clr_b[i].a >> 4);
}
}
const PVRTCTexelInfo *self_info = info[4];
uint32_t punch_through_flag = self_info->punch_through_flag;
for (int i = 0; i < 16; i++, punch_through_flag >>= 1) {
buf[i] = color((clr_a[i].r * (8 - self_info->weight[i]) + clr_b[i].r * self_info->weight[i]) / 8,
(clr_a[i].g * (8 - self_info->weight[i]) + clr_b[i].g * self_info->weight[i]) / 8,
(clr_a[i].b * (8 - self_info->weight[i]) + clr_b[i].b * self_info->weight[i]) / 8,
punch_through_flag & 1
? 0
: (clr_a[i].a * (8 - self_info->weight[i]) + clr_b[i].a * self_info->weight[i]) / 8);
}
}
static void applicate_color_2bpp(const uint8_t *data, PVRTCTexelInfo *const info[9], uint32_t buf[32]) {
static const int INTERP_WEIGHT_X[8][3] = {{4, 4, 0}, {3, 5, 0}, {2, 6, 0}, {1, 7, 0},
{0, 8, 0}, {0, 7, 1}, {0, 6, 2}, {0, 5, 3}};
static const int INTERP_WEIGHT_Y[4][3] = {{2, 2, 0}, {1, 3, 0}, {0, 4, 0}, {0, 3, 1}};
PVRTCTexelColorInt clr_a[32] = {}, clr_b[32] = {};
for (int y = 0, i = 0; y < 4; y++) {
for (int x = 0; x < 8; x++, i++) {
for (int acy = 0, ac = 0; acy < 3; acy++) {
for (int acx = 0; acx < 3; acx++, ac++) {
int interp_weight = INTERP_WEIGHT_X[x][acx] * INTERP_WEIGHT_Y[y][acy];
clr_a[i].r += info[ac]->a.r * interp_weight;
clr_a[i].g += info[ac]->a.g * interp_weight;
clr_a[i].b += info[ac]->a.b * interp_weight;
clr_a[i].a += info[ac]->a.a * interp_weight;
clr_b[i].r += info[ac]->b.r * interp_weight;
clr_b[i].g += info[ac]->b.g * interp_weight;
clr_b[i].b += info[ac]->b.b * interp_weight;
clr_b[i].a += info[ac]->b.a * interp_weight;
}
}
clr_a[i].r = (clr_a[i].r >> 2) + (clr_a[i].r >> 7);
clr_a[i].g = (clr_a[i].g >> 2) + (clr_a[i].g >> 7);
clr_a[i].b = (clr_a[i].b >> 2) + (clr_a[i].b >> 7);
clr_a[i].a = (clr_a[i].a >> 1) + (clr_a[i].a >> 5);
clr_b[i].r = (clr_b[i].r >> 2) + (clr_b[i].r >> 7);
clr_b[i].g = (clr_b[i].g >> 2) + (clr_b[i].g >> 7);
clr_b[i].b = (clr_b[i].b >> 2) + (clr_b[i].b >> 7);
clr_b[i].a = (clr_b[i].a >> 1) + (clr_b[i].a >> 5);
}
}
static const int POSYA[4][2] = {{1, 24}, {4, -8}, {4, -8}, {4, -8}};
static const int POSYB[4][2] = {{4, 8}, {4, 8}, {4, 8}, {7, -24}};
static const int POSXL[8][2] = {{3, 7}, {4, -1}, {4, -1}, {4, -1}, {4, -1}, {4, -1}, {4, -1}, {4, -1}};
static const int POSXR[8][2] = {{4, 1}, {4, 1}, {4, 1}, {4, 1}, {4, 1}, {4, 1}, {4, 1}, {5, -7}};
PVRTCTexelInfo *self_info = info[4];
uint32_t punch_through_flag = self_info->punch_through_flag;
for (int y = 0, i = 0; y < 4; y++) {
for (int x = 0; x < 8; x++, i++, punch_through_flag >>= 1) {
switch (self_info->weight[i]) {
case -1:
self_info->weight[i] =
(info[POSYA[y][0]]->weight[i + POSYA[y][1]] + info[POSYB[y][0]]->weight[i + POSYB[y][1]] + 1) / 2;
break;
case -2:
self_info->weight[i] =
(info[POSXL[x][0]]->weight[i + POSXL[x][1]] + info[POSXR[x][0]]->weight[i + POSXR[x][1]] + 1) / 2;
break;
case -3:
self_info->weight[i] =
(info[POSYA[y][0]]->weight[i + POSYA[y][1]] + info[POSYB[y][0]]->weight[i + POSYB[y][1]] +
info[POSXL[x][0]]->weight[i + POSXL[x][1]] + info[POSXR[x][0]]->weight[i + POSXR[x][1]] + 2) /
4;
break;
}
buf[i] = color((clr_a[i].r * (8 - self_info->weight[i]) + clr_b[i].r * self_info->weight[i]) / 8,
(clr_a[i].g * (8 - self_info->weight[i]) + clr_b[i].g * self_info->weight[i]) / 8,
(clr_a[i].b * (8 - self_info->weight[i]) + clr_b[i].b * self_info->weight[i]) / 8,
punch_through_flag & 1
? 0
: (clr_a[i].a * (8 - self_info->weight[i]) + clr_b[i].a * self_info->weight[i]) / 8);
}
}
}
int decode_pvrtc(const uint8_t *data, const long w, const long h, uint32_t *image, const int is2bpp) {
long bw = is2bpp ? 8 : 4;
long num_blocks_x = is2bpp ? (w + 7) / 8 : (w + 3) / 4;
long num_blocks_y = (h + 3) / 4;
long num_blocks = num_blocks_x * num_blocks_y;
long min_num_blocks = num_blocks_x <= num_blocks_y ? num_blocks_x : num_blocks_y;
if ((num_blocks_x & (num_blocks_x - 1)) || (num_blocks_y & (num_blocks_y - 1))) {
//extern const char* error_msg;
//error_msg = "the number of blocks of each side must be a power of 2";
return 0;
}
PVRTCTexelInfo *texel_info = (PVRTCTexelInfo *)malloc(sizeof(PVRTCTexelInfo) * num_blocks);
if (texel_info == NULL) {
//extern const char* error_msg;
//error_msg = "memory allocation failed";
return 0;
}
void (*get_texel_weights_func)(const uint8_t *, PVRTCTexelInfo *) =
is2bpp ? get_texel_weights_2bpp : get_texel_weights_4bpp;
void (*applicate_color_func)(const uint8_t *, PVRTCTexelInfo *const[9], uint32_t[32]) =
is2bpp ? applicate_color_2bpp : applicate_color_4bpp;
const uint8_t *d = data;
for (long i = 0; i < num_blocks; i++, d += 8) {
get_texel_colors(d, &texel_info[i]);
get_texel_weights_func(d, &texel_info[i]);
}
uint32_t buffer[32];
PVRTCTexelInfo *local_info[9];
long pos_x[3], pos_y[3];
for (long by = 0; by < num_blocks_y; by++) {
pos_y[0] = by == 0 ? num_blocks_y - 1 : by - 1;
pos_y[1] = by;
pos_y[2] = by == num_blocks_y - 1 ? 0 : by + 1;
for (long bx = 0, x = 0; bx < num_blocks_x; bx++, x += 4) {
pos_x[0] = bx == 0 ? num_blocks_x - 1 : bx - 1;
pos_x[1] = bx;
pos_x[2] = bx == num_blocks_x - 1 ? 0 : bx + 1;
for (long cy = 0, c = 0; cy < 3; cy++)
for (long cx = 0; cx < 3; cx++, c++)
local_info[c] = &texel_info[morton_index(pos_x[cx], pos_y[cy], min_num_blocks)];
applicate_color_func(data + morton_index(bx, by, min_num_blocks) * 8, local_info, buffer);
copy_block_buffer(bx, by, w, h, bw, 4, buffer, image);
}
}
free(texel_info);
return 1;
}

29
Texture2DDecoder/pvrtc.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef PVRTC_H
#define PVRTC_H
#include <stdint.h>
typedef struct {
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t a;
} PVRTCTexelColor;
typedef struct {
int r;
int g;
int b;
int a;
} PVRTCTexelColorInt;
typedef struct {
PVRTCTexelColor a;
PVRTCTexelColor b;
int8_t weight[32];
uint32_t punch_through_flag;
} PVRTCTexelInfo;
int decode_pvrtc(const uint8_t *, const long, const long, uint32_t *, const int);
#endif /* end of include guard: PVRTC_H */

View File

@ -0,0 +1,34 @@
#include "unitycrunch.h"
#include <stdint.h>
#include <algorithm>
#include "unitycrunch/crn_decomp.h"
bool unity_crunch_unpack_level(const uint8_t* data, uint32_t data_size, uint32_t level_index, void** ret, uint32_t* ret_size) {
unitycrnd::crn_texture_info tex_info;
if (!unitycrnd::crnd_get_texture_info(data, data_size, &tex_info))
{
return false;
}
unitycrnd::crnd_unpack_context pContext = unitycrnd::crnd_unpack_begin(data, data_size);
if (!pContext)
{
return false;
}
const crn_uint32 width = std::max(1U, tex_info.m_width >> level_index);
const crn_uint32 height = std::max(1U, tex_info.m_height >> level_index);
const crn_uint32 blocks_x = std::max(1U, (width + 3) >> 2);
const crn_uint32 blocks_y = std::max(1U, (height + 3) >> 2);
const crn_uint32 row_pitch = blocks_x * unitycrnd::crnd_get_bytes_per_dxt_block(tex_info.m_format);
const crn_uint32 total_face_size = row_pitch * blocks_y;
*ret = new uint8_t[total_face_size];
*ret_size = total_face_size;
if (!unitycrnd::crnd_unpack_level(pContext, ret, total_face_size, row_pitch, level_index))
{
unitycrnd::crnd_unpack_end(pContext);
return false;
}
unitycrnd::crnd_unpack_end(pContext);
return true;
}

View File

@ -0,0 +1,5 @@
#pragma once
#include <stdint.h>
bool unity_crunch_unpack_level(const uint8_t* data, uint32_t data_size, uint32_t level_index, void** ret, uint32_t* ret_size);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,291 @@
#ifndef CRND_INCLUDE_CRN_DEFS_H
#define CRND_INCLUDE_CRN_DEFS_H
// Include crnlib.h (only to bring in some basic CRN-related types).
#include "crnlib.h"
#define CRND_LIB_VERSION 104
#define CRND_VERSION_STRING "01.04"
#ifdef _DEBUG
#define CRND_BUILD_DEBUG
#else
#define CRND_BUILD_RELEASE
#endif
// CRN decompression API
namespace unitycrnd {
typedef unsigned char uint8;
typedef signed char int8;
typedef unsigned short uint16;
typedef signed short int16;
typedef unsigned int uint32;
typedef uint32 uint32;
typedef unsigned int uint;
typedef signed int int32;
#ifdef __GNUC__
typedef unsigned long long uint64;
typedef long long int64;
#else
typedef unsigned __int64 uint64;
typedef signed __int64 int64;
#endif
// The crnd library assumes all allocation blocks have at least CRND_MIN_ALLOC_ALIGNMENT alignment.
const uint32 CRND_MIN_ALLOC_ALIGNMENT = sizeof(uint32) * 2U;
// realloc callback:
// Used to allocate, resize, or free memory blocks.
// If p is NULL, the realloc function attempts to allocate a block of at least size bytes. Returns NULL on out of memory.
// *pActual_size must be set to the actual size of the allocated block, which must be greater than or equal to the requested size.
// If p is not NULL, and size is 0, the realloc function frees the specified block, and always returns NULL. *pActual_size should be set to 0.
// If p is not NULL, and size is non-zero, the realloc function attempts to resize the specified block:
// If movable is false, the realloc function attempts to shrink or expand the block in-place. NULL is returned if the block cannot be resized in place, or if the
// underlying heap implementation doesn't support in-place resizing. Otherwise, the pointer to the original block is returned.
// If movable is true, it is permissible to move the block's contents if it cannot be resized in place. NULL is returned if the block cannot be resized in place, and there
// is not enough memory to relocate the block.
// In all cases, *pActual_size must be set to the actual size of the allocated block, whether it was successfully resized or not.
typedef void* (*crnd_realloc_func)(void* p, size_t size, size_t* pActual_size, bool movable, void* pUser_data);
// msize callback: Returns the size of the memory block in bytes, or 0 if the pointer or block is invalid.
typedef size_t (*crnd_msize_func)(void* p, void* pUser_data);
// crnd_set_memory_callbacks() - Use to override the crnd library's memory allocation functions.
// If any input parameters are NULL, the memory callback functions are reset to the default functions.
// The default functions call malloc(), free(), _msize(), _expand(), etc.
void crnd_set_memory_callbacks(crnd_realloc_func pRealloc, crnd_msize_func pMSize, void* pUser_data);
struct crn_file_info {
inline crn_file_info()
: m_struct_size(sizeof(crn_file_info)) {}
uint32 m_struct_size;
uint32 m_actual_data_size;
uint32 m_header_size;
uint32 m_total_palette_size;
uint32 m_tables_size;
uint32 m_levels;
uint32 m_level_compressed_size[cCRNMaxLevels];
uint32 m_color_endpoint_palette_entries;
uint32 m_color_selector_palette_entries;
uint32 m_alpha_endpoint_palette_entries;
uint32 m_alpha_selector_palette_entries;
};
struct crn_texture_info {
inline crn_texture_info()
: m_struct_size(sizeof(crn_texture_info)) {}
uint32 m_struct_size;
uint32 m_width;
uint32 m_height;
uint32 m_levels;
uint32 m_faces;
uint32 m_bytes_per_block;
uint32 m_userdata0;
uint32 m_userdata1;
crn_format m_format;
};
struct crn_level_info {
inline crn_level_info()
: m_struct_size(sizeof(crn_level_info)) {}
uint32 m_struct_size;
uint32 m_width;
uint32 m_height;
uint32 m_faces;
uint32 m_blocks_x;
uint32 m_blocks_y;
uint32 m_bytes_per_block;
crn_format m_format;
};
// Returns the FOURCC format code corresponding to the specified CRN format.
uint32 crnd_crn_format_to_fourcc(crn_format fmt);
// Returns the fundamental GPU format given a potentially swizzled DXT5 crn_format.
crn_format crnd_get_fundamental_dxt_format(crn_format fmt);
// Returns the size of the crn_format in bits/texel (either 4 or 8).
uint32 crnd_get_crn_format_bits_per_texel(crn_format fmt);
// Returns the number of bytes per DXTn block (8 or 16).
uint32 crnd_get_bytes_per_dxt_block(crn_format fmt);
// Validates the entire file by checking the header and data CRC's.
// This is not something you want to be doing much!
// The crn_file_info.m_struct_size field must be set before calling this function.
bool crnd_validate_file(const void* pData, uint32 data_size, crn_file_info* pFile_info);
// Retrieves texture information from the CRN file.
// The crn_texture_info.m_struct_size field must be set before calling this function.
bool crnd_get_texture_info(const void* pData, uint32 data_size, crn_texture_info* pTexture_info);
// Retrieves mipmap level specific information from the CRN file.
// The crn_level_info.m_struct_size field must be set before calling this function.
bool crnd_get_level_info(const void* pData, uint32 data_size, uint32 level_index, crn_level_info* pLevel_info);
// Transcode/unpack context handle.
typedef void* crnd_unpack_context;
// crnd_unpack_begin() - Decompresses the texture's decoder tables and endpoint/selector palettes.
// Once you call this function, you may call crnd_unpack_level() to unpack one or more mip levels.
// Don't call this once per mip level (unless you absolutely must)!
// This function allocates enough memory to hold: Huffman decompression tables, and the endpoint/selector palettes (color and/or alpha).
// Worst case allocation is approx. 200k, assuming all palettes contain 8192 entries.
// pData must point to a buffer holding all of the compressed .CRN file data.
// This buffer must be stable until crnd_unpack_end() is called.
// Returns NULL if out of memory, or if any of the input parameters are invalid.
crnd_unpack_context crnd_unpack_begin(const void* pData, uint32 data_size);
// Returns a pointer to the compressed .CRN data associated with a crnd_unpack_context.
// Returns false if any of the input parameters are invalid.
bool crnd_get_data(crnd_unpack_context pContext, const void** ppData, uint32* pData_size);
// crnd_unpack_level() - Transcodes the specified mipmap level to a destination buffer in cached or write combined memory.
// pContext - Context created by a call to crnd_unpack_begin().
// ppDst - A pointer to an array of 1 or 6 destination buffer pointers. Cubemaps require an array of 6 pointers, 2D textures require an array of 1 pointer.
// dst_size_in_bytes - Optional size of each destination buffer. Only used for debugging - OK to set to UINT32_MAX.
// row_pitch_in_bytes - The pitch in bytes from one row of DXT blocks to the next. Must be a multiple of 4.
// level_index - mipmap level index, where 0 is the largest/first level.
// Returns false if any of the input parameters, or the compressed stream, are invalid.
// This function does not allocate any memory.
bool crnd_unpack_level(
crnd_unpack_context pContext,
void** ppDst, uint32 dst_size_in_bytes, uint32 row_pitch_in_bytes,
uint32 level_index);
// crnd_unpack_level_segmented() - Unpacks the specified mipmap level from a "segmented" CRN file.
// See the crnd_create_segmented_file() API below.
// Segmented files allow the user to control where the compressed mipmap data is stored.
bool crnd_unpack_level_segmented(
crnd_unpack_context pContext,
const void* pSrc, uint32 src_size_in_bytes,
void** ppDst, uint32 dst_size_in_bytes, uint32 row_pitch_in_bytes,
uint32 level_index);
// crnd_unpack_end() - Frees the decompress tables and unpacked palettes associated with the specified unpack context.
// Returns false if the context is NULL, or if it points to an invalid context.
// This function frees all memory associated with the context.
bool crnd_unpack_end(crnd_unpack_context pContext);
// The following API's allow the user to create "segmented" CRN files. A segmented file contains multiple pieces:
// - Base data: Header + compression tables
// - Level data: Individual mipmap levels
// This allows mipmap levels from multiple CRN files to be tightly packed together into single files.
// Returns a pointer to the level's compressed data, and optionally returns the level's compressed data size if pSize is not NULL.
const void* crnd_get_level_data(const void* pData, uint32 data_size, uint32 level_index, uint32* pSize);
// Returns the compressed size of the texture's header and compression tables (but no levels).
uint32 crnd_get_segmented_file_size(const void* pData, uint32 data_size);
// Creates a "segmented" CRN texture from a normal CRN texture. The new texture will be created at pBase_data, and will be crnd_get_base_data_size() bytes long.
// base_data_size must be >= crnd_get_base_data_size().
// The base data will contain the CRN header and compression tables, but no mipmap data.
bool crnd_create_segmented_file(const void* pData, uint32 data_size, void* pBase_data, uint base_data_size);
} // namespace unitycrnd
// Low-level CRN file header cracking.
namespace unitycrnd {
template <unsigned int N>
struct crn_packed_uint {
inline crn_packed_uint() {}
inline crn_packed_uint(unsigned int val) { *this = val; }
inline crn_packed_uint(const crn_packed_uint& other) { *this = other; }
inline crn_packed_uint& operator=(const crn_packed_uint& rhs) {
if (this != &rhs)
memcpy(m_buf, rhs.m_buf, sizeof(m_buf));
return *this;
}
inline crn_packed_uint& operator=(unsigned int val) {
//CRND_ASSERT((N == 4U) || (val < (1U << (N * 8U))));
val <<= (8U * (4U - N));
for (unsigned int i = 0; i < N; i++) {
m_buf[i] = static_cast<unsigned char>(val >> 24U);
val <<= 8U;
}
return *this;
}
inline operator unsigned int() const {
switch (N) {
case 1:
return m_buf[0];
case 2:
return (m_buf[0] << 8U) | m_buf[1];
case 3:
return (m_buf[0] << 16U) | (m_buf[1] << 8U) | (m_buf[2]);
default:
return (m_buf[0] << 24U) | (m_buf[1] << 16U) | (m_buf[2] << 8U) | (m_buf[3]);
}
}
unsigned char m_buf[N];
};
#pragma pack(push)
#pragma pack(1)
struct crn_palette {
crn_packed_uint<3> m_ofs;
crn_packed_uint<3> m_size;
crn_packed_uint<2> m_num;
};
enum crn_header_flags {
// If set, the compressed mipmap level data is not located after the file's base data - it will be separately managed by the user instead.
cCRNHeaderFlagSegmented = 1
};
struct crn_header {
enum { cCRNSigValue = ('H' << 8) | 'x' };
crn_packed_uint<2> m_sig;
crn_packed_uint<2> m_header_size;
crn_packed_uint<2> m_header_crc16;
crn_packed_uint<4> m_data_size;
crn_packed_uint<2> m_data_crc16;
crn_packed_uint<2> m_width;
crn_packed_uint<2> m_height;
crn_packed_uint<1> m_levels;
crn_packed_uint<1> m_faces;
crn_packed_uint<1> m_format;
crn_packed_uint<2> m_flags;
crn_packed_uint<4> m_reserved;
crn_packed_uint<4> m_userdata0;
crn_packed_uint<4> m_userdata1;
crn_palette m_color_endpoints;
crn_palette m_color_selectors;
crn_palette m_alpha_endpoints;
crn_palette m_alpha_selectors;
crn_packed_uint<2> m_tables_size;
crn_packed_uint<3> m_tables_ofs;
// m_level_ofs[] is actually an array of offsets: m_level_ofs[m_levels]
crn_packed_uint<4> m_level_ofs[1];
};
const unsigned int cCRNHeaderMinSize = 62U;
#pragma pack(pop)
} // namespace unitycrnd
#endif // CRND_INCLUDE_CRN_DEFS_H

View File

@ -0,0 +1,640 @@
// File: crnlib.h - Advanced DXTn texture compression library.
// Copyright (c) 2010-2016 Richard Geldreich, Jr. and Binomial LLC
// See copyright notice and license at the end of this file.
//
// This header file contains the public crnlib declarations for DXTn,
// clustered DXTn, and CRN compression/decompression.
//
// Note: This library does NOT need to be linked into your game executable if
// all you want to do is transcode .CRN files to raw DXTn bits at run-time.
// The crn_decomp.h header file library contains all the code necessary for
// decompression.
//
// Important: If compiling with gcc, be sure strict aliasing is disabled: -fno-strict-aliasing
#ifndef CRNLIB_H
#define CRNLIB_H
#ifdef _MSC_VER
#pragma warning(disable : 4127) // conditional expression is constant
#endif
#define CRNLIB_VERSION 104
#define CRNLIB_SUPPORT_ATI_COMPRESS 0
#define CRNLIB_SUPPORT_SQUISH 0
typedef unsigned char crn_uint8;
typedef unsigned short crn_uint16;
typedef unsigned int crn_uint32;
typedef signed char crn_int8;
typedef signed short crn_int16;
typedef signed int crn_int32;
typedef unsigned int crn_bool;
// crnlib can compress to these file types.
enum crn_file_type {
// .CRN
cCRNFileTypeCRN = 0,
// .DDS using regular DXT or clustered DXT
cCRNFileTypeDDS,
cCRNFileTypeForceDWORD = 0xFFFFFFFF
};
// Supported compressed pixel formats.
// Basically all the standard DX9 formats, with some swizzled DXT5 formats
// (most of them supported by ATI's Compressonator), along with some ATI/X360 GPU specific formats.
enum crn_format {
cCRNFmtInvalid = -1,
cCRNFmtDXT1 = 0,
cCRNFmtFirstValid = cCRNFmtDXT1,
// cCRNFmtDXT3 is not currently supported when writing to CRN - only DDS.
cCRNFmtDXT3,
cCRNFmtDXT5,
// Various DXT5 derivatives
cCRNFmtDXT5_CCxY, // Luma-chroma
cCRNFmtDXT5_xGxR, // Swizzled 2-component
cCRNFmtDXT5_xGBR, // Swizzled 3-component
cCRNFmtDXT5_AGBR, // Swizzled 4-component
// ATI 3DC and X360 DXN
cCRNFmtDXN_XY,
cCRNFmtDXN_YX,
// DXT5 alpha blocks only
cCRNFmtDXT5A,
cCRNFmtETC1,
cCRNFmtETC2,
cCRNFmtETC2A,
cCRNFmtETC1S,
cCRNFmtETC2AS,
cCRNFmtTotal,
cCRNFmtForceDWORD = 0xFFFFFFFF
};
// Various library/file format limits.
enum crn_limits {
// Max. mipmap level resolution on any axis.
cCRNMaxLevelResolution = 4096,
cCRNMinPaletteSize = 8,
cCRNMaxPaletteSize = 8192,
cCRNMaxFaces = 6,
cCRNMaxLevels = 16,
cCRNMaxHelperThreads = 15,
cCRNMinQualityLevel = 0,
cCRNMaxQualityLevel = 255
};
// CRN/DDS compression flags.
// See the m_flags member in the crn_comp_params struct, below.
enum crn_comp_flags {
// Enables perceptual colorspace distance metrics if set.
// Important: Be sure to disable this when compressing non-sRGB colorspace images, like normal maps!
// Default: Set
cCRNCompFlagPerceptual = 1,
// Enables (up to) 8x8 macroblock usage if set. If disabled, only 4x4 blocks are allowed.
// Compression ratio will be lower when disabled, but may cut down on blocky artifacts because the process used to determine
// where large macroblocks can be used without artifacts isn't perfect.
// Default: Set.
cCRNCompFlagHierarchical = 2,
// cCRNCompFlagQuick disables several output file optimizations - intended for things like quicker previews.
// Default: Not set.
cCRNCompFlagQuick = 4,
// DXT1: OK to use DXT1 alpha blocks for better quality or DXT1A transparency.
// DXT5: OK to use both DXT5 block types.
// Currently only used when writing to .DDS files, as .CRN uses only a subset of the possible DXTn block types.
// Default: Set.
cCRNCompFlagUseBothBlockTypes = 8,
// OK to use DXT1A transparent indices to encode black (assumes pixel shader ignores fetched alpha).
// Currently only used when writing to .DDS files, .CRN never uses alpha blocks.
// Default: Not set.
cCRNCompFlagUseTransparentIndicesForBlack = 16,
// Disables endpoint caching, for more deterministic output.
// Currently only used when writing to .DDS files.
// Default: Not set.
cCRNCompFlagDisableEndpointCaching = 32,
// If enabled, use the cCRNColorEndpointPaletteSize, etc. params to control the CRN palette sizes. Only useful when writing to .CRN files.
// Default: Not set.
cCRNCompFlagManualPaletteSizes = 64,
// If enabled, DXT1A alpha blocks are used to encode single bit transparency.
// Default: Not set.
cCRNCompFlagDXT1AForTransparency = 128,
// If enabled, the DXT1 compressor's color distance metric assumes the pixel shader will be converting the fetched RGB results to luma (Y part of YCbCr).
// This increases quality when compressing grayscale images, because the compressor can spread the luma error amoung all three channels (i.e. it can generate blocks
// with some chroma present if doing so will ultimately lead to lower luma error).
// Only enable on grayscale source images.
// Default: Not set.
cCRNCompFlagGrayscaleSampling = 256,
// If enabled, debug information will be output during compression.
// Default: Not set.
cCRNCompFlagDebugging = 0x80000000,
cCRNCompFlagForceDWORD = 0xFFFFFFFF
};
// Controls DXTn quality vs. speed control - only used when compressing to .DDS.
enum crn_dxt_quality {
cCRNDXTQualitySuperFast,
cCRNDXTQualityFast,
cCRNDXTQualityNormal,
cCRNDXTQualityBetter,
cCRNDXTQualityUber,
cCRNDXTQualityTotal,
cCRNDXTQualityForceDWORD = 0xFFFFFFFF
};
// Which DXTn compressor to use when compressing to plain (non-clustered) .DDS.
enum crn_dxt_compressor_type {
cCRNDXTCompressorCRN, // Use crnlib's ETC1 or DXTc block compressor (default, highest quality, comparable or better than ati_compress or squish, and crnlib's ETC1 is a lot fasterw with similiar quality to Erricson's)
cCRNDXTCompressorCRNF, // Use crnlib's "fast" DXTc block compressor
cCRNDXTCompressorRYG, // Use RYG's DXTc block compressor (low quality, but very fast)
#if CRNLIB_SUPPORT_ATI_COMPRESS
cCRNDXTCompressorATI,
#endif
#if CRNLIB_SUPPORT_SQUISH
cCRNDXTCompressorSquish,
#endif
cCRNTotalDXTCompressors,
cCRNDXTCompressorForceDWORD = 0xFFFFFFFF
};
// Progress callback function.
// Processing will stop prematurely (and fail) if the callback returns false.
// phase_index, total_phases - high level progress
// subphase_index, total_subphases - progress within current phase
typedef crn_bool (*crn_progress_callback_func)(crn_uint32 phase_index, crn_uint32 total_phases, crn_uint32 subphase_index, crn_uint32 total_subphases, void* pUser_data_ptr);
// CRN/DDS compression parameters struct.
struct crn_comp_params {
inline crn_comp_params() { clear(); }
// Clear struct to default parameters.
inline void clear() {
m_size_of_obj = sizeof(*this);
m_file_type = cCRNFileTypeCRN;
m_faces = 1;
m_width = 0;
m_height = 0;
m_levels = 1;
m_format = cCRNFmtDXT1;
m_flags = cCRNCompFlagPerceptual | cCRNCompFlagHierarchical | cCRNCompFlagUseBothBlockTypes;
for (crn_uint32 f = 0; f < cCRNMaxFaces; f++)
for (crn_uint32 l = 0; l < cCRNMaxLevels; l++)
m_pImages[f][l] = NULL;
m_target_bitrate = 0.0f;
m_quality_level = cCRNMaxQualityLevel;
m_dxt1a_alpha_threshold = 128;
m_dxt_quality = cCRNDXTQualityUber;
m_dxt_compressor_type = cCRNDXTCompressorCRN;
m_alpha_component = 3;
m_crn_adaptive_tile_color_psnr_derating = 2.0f;
m_crn_adaptive_tile_alpha_psnr_derating = 2.0f;
m_crn_color_endpoint_palette_size = 0;
m_crn_color_selector_palette_size = 0;
m_crn_alpha_endpoint_palette_size = 0;
m_crn_alpha_selector_palette_size = 0;
m_num_helper_threads = 0;
m_userdata0 = 0;
m_userdata1 = 0;
m_pProgress_func = NULL;
m_pProgress_func_data = NULL;
}
inline bool operator==(const crn_comp_params& rhs) const {
#define CRNLIB_COMP(x) \
do { \
if ((x) != (rhs.x)) \
return false; \
} while (0)
CRNLIB_COMP(m_size_of_obj);
CRNLIB_COMP(m_file_type);
CRNLIB_COMP(m_faces);
CRNLIB_COMP(m_width);
CRNLIB_COMP(m_height);
CRNLIB_COMP(m_levels);
CRNLIB_COMP(m_format);
CRNLIB_COMP(m_flags);
CRNLIB_COMP(m_target_bitrate);
CRNLIB_COMP(m_quality_level);
CRNLIB_COMP(m_dxt1a_alpha_threshold);
CRNLIB_COMP(m_dxt_quality);
CRNLIB_COMP(m_dxt_compressor_type);
CRNLIB_COMP(m_alpha_component);
CRNLIB_COMP(m_crn_adaptive_tile_color_psnr_derating);
CRNLIB_COMP(m_crn_adaptive_tile_alpha_psnr_derating);
CRNLIB_COMP(m_crn_color_endpoint_palette_size);
CRNLIB_COMP(m_crn_color_selector_palette_size);
CRNLIB_COMP(m_crn_alpha_endpoint_palette_size);
CRNLIB_COMP(m_crn_alpha_selector_palette_size);
CRNLIB_COMP(m_num_helper_threads);
CRNLIB_COMP(m_userdata0);
CRNLIB_COMP(m_userdata1);
CRNLIB_COMP(m_pProgress_func);
CRNLIB_COMP(m_pProgress_func_data);
for (crn_uint32 f = 0; f < cCRNMaxFaces; f++)
for (crn_uint32 l = 0; l < cCRNMaxLevels; l++)
CRNLIB_COMP(m_pImages[f][l]);
#undef CRNLIB_COMP
return true;
}
// Returns true if the input parameters are reasonable.
inline bool check() const {
if ((m_file_type > cCRNFileTypeDDS) ||
(((int)m_quality_level < (int)cCRNMinQualityLevel) || ((int)m_quality_level > (int)cCRNMaxQualityLevel)) ||
(m_dxt1a_alpha_threshold > 255) ||
((m_faces != 1) && (m_faces != 6)) ||
((m_width < 1) || (m_width > cCRNMaxLevelResolution)) ||
((m_height < 1) || (m_height > cCRNMaxLevelResolution)) ||
((m_levels < 1) || (m_levels > cCRNMaxLevels)) ||
((m_format < cCRNFmtDXT1) || (m_format >= cCRNFmtTotal)) ||
((m_crn_color_endpoint_palette_size) && ((m_crn_color_endpoint_palette_size < cCRNMinPaletteSize) || (m_crn_color_endpoint_palette_size > cCRNMaxPaletteSize))) ||
((m_crn_color_selector_palette_size) && ((m_crn_color_selector_palette_size < cCRNMinPaletteSize) || (m_crn_color_selector_palette_size > cCRNMaxPaletteSize))) ||
((m_crn_alpha_endpoint_palette_size) && ((m_crn_alpha_endpoint_palette_size < cCRNMinPaletteSize) || (m_crn_alpha_endpoint_palette_size > cCRNMaxPaletteSize))) ||
((m_crn_alpha_selector_palette_size) && ((m_crn_alpha_selector_palette_size < cCRNMinPaletteSize) || (m_crn_alpha_selector_palette_size > cCRNMaxPaletteSize))) ||
(m_alpha_component > 3) ||
(m_num_helper_threads > cCRNMaxHelperThreads) ||
(m_dxt_quality > cCRNDXTQualityUber) ||
(m_dxt_compressor_type >= cCRNTotalDXTCompressors)) {
return false;
}
return true;
}
// Helper to set/get flags from m_flags member.
inline bool get_flag(crn_comp_flags flag) const { return (m_flags & flag) != 0; }
inline void set_flag(crn_comp_flags flag, bool val) {
m_flags &= ~flag;
if (val)
m_flags |= flag;
}
crn_uint32 m_size_of_obj;
crn_file_type m_file_type; // Output file type: cCRNFileTypeCRN or cCRNFileTypeDDS.
crn_uint32 m_faces; // 1 (2D map) or 6 (cubemap)
crn_uint32 m_width; // [1,cCRNMaxLevelResolution], non-power of 2 OK, non-square OK
crn_uint32 m_height; // [1,cCRNMaxLevelResolution], non-power of 2 OK, non-square OK
crn_uint32 m_levels; // [1,cCRNMaxLevelResolution], non-power of 2 OK, non-square OK
crn_format m_format; // Output pixel format.
crn_uint32 m_flags; // see crn_comp_flags enum
// Array of pointers to 32bpp input images.
const crn_uint32* m_pImages[cCRNMaxFaces][cCRNMaxLevels];
// Target bitrate - if non-zero, the compressor will use an interpolative search to find the
// highest quality level that is <= the target bitrate. If it fails to find a bitrate high enough, it'll
// try disabling adaptive block sizes (cCRNCompFlagHierarchical flag) and redo the search. This process can be pretty slow.
float m_target_bitrate;
// Desired quality level.
// Currently, CRN and DDS quality levels are not compatible with eachother from an image quality standpoint.
crn_uint32 m_quality_level; // [cCRNMinQualityLevel, cCRNMaxQualityLevel]
// DXTn compression parameters.
crn_uint32 m_dxt1a_alpha_threshold;
crn_dxt_quality m_dxt_quality;
crn_dxt_compressor_type m_dxt_compressor_type;
// Alpha channel's component. Defaults to 3.
crn_uint32 m_alpha_component;
// Various low-level CRN specific parameters.
float m_crn_adaptive_tile_color_psnr_derating;
float m_crn_adaptive_tile_alpha_psnr_derating;
crn_uint32 m_crn_color_endpoint_palette_size; // [cCRNMinPaletteSize,cCRNMaxPaletteSize]
crn_uint32 m_crn_color_selector_palette_size; // [cCRNMinPaletteSize,cCRNMaxPaletteSize]
crn_uint32 m_crn_alpha_endpoint_palette_size; // [cCRNMinPaletteSize,cCRNMaxPaletteSize]
crn_uint32 m_crn_alpha_selector_palette_size; // [cCRNMinPaletteSize,cCRNMaxPaletteSize]
// Number of helper threads to create during compression. 0=no threading.
crn_uint32 m_num_helper_threads;
// CRN userdata0 and userdata1 members, which are written directly to the header of the output file.
crn_uint32 m_userdata0;
crn_uint32 m_userdata1;
// User provided progress callback.
crn_progress_callback_func m_pProgress_func;
void* m_pProgress_func_data;
};
// Mipmap generator's mode.
enum crn_mip_mode {
cCRNMipModeUseSourceOrGenerateMips, // Use source texture's mipmaps if it has any, otherwise generate new mipmaps
cCRNMipModeUseSourceMips, // Use source texture's mipmaps if it has any, otherwise the output has no mipmaps
cCRNMipModeGenerateMips, // Always generate new mipmaps
cCRNMipModeNoMips, // Output texture has no mipmaps
cCRNMipModeTotal,
cCRNModeForceDWORD = 0xFFFFFFFF
};
const char* crn_get_mip_mode_desc(crn_mip_mode m);
const char* crn_get_mip_mode_name(crn_mip_mode m);
// Mipmap generator's filter kernel.
enum crn_mip_filter {
cCRNMipFilterBox,
cCRNMipFilterTent,
cCRNMipFilterLanczos4,
cCRNMipFilterMitchell,
cCRNMipFilterKaiser, // Kaiser=default mipmap filter
cCRNMipFilterTotal,
cCRNMipFilterForceDWORD = 0xFFFFFFFF
};
const char* crn_get_mip_filter_name(crn_mip_filter f);
// Mipmap generator's scale mode.
enum crn_scale_mode {
cCRNSMDisabled,
cCRNSMAbsolute,
cCRNSMRelative,
cCRNSMLowerPow2,
cCRNSMNearestPow2,
cCRNSMNextPow2,
cCRNSMTotal,
cCRNSMForceDWORD = 0xFFFFFFFF
};
const char* crn_get_scale_mode_desc(crn_scale_mode sm);
// Mipmap generator parameters.
struct crn_mipmap_params {
inline crn_mipmap_params() { clear(); }
inline void clear() {
m_size_of_obj = sizeof(*this);
m_mode = cCRNMipModeUseSourceOrGenerateMips;
m_filter = cCRNMipFilterKaiser;
m_gamma_filtering = true;
m_gamma = 2.2f;
// Default "blurriness" factor of .9 actually sharpens the output a little.
m_blurriness = .9f;
m_renormalize = false;
m_tiled = false;
m_max_levels = cCRNMaxLevels;
m_min_mip_size = 1;
m_scale_mode = cCRNSMDisabled;
m_scale_x = 1.0f;
m_scale_y = 1.0f;
m_window_left = 0;
m_window_top = 0;
m_window_right = 0;
m_window_bottom = 0;
m_clamp_scale = false;
m_clamp_width = 0;
m_clamp_height = 0;
}
inline bool check() const { return true; }
inline bool operator==(const crn_mipmap_params& rhs) const {
#define CRNLIB_COMP(x) \
do { \
if ((x) != (rhs.x)) \
return false; \
} while (0)
CRNLIB_COMP(m_size_of_obj);
CRNLIB_COMP(m_mode);
CRNLIB_COMP(m_filter);
CRNLIB_COMP(m_gamma_filtering);
CRNLIB_COMP(m_gamma);
CRNLIB_COMP(m_blurriness);
CRNLIB_COMP(m_renormalize);
CRNLIB_COMP(m_tiled);
CRNLIB_COMP(m_max_levels);
CRNLIB_COMP(m_min_mip_size);
CRNLIB_COMP(m_scale_mode);
CRNLIB_COMP(m_scale_x);
CRNLIB_COMP(m_scale_y);
CRNLIB_COMP(m_window_left);
CRNLIB_COMP(m_window_top);
CRNLIB_COMP(m_window_right);
CRNLIB_COMP(m_window_bottom);
CRNLIB_COMP(m_clamp_scale);
CRNLIB_COMP(m_clamp_width);
CRNLIB_COMP(m_clamp_height);
return true;
#undef CRNLIB_COMP
}
crn_uint32 m_size_of_obj;
crn_mip_mode m_mode;
crn_mip_filter m_filter;
crn_bool m_gamma_filtering;
float m_gamma;
float m_blurriness;
crn_uint32 m_max_levels;
crn_uint32 m_min_mip_size;
crn_bool m_renormalize;
crn_bool m_tiled;
crn_scale_mode m_scale_mode;
float m_scale_x;
float m_scale_y;
crn_uint32 m_window_left;
crn_uint32 m_window_top;
crn_uint32 m_window_right;
crn_uint32 m_window_bottom;
crn_bool m_clamp_scale;
crn_uint32 m_clamp_width;
crn_uint32 m_clamp_height;
};
// -------- High-level helper function definitions for CDN/DDS compression.
#ifndef CRNLIB_MIN_ALLOC_ALIGNMENT
#define CRNLIB_MIN_ALLOC_ALIGNMENT sizeof(size_t) * 2
#endif
// Function to set an optional user provided memory allocation/reallocation/msize routines.
// By default, crnlib just uses malloc(), free(), etc. for all allocations.
typedef void* (*crn_realloc_func)(void* p, size_t size, size_t* pActual_size, bool movable, void* pUser_data);
typedef size_t (*crn_msize_func)(void* p, void* pUser_data);
void crn_set_memory_callbacks(crn_realloc_func pRealloc, crn_msize_func pMSize, void* pUser_data);
// Frees memory blocks allocated by crn_compress(), crn_decompress_crn_to_dds(), or crn_decompress_dds_to_images().
void crn_free_block(void* pBlock);
// Compresses a 32-bit/pixel texture to either: a regular DX9 DDS file, a "clustered" (or reduced entropy) DX9 DDS file, or a CRN file in memory.
// Input parameters:
// comp_params is the compression parameters struct, defined above.
// compressed_size will be set to the size of the returned memory block containing the output file.
// The returned block must be freed by calling crn_free_block().
// *pActual_quality_level will be set to the actual quality level used to compress the image. May be NULL.
// *pActual_bitrate will be set to the output file's effective bitrate, possibly taking into account LZMA compression. May be NULL.
// Return value:
// The compressed file data, or NULL on failure.
// compressed_size will be set to the size of the returned memory buffer.
// Notes:
// A "regular" DDS file is compressed using normal DXTn compression at the specified DXT quality level.
// A "clustered" DDS file is compressed using clustered DXTn compression to either the target bitrate or the specified integer quality factor.
// The output file is a standard DX9 format DDS file, except the compressor assumes you will be later losslessly compressing the DDS output file using the LZMA algorithm.
// A texture is defined as an array of 1 or 6 "faces" (6 faces=cubemap), where each "face" consists of between [1,cCRNMaxLevels] mipmap levels.
// Mipmap levels are simple 32-bit 2D images with a pitch of width*sizeof(uint32), arranged in the usual raster order (top scanline first).
// The image pixels may be grayscale (YYYX bytes in memory), grayscale/alpha (YYYA in memory), 24-bit (RGBX in memory), or 32-bit (RGBA) colors (where "X"=don't care).
// RGB color data is generally assumed to be in the sRGB colorspace. If not, be sure to clear the "cCRNCompFlagPerceptual" in the crn_comp_params struct!
void* crn_compress(const crn_comp_params& comp_params, crn_uint32& compressed_size, crn_uint32* pActual_quality_level = NULL, float* pActual_bitrate = NULL);
// Like the above function, except this function can also do things like generate mipmaps, and resize or crop the input texture before compression.
// The actual operations performed are controlled by the crn_mipmap_params struct members.
// Be sure to set the "m_gamma_filtering" member of crn_mipmap_params to false if the input texture is not sRGB.
void* crn_compress(const crn_comp_params& comp_params, const crn_mipmap_params& mip_params, crn_uint32& compressed_size, crn_uint32* pActual_quality_level = NULL, float* pActual_bitrate = NULL);
// Transcodes an entire CRN file to DDS using the crn_decomp.h header file library to do most of the heavy lifting.
// The output DDS file's format is guaranteed to be one of the DXTn formats in the crn_format enum.
// This is a fast operation, because the CRN format is explicitly designed to be efficiently transcodable to DXTn.
// For more control over decompression, see the lower-level helper functions in crn_decomp.h, which do not depend at all on crnlib.
void* crn_decompress_crn_to_dds(const void* pCRN_file_data, crn_uint32& file_size);
// Decompresses an entire DDS file in any supported format to uncompressed 32-bit/pixel image(s).
// See the crnlib::pixel_format enum in inc/dds_defs.h for a list of the supported DDS formats.
// You are responsible for freeing each image block, either by calling crn_free_all_images() or manually calling crn_free_block() on each image pointer.
struct crn_texture_desc {
crn_uint32 m_faces;
crn_uint32 m_width;
crn_uint32 m_height;
crn_uint32 m_levels;
crn_uint32 m_fmt_fourcc; // Same as crnlib::pixel_format
};
bool crn_decompress_dds_to_images(const void* pDDS_file_data, crn_uint32 dds_file_size, crn_uint32** ppImages, crn_texture_desc& tex_desc);
// Frees all images allocated by crn_decompress_dds_to_images().
void crn_free_all_images(crn_uint32** ppImages, const crn_texture_desc& desc);
// -------- crn_format related helpers functions.
// Returns the FOURCC format equivalent to the specified crn_format.
crn_uint32 crn_get_format_fourcc(crn_format fmt);
// Returns the crn_format's bits per texel.
crn_uint32 crn_get_format_bits_per_texel(crn_format fmt);
// Returns the crn_format's number of bytes per block.
crn_uint32 crn_get_bytes_per_dxt_block(crn_format fmt);
// Returns the non-swizzled, basic DXTn version of the specified crn_format.
// This is the format you would supply D3D or OpenGL.
crn_format crn_get_fundamental_dxt_format(crn_format fmt);
// -------- String helpers.
// Converts a crn_file_type to a string.
const char* crn_get_file_type_ext(crn_file_type file_type);
// Converts a crn_format to a string.
const char* crn_get_format_string(crn_format fmt);
// Converts a crn_dxt_quality to a string.
const char* crn_get_dxt_quality_string(crn_dxt_quality q);
// -------- Low-level DXTn 4x4 block compressor API
// crnlib's DXTn endpoint optimizer actually supports any number of source pixels (i.e. from 1 to thousands, not just 16),
// but for simplicity this API only supports 4x4 texel blocks.
typedef void* crn_block_compressor_context_t;
// Create a DXTn block compressor.
// This function only supports the basic/nonswizzled "fundamental" formats: DXT1, DXT3, DXT5, DXT5A, DXN_XY and DXN_YX.
// Avoid calling this multiple times if you intend on compressing many blocks, because it allocates some memory.
crn_block_compressor_context_t crn_create_block_compressor(const crn_comp_params& params);
// Compresses a block of 16 pixels to the destination DXTn block.
// pDst_block should be 8 (for DXT1/DXT5A) or 16 bytes (all the others).
// pPixels should be an array of 16 crn_uint32's. Each crn_uint32 must be r,g,b,a (r is always first) in memory.
void crn_compress_block(crn_block_compressor_context_t pContext, const crn_uint32* pPixels, void* pDst_block);
// Frees a DXTn block compressor.
void crn_free_block_compressor(crn_block_compressor_context_t pContext);
// Unpacks a compressed block to pDst_pixels.
// pSrc_block should be 8 (for DXT1/DXT5A) or 16 bytes (all the others).
// pDst_pixel should be an array of 16 crn_uint32's. Each uint32 will be r,g,b,a (r is always first) in memory.
// crn_fmt should be one of the "fundamental" formats: DXT1, DXT3, DXT5, DXT5A, DXN_XY and DXN_YX.
// The various swizzled DXT5 formats (such as cCRNFmtDXT5_xGBR, etc.) will be unpacked as if they where plain DXT5.
// Returns false if the crn_fmt is invalid.
bool crn_decompress_block(const void* pSrc_block, crn_uint32* pDst_pixels, crn_format crn_fmt);
#endif // CRNLIB_H
//------------------------------------------------------------------------------
//
// crnlib uses the ZLIB license:
// http://opensource.org/licenses/Zlib
//
// Copyright (c) 2010-2016 Richard Geldreich, Jr. and Binomial LLC
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
//------------------------------------------------------------------------------