Full Code of hglm/detex for AI

master cab11584d9be
60 files
352.7 KB
126.1k tokens
Showing preview only (367K chars total). The displayed content is truncated. Use the JSON API for full output.
Repository: hglm/detex
Branch: master
Commit: cab11584d9be
Files: 60
Total size: 352.7 KB

Directory structure:
gitextract_zmo0o45s/

├── .gitignore
├── LICENSE
├── Makefile
├── Makefile.conf
├── README
├── README.md
├── bits.c
├── bits.h
├── bptc-tables.c
├── bptc-tables.h
├── clamp.c
├── convert.c
├── dds.c
├── decompress-bc.c
├── decompress-bptc-float.c
├── decompress-bptc.c
├── decompress-eac.c
├── decompress-etc.c
├── decompress-rgtc.c
├── detex-convert.c
├── detex-png.h
├── detex-view.c
├── detex.h
├── division-tables.c
├── file-info.c
├── file-info.h
├── half-float.c
├── half-float.h
├── hdr.c
├── hdr.h
├── ktx.c
├── misc.c
├── misc.h
├── png.c
├── raw.c
├── test-texture-BC1.ktx
├── test-texture-BC1A.ktx
├── test-texture-BC2.ktx
├── test-texture-BC3.ktx
├── test-texture-BPTC.ktx
├── test-texture-BPTC_FLOAT.ktx
├── test-texture-EAC_R11.ktx
├── test-texture-EAC_RG11.ktx
├── test-texture-EAC_SIGNED_R11.ktx
├── test-texture-ETC1.ktx
├── test-texture-ETC2.ktx
├── test-texture-ETC2_EAC.ktx
├── test-texture-ETC2_PUNCHTHROUGH.ktx
├── test-texture-FLOAT_RGB16.ktx
├── test-texture-FLOAT_RGBA16.ktx
├── test-texture-RGB8.dds
├── test-texture-RGB8.ktx
├── test-texture-RGBA8.dds
├── test-texture-RGBA8.ktx
├── test-texture-RGTC1.ktx
├── test-texture-RGTC2.ktx
├── test-texture-SIGNED_RGTC1.ktx
├── test-texture-SIGNED_RGTC2.ktx
├── texture.c
└── validate.c

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# Object files
*.o
*.ko
*.obj
*.elf

# Precompiled Headers
*.gch
*.pch

# Libraries
*.lib
*.a
*.la
*.lo

# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib

# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex

# Debug files
*.dSYM/

# Misc.
.depend
detex-validate
detex-view


================================================
FILE: LICENSE
================================================
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.



================================================
FILE: Makefile
================================================

# Do not edit normally. Configuration settings are in Makefile.conf.

TARGET_MACHINE := $(shell gcc -dumpmachine)
include Makefile.conf

SHORT_LIBRARY_NAME = detex
LIBRARY_NAME = lib$(SHORT_LIBRARY_NAME)
VERSION = 0.1.2
SO_VERSION = 0.1
MAJOR_VERSION = 0

ifeq ($(LIBRARY_CONFIGURATION), DEBUG)
OPTCFLAGS += -ggdb
else
OPTCFLAGS += -Ofast -ffast-math
endif
# Use the 1999 ISO C standard with POSIX.1-2008 definitions.
CFLAGS = -std=c99 -D_POSIX_C_SOURCE=200809L -Wall -Wno-maybe-uninitialized -pipe -I. $(OPTCFLAGS)

ifeq ($(LIBRARY_CONFIGURATION), SHARED)
# Shared library.
LIBRARY_OBJECT = $(LIBRARY_NAME).so.$(VERSION)
INSTALL_TARGET = install_shared
LIBRARY_DEPENDENCY =
TEST_PROGRAM_LFLAGS = -l$(SHORT_LIBRARY_NAME)
CFLAGS_LIB = $(CFLAGS) -fPIC -fvisibility=hidden -DDST_SHARED -DDST_SHARED_EXPORTS
CFLAGS_TEST = $(CFLAGS)
else
# Static or static debug version.
ifeq ($(LIBRARY_CONFIGURATION), DEBUG)
LIBRARY_OBJECT = $(LIBRARY_NAME)_dbg.a
else
LIBRARY_OBJECT = $(LIBRARY_NAME).a
endif
# install_static also works for debugging library
INSTALL_TARGET = install_static
LIBRARY_DEPENDENCY = $(LIBRARY_OBJECT)
TEST_PROGRAM_LFLAGS = $(LIBRARY_OBJECT)
CFLAGS_LIB = $(CFLAGS)
CFLAGS_TEST = $(CFLAGS)
endif
CFLAGS_TEST += -DDETEX_VERSION=\"v$(VERSION)\"
LIBRARY_LIBS = -lm

LIBRARY_MODULE_OBJECTS = bptc-tables.o bits.o clamp.o convert.o dds.o decompress-bc.o decompress-bptc.o \
	decompress-bptc-float.o decompress-etc.o decompress-eac.o decompress-rgtc.o division-tables.o \
	file-info.o half-float.o hdr.o ktx.o misc.o raw.o texture.o
LIBRARY_HEADER_FILES = detex.h
TEST_PROGRAMS = detex-validate detex-view detex-convert

default : library

library : $(LIBRARY_OBJECT)

programs : $(TEST_PROGRAMS)

$(LIBRARY_NAME).so.$(VERSION) : $(LIBRARY_MODULE_OBJECTS) $(LIBRARY_HEADER_FILES)
	g++ -shared -Wl,-soname,$(LIBRARY_NAME).so.$(SO_VERSION) -fPIC -o $(LIBRARY_OBJECT) \
$(LIBRARY_MODULE_OBJECTS) $(LIBRARY_LIBS)
	@echo Run '(sudo) make install to install.'

$(LIBRARY_NAME).a : $(LIBRARY_MODULE_OBJECTS) $(LIBRARY_HEADER_FILES)
	ar r $(LIBRARY_OBJECT) $(LIBRARY_MODULE_OBJECTS)
	@echo 'Run (sudo) make install to install, or make programs to build the test programs.'

$(LIBRARY_NAME)_dbg.a : $(LIBRARY_MODULE_OBJECTS) $(LIBRARY_HEADER_FILES)
	ar r $(LIBRARY_OBJECT) $(LIBRARY_MODULE_OBJECTS)
	@echo 'The library is compiled with debugging enabled.'

install : $(INSTALL_TARGET) install_headers

install_headers : $(LIBRARY_HEADER_FILES)
	@for x in $(LIBRARY_HEADER_FILES); do \
	echo Installing $(HEADER_FILE_INSTALL_DIR)/$$x.; \
	install -m 0644 $$x $(HEADER_FILE_INSTALL_DIR)/$$x; done

install_shared : $(LIBRARY_OBJECT)
	install -m 0644 $(LIBRARY_OBJECT) $(SHARED_LIB_DIR)/$(LIBRARY_OBJECT)
	ln -sf $(SHARED_LIB_DIR)/$(LIBRARY_OBJECT) $(SHARED_LIB_DIR)/$(LIBRARY_NAME).so

install_static : $(LIBRARY_OBJECT)
	install -m 0644 $(LIBRARY_OBJECT) $(STATIC_LIB_DIR)/$(LIBRARY_OBJECT)

install-programs : detex-view detex-convert
	install -m 0755 detex-view $(PROGRAM_INSTALL_DIR)/detex-view
	install -m 0755 detex-convert $(PROGRAM_INSTALL_DIR)/detex-convert

detex-validate : validate.o $(LIBRARY_OBJECT)
	gcc validate.o -o detex-validate $(LIBRARY_OBJECT) $(LIBRARY_LIBS) `pkg-config --libs gtk+-3.0`

detex-view : detex-view.o $(LIBRARY_OBJECT)
	gcc detex-view.o -o detex-view $(LIBRARY_OBJECT) $(LIBRARY_LIBS) `pkg-config --libs gtk+-3.0`

detex-convert : detex-convert.o png.o $(LIBRARY_OBJECT)
	gcc detex-convert.o png.o -o detex-convert $(LIBRARY_OBJECT) $(LIBRARY_LIBS) `pkg-config --libs libpng`

clean :
	rm -f $(LIBRARY_MODULE_OBJECTS)
	rm -f $(TEST_PROGRAMS)
	rm -f validate.o
	rm -f detex-view.o
	rm -f detex-convert.o
	rm -f png.o
	rm -f $(LIBRARY_NAME).so.$(VERSION)
	rm -f $(LIBRARY_NAME).a
	rm -f $(LIBRARY_NAME)_dbg.a

.c.o :
	gcc -c $(CFLAGS_LIB) $< -o $@

validate.o : validate.c
	gcc -c $(CFLAGS_TEST) $< -o $@ `pkg-config --cflags --libs gtk+-3.0`

detex-view.o : detex-view.c
	gcc -c $(CFLAGS_TEST) $< -o $@ `pkg-config --cflags --libs gtk+-3.0`

detex-convert.o : detex-convert.c
	gcc -c $(CFLAGS_TEST) $< -o $@

png.o : png.c
	gcc -c $(CFLAGS_TEST) $< -o $@

dep :
	rm -f .depend
	make .depend

.depend: Makefile.conf Makefile
	gcc -MM $(CFLAGS_LIB) $(patsubst %.o,%.c,$(LIBRARY_MODULE_OBJECTS)) > .depend
        # Make sure Makefile.conf and Makefile are dependency for all modules.
	for x in $(LIBRARY_MODULE_OBJECTS); do \
	echo $$x : Makefile.conf Makefile >> .depend; done
	gcc -MM $(CFLAGS_TEST) validate.c >> .depend
	gcc -MM $(CFLAGS_TEST) detex-view.c >> .depend
	gcc -MM $(CFLAGS_TEST) detex-convert.c png.c >> .depend

include .depend



================================================
FILE: Makefile.conf
================================================
# LIBRARY_CONFIGURATION determines whether a shared or static library will
# be built. Supported values are SHARED, STATIC and DEBUG (static with
# debugging and no optimization). The INCLUDE_DIR is the installation
# directory for library header file.
#
# The HEADER_FILE_INSTALL_DIR is the installation directory prefix for library
# header files. They will be installed in the subdirectory DataSetTurbo.
#
# SHARED_LIB_INSTALL_DIR and STATIC_LIB_INSTALL_DIR are the installation
# directories of the shared and static libraries. The target architecture
# triplet string TARGET_MACHINE, obtained from gcc -dumpmachine, may be
# prefixed with /usr/lib to generate a path like /usr/lib/x86_64-gnu-linux.
#
# The variable TARGET_MACHINE has been initialized with the default library
# directory for the architecture.

LIBRARY_CONFIGURATION = STATIC
HEADER_FILE_INSTALL_DIR = /usr/include
SHARED_LIB_DIR = /usr/lib/$(TARGET_MACHINE)
STATIC_LIB_DIR = /usr/lib/$(TARGET_MACHINE)
PROGRAM_INSTALL_DIR = /usr/bin



================================================
FILE: README
================================================

---- Introduction ----

Detex is a low-level library that can be used to decompress textures and
texture blocks, as well as convert between a variety of pixel formats. It has
been developed using Linux, but is probably usable on other platforms without
too much effort.

Features include:

- Decompression of texture blocks compressed using formats including
  BC1/DXT1/S3TC, BC2, BC3, BC4/RGTC1, BC5/RGTC2, BC6 (BPTC_FLOAT), BC7 (BPTC),
  ETC1 and the ETC2 family.
- Flexible pixel format conversion functions between a variety of formats,
  including many uncompressed formats and mapping HDR textures.
- Loading and saving of KTX and DDS texture files.

Included is a simple texture file viewer program (detex-view) as well as a
command-line utility to convert between texture file formats (detex-convert).
Also included is a validation program (detex-validate) along with a set of test
texture files (test-texture*.*)

---- Installation ----

The libary configuration (static or shared, and installation paths) can be set
in the file Makefile.conf. Compilation as a static library is recommended. Run
make to compile the library, sudo make install to install. Compilation requires
gcc.

Run make programs to compile the programs detex-validate, detex-view and
detex-convert. Compilation of detex-convert requires the presence of libpng12
development headers (package libpng12-dev in Debian-based Linux distributions).
Compilation of detex-view and detex-validate requires the presence of GTK+ 3
development headers (package libgtk-3-dev in Debian). To install detex-view and
detex-convert, run make install-programs.

---- detex-convert ----

detex-convert is a command-line utility that converts between different texture
formats and texture file formats as well as PNG files. Both KTX and DDS texture
files are supported, with support for a large number of compressed and
uncompressed texture formats. The program also supports PNG files for input or
output and writing raw output (without header).

The command-line syntax is as follows:

	detex-convert [<OPTIONS>] <INPUTFILE> <OUTPUTFILE>

The input file and output file are mandatory. The type of input and output file
is auto-detected based on the extension (.ktx, .dds, .raw or .png). Without any
options, the program will convert between different texture file formats.

The following options are recognized:

--format <VALUE>, --format=<VALUE>, synonyms: -f, -o, --output-format

	Set the output format. The input texture will be converted to this
	format and written to the output file. Conversion from uncompressed to
	compressed formats is not supported.

--input-format <VALUE>, --input-format=<VALUE>, synonym: -i

	Override the input format. Normally, the input texture format is
        detected from the input file, this option overrides that value.

--decompress, synonym: -d

	When converting from a compressed input file, perform decompression to
	an uncompressed format that matches the compressed the texture format
	(for example, BC1/S3TC textures are decompressed to RGB8 format). When
	the output file is a PNG file, this option is not necessary.

--quiet

	Suppress messages.

---- Library documentation ----

At present, there is no specific documentation for library functions. However,
the functions are fairly self-explanatory and the library header file detex.h
contains a short description of what each function does.



================================================
FILE: README.md
================================================
# detex
Low-level library that can be used to decompress textures and texture blocks compressed using formats such as BC1/DXT1/S3TC, BC2-BC3, BC4/RGTC1, BC5/RGTC2, BC6 (BPTC_FLOAT), BC7 (BPTC), ETC1 and the ETC2 family.

It includes flexible pixel format conversion functions between a variety of formats, including several uncompressed formats and mapping HDR textures.

Additionally, loading and saving of KTX and DDS texture files is supported.

Included is a simple texture file viewer program as well as a command line utility to convert between texture file formats.

It has been developed using Linux, but is probably usable on other platforms without too much effort.



================================================
FILE: bits.c
================================================
/*

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.

*/

#include "detex.h"
#include "bits.h"

uint32_t detexBlock128ExtractBits(detexBlock128 *block, int nu_bits) {
	uint32_t value = 0;
	for (int i = 0; i < nu_bits; i++) {
		if (block->index < 64) {
			int shift = block->index - i;
			if (shift < 0)
				value |= (block->data0 & ((uint64_t)1 << block->index)) << (- shift);
			else
				value |= (block->data0 & ((uint64_t)1 << block->index)) >> shift;
		}
		else {
			int shift = ((block->index - 64) - i);
			if (shift < 0)
				value |= (block->data1 & ((uint64_t)1 << (block->index - 64))) << (- shift);
			else
				value |= (block->data1 & ((uint64_t)1 << (block->index - 64))) >> shift;
		}
		block->index++;
	}
//	if (block->index > 128)
//		printf("Block overflow (%d)\n", block->index);
	return value;
}



================================================
FILE: bits.h
================================================
/*

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.

*/

/* Data structure used to extract bits from 128-bit bitstring. */

typedef struct {
	uint64_t data0;
	uint64_t data1;
	int index;
} detexBlock128;

uint32_t detexBlock128ExtractBits(detexBlock128 *block, int nu_bits);

/* Return bitfield from bit0 to bit1 from 64-bit bitstring. */
static DETEX_INLINE_ONLY uint32_t detexGetBits64(uint64_t data, int bit0, int bit1) {
	return (data & (((uint64_t)1 << (bit1 + 1)) - 1)) >> bit0;
}

/* Return reversed bitfield (bit1 to bit0) from 64-bit bitstring. */
static DETEX_INLINE_ONLY uint32_t detexGetBits64Reversed(uint64_t data, int bit0, int bit1) {
	// Assumes bit0 > bit1.
	// Reverse the bits.
	uint32_t val = 0;
	for (int i = 0; i <= bit0 - bit1; i++) {
		int shift_right = bit0 - 2 * i;
		if (shift_right >= 0)
			val |= (data & ((uint64_t)1 << (bit0 - i))) >> shift_right;
		else
			val |= (data & ((uint64_t)1 << (bit0 - i))) << (- shift_right);
	}
	return val;
}

/* Clear bit0 to bit1 of 64-bit bitstring. */
static DETEX_INLINE_ONLY uint64_t detexClearBits64(uint64_t data, int bit0, int bit1) {
	uint64_t mask = ~(((uint64_t)1 << (bit1 + 1)) - 1);
	mask |= ((uint64_t)1 << bit0) - 1;
	return data & mask;
}

/* Set bit0 to bit1 of 64-bit bitstring. */
static DETEX_INLINE_ONLY uint64_t detexSetBits64(uint64_t data, int bit0, int bit1, uint64_t val) {
	uint64_t d = detexClearBits64(data, bit0, bit1);
	d |= val << bit0;
	return d;
}



================================================
FILE: bptc-tables.c
================================================
/*

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.

*/

#include "detex.h"
#include "bits.h"
#include "bptc-tables.h"

const uint8_t detex_bptc_table_P2[64 * 16] = {
    0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,
    0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,
    0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1,
    0,0,0,1,0,0,1,1,0,0,1,1,0,1,1,1,
    0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,1,
    0,0,1,1,0,1,1,1,0,1,1,1,1,1,1,1,
    0,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1,
    0,0,0,0,0,0,0,1,0,0,1,1,0,1,1,1,
    0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,
    0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,
    0,0,0,0,0,0,0,1,0,1,1,1,1,1,1,1,
    0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,
    0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,
    0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,
    0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,
    0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,
    0,0,0,0,1,0,0,0,1,1,1,0,1,1,1,1,
    0,1,1,1,0,0,0,1,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,0,
    0,1,1,1,0,0,1,1,0,0,0,1,0,0,0,0,
    0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,
    0,0,0,0,1,0,0,0,1,1,0,0,1,1,1,0,
    0,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,
    0,1,1,1,0,0,1,1,0,0,1,1,0,0,0,1,
    0,0,1,1,0,0,0,1,0,0,0,1,0,0,0,0,
    0,0,0,0,1,0,0,0,1,0,0,0,1,1,0,0,
    0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,
    0,0,1,1,0,1,1,0,0,1,1,0,1,1,0,0,
    0,0,0,1,0,1,1,1,1,1,1,0,1,0,0,0,
    0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,
    0,1,1,1,0,0,0,1,1,0,0,0,1,1,1,0,
    0,0,1,1,1,0,0,1,1,0,0,1,1,1,0,0,
    0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,
    0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,
    0,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0,
    0,0,1,1,0,0,1,1,1,1,0,0,1,1,0,0,
    0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,
    0,1,0,1,0,1,0,1,1,0,1,0,1,0,1,0,
    0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1,
    0,1,0,1,1,0,1,0,1,0,1,0,0,1,0,1,
    0,1,1,1,0,0,1,1,1,1,0,0,1,1,1,0,
    0,0,0,1,0,0,1,1,1,1,0,0,1,0,0,0,
    0,0,1,1,0,0,1,0,0,1,0,0,1,1,0,0,
    0,0,1,1,1,0,1,1,1,1,0,1,1,1,0,0,
    0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0,
    0,0,1,1,1,1,0,0,1,1,0,0,0,0,1,1,
    0,1,1,0,0,1,1,0,1,0,0,1,1,0,0,1,
    0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,
    0,1,0,0,1,1,1,0,0,1,0,0,0,0,0,0,
    0,0,1,0,0,1,1,1,0,0,1,0,0,0,0,0,
    0,0,0,0,0,0,1,0,0,1,1,1,0,0,1,0,
    0,0,0,0,0,1,0,0,1,1,1,0,0,1,0,0,
    0,1,1,0,1,1,0,0,1,0,0,1,0,0,1,1,
    0,0,1,1,0,1,1,0,1,1,0,0,1,0,0,1,
    0,1,1,0,0,0,1,1,1,0,0,1,1,1,0,0,
    0,0,1,1,1,0,0,1,1,1,0,0,0,1,1,0,
    0,1,1,0,1,1,0,0,1,1,0,0,1,0,0,1,
    0,1,1,0,0,0,1,1,0,0,1,1,1,0,0,1,
    0,1,1,1,1,1,1,0,1,0,0,0,0,0,0,1,
    0,0,0,1,1,0,0,0,1,1,1,0,0,1,1,1,
    0,0,0,0,1,1,1,1,0,0,1,1,0,0,1,1,
    0,0,1,1,0,0,1,1,1,1,1,1,0,0,0,0,
    0,0,1,0,0,0,1,0,1,1,1,0,1,1,1,0,
    0,1,0,0,0,1,0,0,0,1,1,1,0,1,1,1
};

const uint8_t detex_bptc_table_P3[64 * 16] = {
    0,0,1,1,0,0,1,1,0,2,2,1,2,2,2,2,
    0,0,0,1,0,0,1,1,2,2,1,1,2,2,2,1,
    0,0,0,0,2,0,0,1,2,2,1,1,2,2,1,1,
    0,2,2,2,0,0,2,2,0,0,1,1,0,1,1,1,
    0,0,0,0,0,0,0,0,1,1,2,2,1,1,2,2,
    0,0,1,1,0,0,1,1,0,0,2,2,0,0,2,2,
    0,0,2,2,0,0,2,2,1,1,1,1,1,1,1,1,
    0,0,1,1,0,0,1,1,2,2,1,1,2,2,1,1,
    0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,
    0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,
    0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,
    0,0,1,2,0,0,1,2,0,0,1,2,0,0,1,2,
    0,1,1,2,0,1,1,2,0,1,1,2,0,1,1,2,
    0,1,2,2,0,1,2,2,0,1,2,2,0,1,2,2,
    0,0,1,1,0,1,1,2,1,1,2,2,1,2,2,2,
    0,0,1,1,2,0,0,1,2,2,0,0,2,2,2,0,
    0,0,0,1,0,0,1,1,0,1,1,2,1,1,2,2,
    0,1,1,1,0,0,1,1,2,0,0,1,2,2,0,0,
    0,0,0,0,1,1,2,2,1,1,2,2,1,1,2,2,
    0,0,2,2,0,0,2,2,0,0,2,2,1,1,1,1,
    0,1,1,1,0,1,1,1,0,2,2,2,0,2,2,2,
    0,0,0,1,0,0,0,1,2,2,2,1,2,2,2,1,
    0,0,0,0,0,0,1,1,0,1,2,2,0,1,2,2,
    0,0,0,0,1,1,0,0,2,2,1,0,2,2,1,0,
    0,1,2,2,0,1,2,2,0,0,1,1,0,0,0,0,
    0,0,1,2,0,0,1,2,1,1,2,2,2,2,2,2,
    0,1,1,0,1,2,2,1,1,2,2,1,0,1,1,0,
    0,0,0,0,0,1,1,0,1,2,2,1,1,2,2,1,
    0,0,2,2,1,1,0,2,1,1,0,2,0,0,2,2,
    0,1,1,0,0,1,1,0,2,0,0,2,2,2,2,2,
    0,0,1,1,0,1,2,2,0,1,2,2,0,0,1,1,
    0,0,0,0,2,0,0,0,2,2,1,1,2,2,2,1,
    0,0,0,0,0,0,0,2,1,1,2,2,1,2,2,2,
    0,2,2,2,0,0,2,2,0,0,1,2,0,0,1,1,
    0,0,1,1,0,0,1,2,0,0,2,2,0,2,2,2,
    0,1,2,0,0,1,2,0,0,1,2,0,0,1,2,0,
    0,0,0,0,1,1,1,1,2,2,2,2,0,0,0,0,
    0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0,
    0,1,2,0,2,0,1,2,1,2,0,1,0,1,2,0,
    0,0,1,1,2,2,0,0,1,1,2,2,0,0,1,1,
    0,0,1,1,1,1,2,2,2,2,0,0,0,0,1,1,
    0,1,0,1,0,1,0,1,2,2,2,2,2,2,2,2,
    0,0,0,0,0,0,0,0,2,1,2,1,2,1,2,1,
    0,0,2,2,1,1,2,2,0,0,2,2,1,1,2,2,
    0,0,2,2,0,0,1,1,0,0,2,2,0,0,1,1,
    0,2,2,0,1,2,2,1,0,2,2,0,1,2,2,1,
    0,1,0,1,2,2,2,2,2,2,2,2,0,1,0,1,
    0,0,0,0,2,1,2,1,2,1,2,1,2,1,2,1,
    0,1,0,1,0,1,0,1,0,1,0,1,2,2,2,2,
    0,2,2,2,0,1,1,1,0,2,2,2,0,1,1,1,
    0,0,0,2,1,1,1,2,0,0,0,2,1,1,1,2,
    0,0,0,0,2,1,1,2,2,1,1,2,2,1,1,2,
    0,2,2,2,0,1,1,1,0,1,1,1,0,2,2,2,
    0,0,0,2,1,1,1,2,1,1,1,2,0,0,0,2,
    0,1,1,0,0,1,1,0,0,1,1,0,2,2,2,2,
    0,0,0,0,0,0,0,0,2,1,1,2,2,1,1,2,
    0,1,1,0,0,1,1,0,2,2,2,2,2,2,2,2,
    0,0,2,2,0,0,1,1,0,0,1,1,0,0,2,2,
    0,0,2,2,1,1,2,2,1,1,2,2,0,0,2,2,
    0,0,0,0,0,0,0,0,0,0,0,0,2,1,1,2,
    0,0,0,2,0,0,0,1,0,0,0,2,0,0,0,1,
    0,2,2,2,1,2,2,2,0,2,2,2,1,2,2,2,
    0,1,0,1,2,2,2,2,2,2,2,2,2,2,2,2,
    0,1,1,1,2,0,1,1,2,2,0,1,2,2,2,0,
};

const uint8_t detex_bptc_table_anchor_index_second_subset[64] = {
    15,15,15,15,15,15,15,15,
    15,15,15,15,15,15,15,15,
    15, 2, 8, 2, 2, 8, 8,15,
     2, 8, 2, 2, 8, 8, 2, 2,
    15,15, 6, 8, 2, 8,15,15,
     2, 8, 2, 2, 2,15,15, 6,
     6, 2, 6, 8,15,15, 2, 2,
    15,15,15,15,15, 2, 2,15
};

const uint8_t detex_bptc_table_anchor_index_second_subset_of_three[64] = {
     3, 3,15,15, 8, 3,15,15,
     8, 8, 6, 6, 6, 5, 3, 3,
     3, 3, 8,15, 3, 3, 6,10,
     5, 8, 8, 6, 8, 5,15,15,
     8,15, 3, 5, 6,10, 8,15,
    15, 3,15, 5,15,15,15,15,
     3,15, 5, 5, 5, 8, 5,10,
     5,10, 8,13,15,12, 3, 3
};

const uint8_t detex_bptc_table_anchor_index_third_subset[64] = {
    15, 8, 8, 3,15,15, 3, 8,
    15,15,15,15,15,15,15, 8,
    15, 8,15, 3,15, 8,15, 8,
     3,15, 6,10,15,15,10, 8,
    15, 3,15,10,10, 8, 9,10,
     6,15, 8,15, 3, 6, 6, 8,
    15, 3,15,15,15,15,15,15,
    15,15,15,15, 3,15,15, 8
};

const uint16_t detex_bptc_table_aWeight2[4] = {
	0, 21, 43, 64
};

const uint16_t detex_bptc_table_aWeight3[8] = {
	0, 9, 18, 27, 37, 46, 55, 64
};

const uint16_t detex_bptc_table_aWeight4[16] = {
	0, 4, 9, 13, 17, 21, 26, 30,
	34, 38, 43, 47, 51, 55, 60, 64
};




================================================
FILE: bptc-tables.h
================================================
/*

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.

*/

extern const uint8_t detex_bptc_table_P2[64 * 16];
extern const uint8_t detex_bptc_table_P3[64 * 16];

extern const uint8_t detex_bptc_table_anchor_index_second_subset[64];
extern const uint8_t detex_bptc_table_anchor_index_second_subset_of_three[64];
extern const uint8_t detex_bptc_table_anchor_index_third_subset[64];

extern const uint16_t detex_bptc_table_aWeight2[4];
extern const uint16_t detex_bptc_table_aWeight3[8];
extern const uint16_t detex_bptc_table_aWeight4[16];



================================================
FILE: clamp.c
================================================
/*

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.

*/

#include "detex.h"

// Define an array to speed up clamping of values to the ranges 0 to 255.

const uint8_t detex_clamp0to255_table[255 + 256 + 256] = {
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
	17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
	33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
	49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
	65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
	81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
	97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
	113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
	129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144,
	145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160,
	161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176,
	177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192,
	193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208,
	209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
	225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240,
	241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 255,
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
};




================================================
FILE: convert.c
================================================
/*

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.

*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "detex.h"
#include "half-float.h"
#include "hdr.h"
#include "misc.h"

// Conversion functions. For conversions where the pixel size is unchanged,
// the conversion is performed in-place and target_pixel_buffer will be NULL.

static void ConvertNoop(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
}

// In-place BGR <-> RGB conversions.

static void ConvertPixel32RGBA8ToPixel32BGRA8(uint8_t * DETEX_RESTRICT source_pixel_buffer, int nu_pixels,
uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint32_t *source_pixel32_buffer = (uint32_t *)source_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		/* Swap R and B. */
		uint32_t pixel = *source_pixel32_buffer;
		pixel = detexPack32RGBA8(
			detexPixel32GetB8(pixel),
			detexPixel32GetG8(pixel),
			detexPixel32GetR8(pixel),
			detexPixel32GetA8(pixel)
			);
		*source_pixel32_buffer = pixel;
		source_pixel32_buffer++;
	}
}

static void ConvertPixel64RGBX16ToPixel64BGRX16(uint8_t * DETEX_RESTRICT source_pixel_buffer, int nu_pixels,
uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint64_t *source_pixel64_buffer = (uint64_t *)source_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		/* Swap R and B (16-bit). */
		uint64_t pixel = *source_pixel64_buffer;
		pixel = detexPack64RGBA16(
			detexPixel64GetB16(pixel),
			detexPixel64GetG16(pixel),
			detexPixel64GetR16(pixel),
			detexPixel64GetA16(pixel)
			);
		*source_pixel64_buffer = pixel;
		source_pixel64_buffer++;
	}
}

// Swapping red and blue (not in-place).

static void ConvertPixel24RGB8ToPixel32BGRX8(uint8_t * DETEX_RESTRICT source_pixel_buffer, int nu_pixels,
uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint32_t *target_pixel32_buffer = (uint32_t *)target_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		/* Swap R and B. */
		uint32_t red = source_pixel_buffer[0];
		uint32_t green = source_pixel_buffer[1];
		uint32_t blue = source_pixel_buffer[2];
		uint32_t pixel = detexPack32RGB8Alpha0xFF(blue, green, red);
		*target_pixel32_buffer = pixel;
		source_pixel_buffer += 3;
		target_pixel32_buffer++;
	}
}

// In-place signed integer conversions (8-bit components).

static void ConvertPixel8R8ToPixel8SignedR8(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	for (int i = 0; i < nu_pixels; i++) {
		int32_t red = *source_pixel_buffer;
		red -= 128;
		*source_pixel_buffer = red;
		source_pixel_buffer++;
	}
}

static void ConvertPixel16RG8ToPixel16SignedRG8(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint16_t *source_pixel16_buffer = (uint16_t *)source_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		uint32_t pixel = *source_pixel16_buffer;
		uint32_t red = (uint8_t)((int)detexPixel32GetR8(pixel) - 128);
		uint32_t green = (uint8_t)((int)detexPixel32GetG8(pixel) - 128);
		*source_pixel16_buffer = detexPack32RG8(red, green);
		source_pixel16_buffer++;
	}
}

static void ConvertPixel8SignedR8ToPixel8R8(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	for (int i = 0; i < nu_pixels; i++) {
		int32_t red = *(int8_t *)source_pixel_buffer + 128;
		*source_pixel_buffer = (uint8_t)red;
		source_pixel_buffer++;
	}
}

static void ConvertPixel16SignedRG8ToPixel16RG8(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint16_t *source_pixel16_buffer = (uint16_t *)source_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		uint32_t pixel = *source_pixel16_buffer;
		uint32_t red = (uint8_t)(detexPixel32GetSignedR8(pixel) + 128);
		uint32_t green = (uint8_t)(detexPixel32GetSignedG8(pixel) + 128);
		*source_pixel16_buffer = detexPack32RG8(red, green);
		source_pixel16_buffer++;
	}
}

// In-place signed integer conversions (16-bit components).

static void ConvertPixel16R16ToPixel16SignedR16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint16_t *source_pixel16_buffer = (uint16_t *)source_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		int32_t red = *source_pixel16_buffer;
		red -= 32768;
		*(int16_t *)source_pixel16_buffer = (int16_t)red;
		source_pixel16_buffer++;
	}
}

static void ConvertPixel32RG16ToPixel32SignedRG16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint32_t *source_pixel32_buffer = (uint32_t *)source_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		uint32_t pixel = *source_pixel32_buffer;
		uint32_t red = (uint16_t)((int)detexPixel32GetR16(pixel) - 32768);
		uint32_t green = (uint16_t)((int)detexPixel32GetG16(pixel) - 32768);
		*source_pixel32_buffer = detexPack32RG16(red, green);
		source_pixel32_buffer++;
	}
}

static void ConvertPixel16SignedR16ToPixel16R16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint16_t *source_pixel16_buffer = (uint16_t *)source_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		int32_t red = *(int16_t *)source_pixel16_buffer;
		red += 32768;
		*source_pixel16_buffer = (uint16_t)red;
		source_pixel16_buffer++;
	}
}

static void ConvertPixel32SignedRG16ToPixel32RG16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint32_t *source_pixel32_buffer = (uint32_t *)source_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		uint32_t pixel = *source_pixel32_buffer;
		uint32_t red = (uint16_t)(detexPixel32GetSignedR16(pixel) + 32768);
		uint32_t green = (uint16_t)(detexPixel32GetSignedG16(pixel) + 32768);
		*source_pixel32_buffer = detexPack32RG16(red, green);
		source_pixel32_buffer++;
	}
}

// Reducing the number of components.

static void ConvertPixel32RGBA8ToPixel8R8(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint32_t *source_pixel32_buffer = (uint32_t *)source_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		uint32_t pixel = *source_pixel32_buffer;
		*target_pixel_buffer = detexPixel32GetR8(pixel);
		source_pixel32_buffer++;
		target_pixel_buffer++;
	}
}

static void ConvertPixel32RGBA8ToPixel16RG8(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint32_t *source_pixel32_buffer = (uint32_t *)source_pixel_buffer;
	uint16_t *target_pixel16_buffer = (uint16_t *)target_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		uint32_t pixel = *source_pixel32_buffer;
		*target_pixel16_buffer = (uint16_t)detexPack32RG8(
			detexPixel32GetR8(pixel), detexPixel32GetG8(pixel));
		source_pixel32_buffer++;
		target_pixel16_buffer++;
	}
}

static void ConvertPixel24RGB8ToPixel8R8(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	for (int i = 0; i < nu_pixels; i++) {
		uint32_t red = source_pixel_buffer[0];
		*target_pixel_buffer = red;
		source_pixel_buffer += 3;
		target_pixel_buffer++;
	}
}

static void ConvertPixel24RGB8ToPixel16RG8(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	for (int i = 0; i < nu_pixels; i++) {
		uint32_t red = source_pixel_buffer[0];
		uint32_t green = source_pixel_buffer[1];
		target_pixel_buffer[0] = red;
		target_pixel_buffer[1] = green;
		source_pixel_buffer += 3;
		target_pixel_buffer += 2;
	}
}

// Increasing the number of components.

static void ConvertPixel8R8ToPixel32RGBX8(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint32_t *target_pixel32_buffer = (uint32_t *)target_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		uint32_t red = *source_pixel_buffer;
		*target_pixel32_buffer = detexPack32RGB8Alpha0xFF(red, 0, 0);
		source_pixel_buffer++;
		target_pixel32_buffer++;
	}
}

static void ConvertPixel16RG8ToPixel32RGBX8(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint16_t *source_pixel16_buffer = (uint16_t *)source_pixel_buffer;
	uint32_t *target_pixel32_buffer = (uint32_t *)target_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		uint32_t pixel = *source_pixel16_buffer;
		uint32_t red = detexPixel32GetR8(pixel);
		uint32_t green = detexPixel32GetG8(pixel);
		*target_pixel32_buffer = detexPack32RGB8Alpha0xFF(red, green, 0);
		source_pixel16_buffer++;
		target_pixel32_buffer++;
	}
}

// Conversion to component of different size.

static void ConvertPixel16R16ToPixel8R8(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint16_t *source_pixel16_buffer = (uint16_t *)source_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		uint32_t pixel = *source_pixel16_buffer;
		*target_pixel_buffer = (detexPixel32GetR16(pixel) + 127) * 255 / 65535;
		source_pixel16_buffer++;
		target_pixel_buffer++;
	}
}

static void ConvertPixel32RG16ToPixel16RG8(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint32_t *source_pixel32_buffer = (uint32_t *)source_pixel_buffer;
	uint16_t *target_pixel16_buffer = (uint16_t *)target_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		uint32_t pixel = *source_pixel32_buffer;
		*target_pixel16_buffer = (uint16_t)detexPack32RG8(
			(detexPixel32GetR16(pixel) + 127) * 255 / 65535,
			(detexPixel32GetG16(pixel) + 127) * 255 / 65535	
			);
		source_pixel32_buffer++;
		target_pixel16_buffer++;
	}
}

static void ConvertPixel48RGB16ToPixel24RGB8(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint16_t *source_pixel16_buffer = (uint16_t *)source_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		uint32_t red = source_pixel16_buffer[0];
		uint32_t green = source_pixel16_buffer[1];
		uint32_t blue = source_pixel16_buffer[2];
		target_pixel_buffer[0] = (red + 127) * 255 / 65535;
		target_pixel_buffer[1] = (green + 127) * 255 / 65535;
		target_pixel_buffer[2] = (blue + 127) * 255 / 65535;
		source_pixel16_buffer += 3;
		target_pixel_buffer += 3;
	}
}

static void ConvertPixel64RGBX16ToPixel32RGBX8(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint64_t *source_pixel64_buffer = (uint64_t *)source_pixel_buffer;
	uint32_t *target_pixel32_buffer = (uint32_t *)target_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		uint64_t pixel = *source_pixel64_buffer;
		*target_pixel32_buffer = (uint32_t)detexPack32RGB8Alpha0xFF(
			(detexPixel64GetR16(pixel) + 127) * 255 / 65535,
			(detexPixel64GetG16(pixel) + 127) * 255 / 65535,
			(detexPixel64GetB16(pixel) + 127) * 255 / 65535	
			);
		source_pixel64_buffer++;
		target_pixel32_buffer++;
	}
}

static void ConvertPixel64RGBA16ToPixel32RGBA8(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint64_t *source_pixel64_buffer = (uint64_t *)source_pixel_buffer;
	uint32_t *target_pixel32_buffer = (uint32_t *)target_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		uint64_t pixel = *source_pixel64_buffer;
		*target_pixel32_buffer = (uint32_t)detexPack32RGBA8(
			(detexPixel64GetR16(pixel) + 127) * 255 / 65535,
			(detexPixel64GetG16(pixel) + 127) * 255 / 65535,
			(detexPixel64GetB16(pixel) + 127) * 255 / 65535,
			(detexPixel64GetA16(pixel) + 127) * 255 / 65535	
			);
		source_pixel64_buffer++;
		target_pixel32_buffer++;
	}
}

static void ConvertPixel8R8ToPixel16R16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint16_t *target_pixel16_buffer = (uint16_t *)target_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		uint32_t pixel = *source_pixel_buffer;
		*target_pixel16_buffer = pixel * 65535 / 255;
		source_pixel_buffer++;
		target_pixel16_buffer++;
	}
}

static void ConvertPixel16RG8ToPixel32RG16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint16_t *source_pixel16_buffer = (uint16_t *)source_pixel_buffer;
	uint32_t *target_pixel32_buffer = (uint32_t *)target_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		uint32_t pixel = *source_pixel16_buffer;
		*target_pixel32_buffer = detexPack32RG16(
			detexPixel32GetR8(pixel) * 65535 / 255,
			detexPixel32GetG8(pixel) * 65535 / 255
			);
		source_pixel16_buffer++;
		target_pixel32_buffer++;
	}
}

static void ConvertPixel24RGB8ToPixel48RGB16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint16_t *target_pixel16_buffer = (uint16_t *)target_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		uint32_t red = source_pixel_buffer[0];
		uint32_t green = source_pixel_buffer[1];
		uint32_t blue = source_pixel_buffer[2];
		target_pixel16_buffer[0] = red * 65535 / 255;
		target_pixel16_buffer[1] = green * 65535 / 255;
		target_pixel16_buffer[2] = blue * 65535 / 255;
		source_pixel_buffer += 3;
		target_pixel16_buffer += 3;
	}
}

static void ConvertPixel32RGBX8ToPixel64RGBX16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint32_t *source_pixel32_buffer = (uint32_t *)source_pixel_buffer;
	uint64_t *target_pixel64_buffer = (uint64_t *)target_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		uint32_t pixel = *source_pixel32_buffer;
		*target_pixel64_buffer = detexPack64RGBA16(
			detexPixel32GetR8(pixel) * 65535 / 255,
			detexPixel32GetG8(pixel) * 65535 / 255,
			detexPixel32GetB8(pixel) * 65535 / 255,
			0xFFFF
			);
		source_pixel32_buffer++;
		target_pixel64_buffer++;
	}
}

static void ConvertPixel32RGBA8ToPixel64RGBA16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint32_t *source_pixel32_buffer = (uint32_t *)source_pixel_buffer;
	uint64_t *target_pixel64_buffer = (uint64_t *)target_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		uint32_t pixel = *source_pixel32_buffer;
		*target_pixel64_buffer = detexPack64RGBA16(
			detexPixel32GetR8(pixel) * 65535 / 255,
			detexPixel32GetG8(pixel) * 65535 / 255,
			detexPixel32GetB8(pixel) * 65535 / 255,
			detexPixel32GetA8(pixel) * 65535 / 255
			);
		source_pixel32_buffer++;
		target_pixel64_buffer++;
	}
}

// Float to half-float conversion.

static void ConvertPixel32FloatR32ToPixel16FloatR16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	detexConvertFloatToHalfFloat((float *)source_pixel_buffer, nu_pixels, (uint16_t *)target_pixel_buffer);
}

static void ConvertPixel64FloatRG32ToPixel32FloatRG16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	detexConvertFloatToHalfFloat((float *)source_pixel_buffer, nu_pixels * 2, (uint16_t *)target_pixel_buffer);
}

static void ConvertPixel96FloatRGB32ToPixel48FloatRGB16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	detexConvertFloatToHalfFloat((float *)source_pixel_buffer, nu_pixels * 3, (uint16_t *)target_pixel_buffer);
}

static void ConvertPixel128FloatRGBX32ToPixel64FloatRGBX16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	detexConvertFloatToHalfFloat((float *)source_pixel_buffer, nu_pixels * 4, (uint16_t *)target_pixel_buffer);
}

// Float to 16-bit integer conversion.

static void ConvertPixel32FloatR32ToPixel16R16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	detexConvertNormalizedFloatToUInt16((float *)source_pixel_buffer, nu_pixels, (uint16_t *)target_pixel_buffer);
}

static void ConvertPixel64FloatRG32ToPixel32RG16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	detexConvertNormalizedFloatToUInt16((float *)source_pixel_buffer, nu_pixels * 2, (uint16_t *)target_pixel_buffer);
}

static void ConvertPixel96FloatRGB32ToPixel48RGB16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	detexConvertNormalizedFloatToUInt16((float *)source_pixel_buffer, nu_pixels * 3, (uint16_t *)target_pixel_buffer);
}

static void ConvertPixel128FloatRGBX32ToPixel64RGBX16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	detexConvertNormalizedFloatToUInt16((float *)source_pixel_buffer, nu_pixels * 4, (uint16_t *)target_pixel_buffer);
}

// Half-float to float conversion.

static void ConvertPixel16FloatR16ToPixel32FloatR32(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	detexConvertHalfFloatToFloat((uint16_t *)source_pixel_buffer, nu_pixels, (float *)target_pixel_buffer);
}

static void ConvertPixel32FloatRG16ToPixel64FloatRG32(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	detexConvertHalfFloatToFloat((uint16_t *)source_pixel_buffer, nu_pixels * 2, (float *)target_pixel_buffer);
}

static void ConvertPixel48FloatRGB16ToPixel96FloatRGB32(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	detexConvertHalfFloatToFloat((uint16_t *)source_pixel_buffer, nu_pixels * 3, (float *)target_pixel_buffer);
}

static void ConvertPixel64FloatRGBX16ToPixel128FloatRGBX32(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	detexConvertHalfFloatToFloat((uint16_t *)source_pixel_buffer, nu_pixels * 4, (float *)target_pixel_buffer);
}

// Conversion from 16-bit integer to half-float (in-place).

static void ConvertPixel16R16ToPixel16FloatR16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	float float_buffer[64];
	uint16_t *source_pixel16_buffer = (uint16_t *)source_pixel_buffer;
	// Split conversion into stages.
	for (int i = 0; i < nu_pixels; i += 64) {
		float *target_pixelf_buffer = (float *)float_buffer;
		int nu_stage_pixels = 64;
		if (i + 64 > nu_pixels)
			nu_stage_pixels = nu_pixels - i;
		for (int j = 0; j < nu_stage_pixels; j++) {
			int red = *source_pixel16_buffer;
			float redf = red * (1.0f / 65535.0f);
			*target_pixelf_buffer = redf;
			source_pixel16_buffer++;
			target_pixelf_buffer++;
		}
		ConvertPixel32FloatR32ToPixel16FloatR16((uint8_t *)float_buffer,
			nu_stage_pixels, source_pixel_buffer);
		source_pixel_buffer += nu_stage_pixels * 2;
	}
}

static void ConvertPixel32RG16ToPixel32FloatRG16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	float float_buffer[128];
	uint16_t *source_pixel16_buffer = (uint16_t *)source_pixel_buffer;
	// Split conversion into stages.
	for (int i = 0; i < nu_pixels; i += 64) {
		float *target_pixelf_buffer = (float *)float_buffer;
		int nu_stage_pixels = 64;
		if (i + 64 > nu_pixels)
			nu_stage_pixels = nu_pixels - i;
		for (int j = 0; j < nu_stage_pixels; j++) {
			int red = source_pixel16_buffer[0];
			int green = source_pixel16_buffer[1];
			float redf = red * (1.0f / 65535.0f);
			float greenf = green * (1.0f / 65535.0f);
			target_pixelf_buffer[0] = redf;
			target_pixelf_buffer[1] = greenf;
			source_pixel16_buffer += 2;
			target_pixelf_buffer += 2;
		}
		ConvertPixel64FloatRG32ToPixel32FloatRG16((uint8_t *)float_buffer,
			nu_stage_pixels, source_pixel_buffer);
		source_pixel_buffer += nu_stage_pixels * 4;
	}
}

static void ConvertPixel48RGB16ToPixel48FloatRGB16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	float float_buffer[192];
	uint16_t *source_pixel16_buffer = (uint16_t *)source_pixel_buffer;
	// Split conversion into stages.
	for (int i = 0; i < nu_pixels; i += 64) {
		float *target_pixelf_buffer = (float *)float_buffer;
		int nu_stage_pixels = 64;
		if (i + 64 > nu_pixels)
			nu_stage_pixels = nu_pixels - i;
		for (int j = 0; j < nu_stage_pixels; j++) {
			int red = source_pixel16_buffer[0];
			int green = source_pixel16_buffer[1];
			int blue = source_pixel16_buffer[2];
			float redf = red * (1.0f / 65535.0f);
			float greenf = green * (1.0f / 65535.0f);
			float bluef = blue * (1.0f / 65535.0f);
			target_pixelf_buffer[0] = redf;
			target_pixelf_buffer[1] = greenf;
			target_pixelf_buffer[2] = bluef;
			source_pixel16_buffer += 3;
			target_pixelf_buffer += 3;
		}
		ConvertPixel96FloatRGB32ToPixel48FloatRGB16((uint8_t *)float_buffer,
			nu_stage_pixels, source_pixel_buffer);
		source_pixel_buffer += nu_stage_pixels * 6;
	}
}

static void ConvertPixel64RGBX16ToPixel64FloatRGBX16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	float float_buffer[128];
	uint16_t *source_pixel16_buffer = (uint16_t *)source_pixel_buffer;
	// Split conversion into stages.
	for (int i = 0; i < nu_pixels; i += 32) {
		float *target_pixelf_buffer = (float *)float_buffer;
		int nu_stage_pixels = 32;
		if (i + 32 > nu_pixels)
			nu_stage_pixels = nu_pixels - i;
		for (int j = 0; j < nu_stage_pixels; j++) {
			int16_t red = source_pixel16_buffer[0];
			int16_t green = source_pixel16_buffer[1];
			int16_t blue = source_pixel16_buffer[2];
			float redf = red * (1.0f / 65535.0f);
			float greenf = green * (1.0f / 65535.0f);
			float bluef = blue  * (1.0f / 65535.0f);
			target_pixelf_buffer[0] = redf;
			target_pixelf_buffer[1] = greenf;
			target_pixelf_buffer[2] = bluef;
			target_pixelf_buffer[3] = 1.0f;
			source_pixel16_buffer += 4;
			target_pixelf_buffer += 4;
		}
		ConvertPixel128FloatRGBX32ToPixel64FloatRGBX16((uint8_t *)float_buffer,
			nu_stage_pixels, source_pixel_buffer);
		source_pixel_buffer += nu_stage_pixels * 8;
	}
}

// Conversion from normalized half-float to 16-bit integer (in-place).

static void ConvertPixel16FloatR16ToPixel16R16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	detexConvertNormalizedHalfFloatToUInt16((uint16_t *)source_pixel_buffer, nu_pixels);
}

static void ConvertPixel32FloatRG16ToPixel32RG16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	detexConvertNormalizedHalfFloatToUInt16((uint16_t *)source_pixel_buffer, nu_pixels * 2);
}

static void ConvertPixel48FloatRGB16ToPixel48RGB16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	detexConvertNormalizedHalfFloatToUInt16((uint16_t *)source_pixel_buffer, nu_pixels * 3);
}

static void ConvertPixel64FloatRGBX16ToPixel64RGBX16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	detexConvertNormalizedHalfFloatToUInt16((uint16_t *)source_pixel_buffer, nu_pixels * 4);
}

static void ConvertPixel64FloatRGBA16ToPixel64RGBA16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	detexConvertNormalizedHalfFloatToUInt16((uint16_t *)source_pixel_buffer, nu_pixels * 4);
}

// Conversion from HDR half-float to 16-bit integer (in-place). Depends on gamma parameters.

static void ConvertPixel16FloatR16HDRToPixel16R16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint16_t *source_pixel16_buffer = (uint16_t *)source_pixel_buffer;
	detexConvertHDRHalfFloatToUInt16(source_pixel16_buffer, nu_pixels);
}

static void ConvertPixel32FloatRG16HDRToPixel32RG16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint16_t *source_pixel16_buffer = (uint16_t *)source_pixel_buffer;
	detexConvertHDRHalfFloatToUInt16(source_pixel16_buffer, nu_pixels * 2);
}

static void ConvertPixel64FloatRGBX16HDRToPixel64RGBX16(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint16_t *source_pixel16_buffer = (uint16_t *)source_pixel_buffer;
	detexConvertHDRHalfFloatToUInt16(source_pixel16_buffer, nu_pixels * 4);
}

// Conversion HDR float to float (in_place).

static void ConvertPixel32FloatR32HDRToPixel32FloatR32(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	float *source_pixelf_buffer = (float *)source_pixel_buffer;
	detexConvertHDRFloatToFloat(source_pixelf_buffer, nu_pixels);
}

static void ConvertPixel64FloatRG32HDRToPixel64FloatRG32(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	float *source_pixelf_buffer = (float *)source_pixel_buffer;
	detexConvertHDRFloatToFloat(source_pixelf_buffer, nu_pixels * 2);
}

static void ConvertPixel96FloatRGB32HDRToPixel96FloatRGB32(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	float *source_pixelf_buffer = (float *)source_pixel_buffer;
	detexConvertHDRFloatToFloat(source_pixelf_buffer, nu_pixels * 3);
}

static void ConvertPixel128FloatRGBX32HDRToPixel128FloatRGBX32(uint8_t * DETEX_RESTRICT source_pixel_buffer,
int nu_pixels, uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	float *source_pixelf_buffer = (float *)source_pixel_buffer;
	detexConvertHDRFloatToFloat(source_pixelf_buffer, nu_pixels * 4);
}

// Conversion between packed RGB8 and RGBX8 and vice-versa.

static void ConvertPixel24RGB8ToPixel32RGBX8(uint8_t * DETEX_RESTRICT source_pixel_buffer, int nu_pixels,
uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint32_t *target_pixel32_buffer = (uint32_t *)target_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		uint32_t red = source_pixel_buffer[0];
		uint32_t green = source_pixel_buffer[1];
		uint32_t blue = source_pixel_buffer[2];
		*target_pixel32_buffer = detexPack32RGB8Alpha0xFF(red, green, blue);
		source_pixel_buffer += 3;
		target_pixel32_buffer++;
	}
}

static void ConvertPixel32RGBX8ToPixel24RGB8(uint8_t * DETEX_RESTRICT source_pixel_buffer, int nu_pixels,
uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint32_t *source_pixel32_buffer = (uint32_t *)source_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		uint32_t pixel = *source_pixel32_buffer;
		target_pixel_buffer[0] = detexPixel32GetR8(pixel);
		target_pixel_buffer[1] = detexPixel32GetG8(pixel);
		target_pixel_buffer[2] = detexPixel32GetB8(pixel);
		source_pixel32_buffer++;
		target_pixel_buffer += 3;
	}
}

// Conversion between packed half-float RGB16 and half-float RGBX16.

static void ConvertPixel48RGB16ToPixel64RGBX16(uint8_t * DETEX_RESTRICT source_pixel_buffer, int nu_pixels,
uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint16_t hf[1];
	float f[1];
	f[0] = 1.0f;
	detexConvertFloatToHalfFloat(&f[0], 1, &hf[0]);
	uint16_t *source_pixel16_buffer = (uint16_t *)source_pixel_buffer;
	uint64_t *target_pixel64_buffer = (uint64_t *)target_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		uint32_t red = source_pixel16_buffer[0];
		uint32_t green = source_pixel16_buffer[1];
		uint32_t blue = source_pixel16_buffer[2];
		*target_pixel64_buffer = detexPack64RGBA16(red, green, blue, hf[0]);
		source_pixel16_buffer += 3;
		target_pixel64_buffer++;
	}
}

static void ConvertPixel64RGBX16ToPixel48RGB16(uint8_t * DETEX_RESTRICT source_pixel_buffer, int nu_pixels,
uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	uint64_t *source_pixel64_buffer = (uint64_t *)source_pixel_buffer;
	uint16_t *target_pixel16_buffer = (uint16_t *)target_pixel16_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		uint64_t pixel = *source_pixel64_buffer;
		target_pixel16_buffer[0] = detexPixel64GetR16(pixel);
		target_pixel16_buffer[1] = detexPixel64GetG16(pixel);
		target_pixel16_buffer[2] = detexPixel64GetB16(pixel);
		source_pixel64_buffer++;
		target_pixel16_buffer += 3;
	}
}

// Conversion between packed float RGB32 and float RGBX32.

static void ConvertPixel96RGB32ToPixel128RGBX32(uint8_t * DETEX_RESTRICT source_pixel_buffer, int nu_pixels,
uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	float *source_pixelf_buffer = (float *)source_pixel_buffer;
	float *target_pixelf_buffer = (float *)target_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		float red = source_pixelf_buffer[0];
		float green = source_pixelf_buffer[1];
		float blue = source_pixelf_buffer[2];
		target_pixelf_buffer[0] = red;
		target_pixelf_buffer[1] = green;
		target_pixelf_buffer[2] = blue;
		target_pixelf_buffer[3] = 1.0f;
		source_pixelf_buffer += 3;
		target_pixelf_buffer += 4;
	}
}

static void ConvertPixel128RGBX32ToPixel96RGB32(uint8_t * DETEX_RESTRICT source_pixel_buffer, int nu_pixels,
uint8_t * DETEX_RESTRICT target_pixel_buffer) {
	float *source_pixelf_buffer = (float *)source_pixel_buffer;
	float *target_pixelf_buffer = (float *)target_pixel_buffer;
	for (int i = 0; i < nu_pixels; i++) {
		float red = source_pixelf_buffer[0];
		float green = source_pixelf_buffer[1];
		float blue = source_pixelf_buffer[2];
		target_pixelf_buffer[0] = red;
		target_pixelf_buffer[1] = green;
		target_pixelf_buffer[2] = blue;
		source_pixelf_buffer += 4;
		target_pixelf_buffer += 3;
	}
}


typedef void (*detexConversionFunc)(uint8_t *source_pixel_buffer, int nu_pixels,
	uint8_t *target_pixel_buffer);

typedef struct {
	uint32_t source_format;
	uint32_t target_format;
	detexConversionFunc conversion_func;
} detexConversionType;

// Conversion table. Conversions for which the source pixel size is equal to the
// target pixel size are performed in-place on the source pixel buffer.
detexConversionType detex_conversion_table[] = {
	// No-ops (in-place).
	// 0
	{ DETEX_PIXEL_FORMAT_RGBX8, DETEX_PIXEL_FORMAT_RGBA8, ConvertNoop },
	{ DETEX_PIXEL_FORMAT_RGBA8, DETEX_PIXEL_FORMAT_RGBX8, ConvertNoop },
	{ DETEX_PIXEL_FORMAT_BGRX8, DETEX_PIXEL_FORMAT_BGRA8, ConvertNoop },
	{ DETEX_PIXEL_FORMAT_BGRA8, DETEX_PIXEL_FORMAT_BGRX8, ConvertNoop },
	// Swapping red and blue (in-place).
	{ DETEX_PIXEL_FORMAT_RGBX8, DETEX_PIXEL_FORMAT_BGRX8, ConvertPixel32RGBA8ToPixel32BGRA8 },
	{ DETEX_PIXEL_FORMAT_BGRX8, DETEX_PIXEL_FORMAT_RGBX8, ConvertPixel32RGBA8ToPixel32BGRA8 },
	{ DETEX_PIXEL_FORMAT_RGBA8, DETEX_PIXEL_FORMAT_BGRA8, ConvertPixel32RGBA8ToPixel32BGRA8 },
	{ DETEX_PIXEL_FORMAT_BGRA8, DETEX_PIXEL_FORMAT_RGBA8, ConvertPixel32RGBA8ToPixel32BGRA8 },
	{ DETEX_PIXEL_FORMAT_FLOAT_RGBX16, DETEX_PIXEL_FORMAT_FLOAT_BGRX16, ConvertPixel64RGBX16ToPixel64BGRX16 },
	{ DETEX_PIXEL_FORMAT_FLOAT_BGRX16, DETEX_PIXEL_FORMAT_FLOAT_RGBX16, ConvertPixel64RGBX16ToPixel64BGRX16 },
	// Swapping red and blue (not in-place)
	{ DETEX_PIXEL_FORMAT_RGB8, DETEX_PIXEL_FORMAT_BGRX8, ConvertPixel24RGB8ToPixel32BGRX8 },
	// Signed integer conversions (in-place).
	// 11
	{ DETEX_PIXEL_FORMAT_R8, DETEX_PIXEL_FORMAT_SIGNED_R8, ConvertPixel8R8ToPixel8SignedR8 },
	{ DETEX_PIXEL_FORMAT_RG8, DETEX_PIXEL_FORMAT_SIGNED_RG8, ConvertPixel16RG8ToPixel16SignedRG8 },
	{ DETEX_PIXEL_FORMAT_SIGNED_R8, DETEX_PIXEL_FORMAT_R8, ConvertPixel8SignedR8ToPixel8R8 },
	{ DETEX_PIXEL_FORMAT_SIGNED_RG8, DETEX_PIXEL_FORMAT_RG8, ConvertPixel16SignedRG8ToPixel16RG8 },
	{ DETEX_PIXEL_FORMAT_R16, DETEX_PIXEL_FORMAT_SIGNED_R16, ConvertPixel16R16ToPixel16SignedR16 },
	{ DETEX_PIXEL_FORMAT_RG16, DETEX_PIXEL_FORMAT_SIGNED_RG16, ConvertPixel32RG16ToPixel32SignedRG16 },
	{ DETEX_PIXEL_FORMAT_SIGNED_R16, DETEX_PIXEL_FORMAT_R16, ConvertPixel16SignedR16ToPixel16R16 },
	{ DETEX_PIXEL_FORMAT_SIGNED_RG16, DETEX_PIXEL_FORMAT_RG16, ConvertPixel32SignedRG16ToPixel32RG16 },
	// Reducing the number of components.
	{ DETEX_PIXEL_FORMAT_RGBA8, DETEX_PIXEL_FORMAT_R8, ConvertPixel32RGBA8ToPixel8R8 },
	{ DETEX_PIXEL_FORMAT_RGBA8, DETEX_PIXEL_FORMAT_RG8, ConvertPixel32RGBA8ToPixel16RG8 },
	{ DETEX_PIXEL_FORMAT_RGB8, DETEX_PIXEL_FORMAT_R8, ConvertPixel24RGB8ToPixel8R8 },
	{ DETEX_PIXEL_FORMAT_RGB8, DETEX_PIXEL_FORMAT_RG8, ConvertPixel24RGB8ToPixel16RG8 },
	// Increasing the number of components.
	// 23
	{ DETEX_PIXEL_FORMAT_R8, DETEX_PIXEL_FORMAT_RGBX8, ConvertPixel8R8ToPixel32RGBX8 },
	{ DETEX_PIXEL_FORMAT_RG8, DETEX_PIXEL_FORMAT_RGBX8, ConvertPixel16RG8ToPixel32RGBX8 },
	// Conversion to component of different size.
	{ DETEX_PIXEL_FORMAT_R16, DETEX_PIXEL_FORMAT_R8, ConvertPixel16R16ToPixel8R8 },
	{ DETEX_PIXEL_FORMAT_RG16, DETEX_PIXEL_FORMAT_RG8, ConvertPixel32RG16ToPixel16RG8 },
	{ DETEX_PIXEL_FORMAT_RGB16, DETEX_PIXEL_FORMAT_RGB8, ConvertPixel48RGB16ToPixel24RGB8 },
	{ DETEX_PIXEL_FORMAT_RGBX16, DETEX_PIXEL_FORMAT_RGBX8, ConvertPixel64RGBX16ToPixel32RGBX8 },
	{ DETEX_PIXEL_FORMAT_RGBA16, DETEX_PIXEL_FORMAT_RGBA8, ConvertPixel64RGBA16ToPixel32RGBA8 },
	{ DETEX_PIXEL_FORMAT_R8, DETEX_PIXEL_FORMAT_R16, ConvertPixel8R8ToPixel16R16 },
	{ DETEX_PIXEL_FORMAT_RG8, DETEX_PIXEL_FORMAT_RG16, ConvertPixel16RG8ToPixel32RG16 },
	{ DETEX_PIXEL_FORMAT_RGB8, DETEX_PIXEL_FORMAT_RGB16, ConvertPixel24RGB8ToPixel48RGB16 },
	{ DETEX_PIXEL_FORMAT_RGBX8, DETEX_PIXEL_FORMAT_RGBX16, ConvertPixel32RGBX8ToPixel64RGBX16 },
	{ DETEX_PIXEL_FORMAT_RGBA8, DETEX_PIXEL_FORMAT_RGBA16, ConvertPixel32RGBA8ToPixel64RGBA16 },
	// Integer to half-float conversion (in-place).
	// 35
	{ DETEX_PIXEL_FORMAT_R16, DETEX_PIXEL_FORMAT_FLOAT_R16, ConvertPixel16R16ToPixel16FloatR16 },
	{ DETEX_PIXEL_FORMAT_RG16, DETEX_PIXEL_FORMAT_FLOAT_RG16, ConvertPixel32RG16ToPixel32FloatRG16 },
	{ DETEX_PIXEL_FORMAT_RGB16, DETEX_PIXEL_FORMAT_FLOAT_RGB16, ConvertPixel48RGB16ToPixel48FloatRGB16 },
	{ DETEX_PIXEL_FORMAT_RGBX16, DETEX_PIXEL_FORMAT_FLOAT_RGBX16, ConvertPixel64RGBX16ToPixel64FloatRGBX16 },
	// Half-float to integer conversion (in-place). Note: Integer format has higher precision.
	{ DETEX_PIXEL_FORMAT_FLOAT_R16, DETEX_PIXEL_FORMAT_R16, ConvertPixel16FloatR16ToPixel16R16 },
	{ DETEX_PIXEL_FORMAT_FLOAT_RG16, DETEX_PIXEL_FORMAT_RG16, ConvertPixel32FloatRG16ToPixel32RG16 },
	{ DETEX_PIXEL_FORMAT_FLOAT_RGB16, DETEX_PIXEL_FORMAT_RGB16, ConvertPixel48FloatRGB16ToPixel48RGB16 },
	{ DETEX_PIXEL_FORMAT_FLOAT_RGBX16, DETEX_PIXEL_FORMAT_RGBX16, ConvertPixel64FloatRGBX16ToPixel64RGBX16 },
	{ DETEX_PIXEL_FORMAT_FLOAT_RGBA16, DETEX_PIXEL_FORMAT_RGBA16, ConvertPixel64FloatRGBA16ToPixel64RGBA16 },
	// HDR half-float to integer conversion (in-place).
	{ DETEX_PIXEL_FORMAT_FLOAT_R16_HDR, DETEX_PIXEL_FORMAT_R16, ConvertPixel16FloatR16HDRToPixel16R16 },
	{ DETEX_PIXEL_FORMAT_FLOAT_RG16_HDR, DETEX_PIXEL_FORMAT_RG16, ConvertPixel32FloatRG16HDRToPixel32RG16 },
	{ DETEX_PIXEL_FORMAT_FLOAT_RGBX16_HDR, DETEX_PIXEL_FORMAT_RGBX16, ConvertPixel64FloatRGBX16HDRToPixel64RGBX16 },
	// Float to half-float conversion.
	// 47
	{ DETEX_PIXEL_FORMAT_FLOAT_R32, DETEX_PIXEL_FORMAT_FLOAT_R16, ConvertPixel32FloatR32ToPixel16FloatR16 },
	{ DETEX_PIXEL_FORMAT_FLOAT_RG32, DETEX_PIXEL_FORMAT_FLOAT_RG16, ConvertPixel64FloatRG32ToPixel32FloatRG16 },
	{ DETEX_PIXEL_FORMAT_FLOAT_RGB32, DETEX_PIXEL_FORMAT_FLOAT_RGB16, ConvertPixel96FloatRGB32ToPixel48FloatRGB16 },
	{ DETEX_PIXEL_FORMAT_FLOAT_RGBX32, DETEX_PIXEL_FORMAT_FLOAT_RGBX16, ConvertPixel128FloatRGBX32ToPixel64FloatRGBX16 },
	// Float to 16-bit integer conversion.
	{ DETEX_PIXEL_FORMAT_FLOAT_R32, DETEX_PIXEL_FORMAT_R16, ConvertPixel32FloatR32ToPixel16R16 },
	{ DETEX_PIXEL_FORMAT_FLOAT_RG32, DETEX_PIXEL_FORMAT_RG16, ConvertPixel64FloatRG32ToPixel32RG16 },
	{ DETEX_PIXEL_FORMAT_FLOAT_RGB32, DETEX_PIXEL_FORMAT_RGB16, ConvertPixel96FloatRGB32ToPixel48RGB16 },
	{ DETEX_PIXEL_FORMAT_FLOAT_RGBX32, DETEX_PIXEL_FORMAT_RGBX16, ConvertPixel128FloatRGBX32ToPixel64RGBX16 },
	// Half-float to float conversion.
	// 55
	{ DETEX_PIXEL_FORMAT_FLOAT_R16, DETEX_PIXEL_FORMAT_FLOAT_R32, ConvertPixel16FloatR16ToPixel32FloatR32 },
	{ DETEX_PIXEL_FORMAT_FLOAT_RG16, DETEX_PIXEL_FORMAT_FLOAT_RG32, ConvertPixel32FloatRG16ToPixel64FloatRG32 },
	{ DETEX_PIXEL_FORMAT_FLOAT_RGB16, DETEX_PIXEL_FORMAT_FLOAT_RGB32, ConvertPixel48FloatRGB16ToPixel96FloatRGB32 },
	{ DETEX_PIXEL_FORMAT_FLOAT_RGBX16, DETEX_PIXEL_FORMAT_FLOAT_RGBX32, ConvertPixel64FloatRGBX16ToPixel128FloatRGBX32 },
	// HDR Float to float conversion.
	{ DETEX_PIXEL_FORMAT_FLOAT_R32_HDR, DETEX_PIXEL_FORMAT_FLOAT_R32, ConvertPixel32FloatR32HDRToPixel32FloatR32 },
	{ DETEX_PIXEL_FORMAT_FLOAT_RG32_HDR, DETEX_PIXEL_FORMAT_FLOAT_RG32, ConvertPixel64FloatRG32HDRToPixel64FloatRG32 },
	{ DETEX_PIXEL_FORMAT_FLOAT_RGB32_HDR, DETEX_PIXEL_FORMAT_FLOAT_RGB32, ConvertPixel96FloatRGB32HDRToPixel96FloatRGB32 },
	{ DETEX_PIXEL_FORMAT_FLOAT_RGBX32_HDR, DETEX_PIXEL_FORMAT_FLOAT_RGBX32,
		ConvertPixel128FloatRGBX32HDRToPixel128FloatRGBX32 },
	// Conversion between packed RGB8 and RGBX8.
	// 62
	{ DETEX_PIXEL_FORMAT_RGB8, DETEX_PIXEL_FORMAT_RGBX8, ConvertPixel24RGB8ToPixel32RGBX8 },
	{ DETEX_PIXEL_FORMAT_RGBX8, DETEX_PIXEL_FORMAT_RGB8, ConvertPixel32RGBX8ToPixel24RGB8 },
	// Conversion between packed half-float RGB16 and half-float RGBX16.
	{ DETEX_PIXEL_FORMAT_FLOAT_RGB16, DETEX_PIXEL_FORMAT_FLOAT_RGBX16, ConvertPixel48RGB16ToPixel64RGBX16 },
	{ DETEX_PIXEL_FORMAT_FLOAT_RGBX16, DETEX_PIXEL_FORMAT_FLOAT_RGB16, ConvertPixel64RGBX16ToPixel48RGB16 },
	{ DETEX_PIXEL_FORMAT_FLOAT_RGB16_HDR, DETEX_PIXEL_FORMAT_FLOAT_RGBX16_HDR, ConvertPixel48RGB16ToPixel64RGBX16 },
	{ DETEX_PIXEL_FORMAT_FLOAT_RGBX16_HDR, DETEX_PIXEL_FORMAT_FLOAT_RGB16_HDR, ConvertPixel64RGBX16ToPixel48RGB16 },
	// Conversion between packed float RGB32 and float RGBX32.
	{ DETEX_PIXEL_FORMAT_FLOAT_RGB32, DETEX_PIXEL_FORMAT_FLOAT_RGBX32, ConvertPixel96RGB32ToPixel128RGBX32 },
	{ DETEX_PIXEL_FORMAT_FLOAT_RGBX32, DETEX_PIXEL_FORMAT_FLOAT_RGB32, ConvertPixel128RGBX32ToPixel96RGB32 },
	{ DETEX_PIXEL_FORMAT_FLOAT_RGB32_HDR, DETEX_PIXEL_FORMAT_FLOAT_RGBX32_HDR, ConvertPixel96RGB32ToPixel128RGBX32 },
	{ DETEX_PIXEL_FORMAT_FLOAT_RGBX32_HDR, DETEX_PIXEL_FORMAT_FLOAT_RGB32_HDR, ConvertPixel128RGBX32ToPixel96RGB32 },
};

#define NU_CONVERSION_TYPES (sizeof(detex_conversion_table) / sizeof(detex_conversion_table[0]))

// #define TRACE_MATCH_CONVERSION

static __thread uint32_t cached_source_format = -1;
static __thread uint32_t cached_target_format = -1;
static __thread int cached_nu_conversions = - 1;
static __thread uint32_t cached_conversion[4];

static void CacheResult(uint32_t source_format, uint32_t target_format, int n, uint32_t *conversion) {
	cached_source_format = source_format;
	cached_target_format = target_format;
	cached_nu_conversions = n;
//	printf("Caching conversion ( ");
	for (int i = 0; i < n; i++) {
		cached_conversion[i] = conversion[i];
//		printf("%d ", conversion[i]);
	}
//	printf(")\n");
}

// Match conversion. Returns number of conversion steps, -1 if not succesful.
static int detexMatchConversion(uint32_t source_pixel_format, uint32_t target_pixel_format,
uint32_t *conversion) {
	// Immediately return if the formats are identical.
	if (source_pixel_format == target_pixel_format)
		return 0;
#ifdef TRACE_MATCH_CONVERSION
	printf("Matching conversion between %s and %s.\n", detexGetTextureFormatText(source_pixel_format),
		detexGetTextureFormatText(target_pixel_format));
#endif
	// Check whether the conversion has been cached.
	if (source_pixel_format == cached_source_format && target_pixel_format == cached_target_format) {
		for (int i = 0; i < cached_nu_conversions; i++)
			conversion[i] = cached_conversion[i];
		return cached_nu_conversions;
	}
	// First check direct conversions.
	for (int i = 0; i < NU_CONVERSION_TYPES; i++)
		if (detex_conversion_table[i].target_format == target_pixel_format 
		&& detex_conversion_table[i].source_format == source_pixel_format) {
			conversion[0] = i;
			CacheResult(source_pixel_format, target_pixel_format, 1, conversion);
			return 1;
		}
	// Check two-step conversions.
	int min_components = detexGetNumberOfComponents(source_pixel_format);
	int n = detexGetNumberOfComponents(target_pixel_format);
	if (n < min_components)
		min_components = n;
	int min_precision = detexGetComponentPrecision(source_pixel_format);
	n = detexGetComponentPrecision(target_pixel_format);
	if (n < min_precision)
		min_precision = n;
	for (int i = 0; i < NU_CONVERSION_TYPES; i++)
		if (detex_conversion_table[i].target_format == target_pixel_format) {
			// Avoid loss of components.
			if (detexGetNumberOfComponents(detex_conversion_table[i].source_format) <
			min_components)
				continue;
			// Avoid loss of precision.
			if (detexGetComponentPrecision(detex_conversion_table[i].source_format) <
			min_precision)
				continue;
			conversion[1] = i;
			for (int j = 0; j < NU_CONVERSION_TYPES; j++) {
				if (detex_conversion_table[j].target_format ==
				detex_conversion_table[i].source_format &&
				detex_conversion_table[j].source_format == source_pixel_format) {
					conversion[0] = j;
					CacheResult(source_pixel_format, target_pixel_format, 2, conversion);
					return 2;
				}
			}
		}
	// Check three-step conversions.
	for (int i = 0; i < NU_CONVERSION_TYPES; i++)
		// Match the first conversion with the source format.
		if (detex_conversion_table[i].source_format == source_pixel_format) {
			// Avoid loss of components.
			if (detexGetNumberOfComponents(detex_conversion_table[i].target_format) <
			min_components)
				continue;
			// Avoid loss of precision.
			if (detexGetComponentPrecision(detex_conversion_table[i].target_format) <
			min_precision)
				continue;
			conversion[0] = i;
#ifdef TRACE_MATCH_CONVERSION
			printf("Trying conversion ( %d ? ? )\n", conversion[0]);
#endif
			// Match the third conversion with the target format.
			for (int j = 0; j < NU_CONVERSION_TYPES; j++)
				if (detex_conversion_table[j].target_format == target_pixel_format) {
					// Avoid loss of components.
					if (detexGetNumberOfComponents(detex_conversion_table[j].source_format) <
					min_components)
						continue;
					// Avoid loss of precision.
					if (detexGetComponentPrecision(detex_conversion_table[j].source_format) <
					min_precision)
						continue;
					conversion[2] = j;
#ifdef TRACE_MATCH_CONVERSION
					printf("Trying conversion ( %d ? %d )\n", conversion[0], conversion[2]);
#endif
					for (int k = 0; k < NU_CONVERSION_TYPES; k++)
						if (detex_conversion_table[k].target_format ==
						detex_conversion_table[j].source_format &&
						detex_conversion_table[k].source_format ==
						detex_conversion_table[i].target_format) {
							conversion[1] = k;
							CacheResult(source_pixel_format, target_pixel_format,
								3, conversion);
							return 3;
						}
				}
		}
	// Check four-step conversions.
	for (int i = 0; i < NU_CONVERSION_TYPES; i++)
		// Match the first conversion with the source format.
		if (detex_conversion_table[i].source_format == source_pixel_format) {
			// Avoid loss of components.
			if (detexGetNumberOfComponents(detex_conversion_table[i].target_format) <
			min_components)
				continue;
			// Avoid loss of precision.
			if (detexGetComponentPrecision(detex_conversion_table[i].target_format) <
			min_precision)
				continue;
			conversion[0] = i;
#ifdef TRACE_MATCH_CONVERSION
			printf("Trying conversion ( %d ? ? ? )\n", conversion[0]);
#endif
			// Match the fourth conversion with the target format.
			for (int j = 0; j < NU_CONVERSION_TYPES; j++)
				if (detex_conversion_table[j].target_format == target_pixel_format) {
					// Avoid loss of components.
					if (detexGetNumberOfComponents(detex_conversion_table[j].source_format) <
					min_components)
						continue;
					// Avoid loss of precision.
					if (detexGetComponentPrecision(detex_conversion_table[j].source_format) <
					min_precision)
						continue;
					conversion[3] = j;
#ifdef TRACE_MATCH_CONVERSION
					printf("Trying conversion ( %d ? ? %d )\n", conversion[0], conversion[3]);
#endif
					// Match the second conversion
					for (int k = 0; k < NU_CONVERSION_TYPES; k++)
						if (detex_conversion_table[k].source_format ==
						detex_conversion_table[i].target_format) {
							// Avoid loss of components.
							if (detexGetNumberOfComponents(
							detex_conversion_table[k].target_format) < min_components)
								continue;
							// Avoid loss of precision.
							if (detexGetComponentPrecision(
							detex_conversion_table[k].target_format) < min_precision)
								continue;
							conversion[1] = k;
#ifdef TRACE_MATCH_CONVERSION
							printf("Trying conversion ( %d %d ? %d )\n",
								conversion[0], conversion[1], conversion[3]);
#endif
							// Match the third conversion.
							for (int l = 0; l < NU_CONVERSION_TYPES; l++) {
								if (detex_conversion_table[l].target_format ==
								detex_conversion_table[j].source_format &&
								detex_conversion_table[l].source_format ==
								detex_conversion_table[k].target_format) {
									conversion[2] = l;
									CacheResult(source_pixel_format, target_pixel_format,
										4, conversion);
									return 4;
								}
							}
						}
				}
		}
	return - 1;
}

// Temporary pixel buffer management for conversion function.

#define DETEX_MAX_TEMP_PIXEL_BUFFERS 3

typedef struct {
	uint8_t *pixel_buffer[DETEX_MAX_TEMP_PIXEL_BUFFERS];
	uint32_t size[DETEX_MAX_TEMP_PIXEL_BUFFERS];
	int nu_buffers;
} TempPixelBufferInfo;

static void InitTemporaryPixelBuffers(TempPixelBufferInfo *info) {
	info->nu_buffers = 0;
}

static uint8_t *AllocateTemporaryPixelBuffer(TempPixelBufferInfo *info, uint32_t size) {
	if (info->nu_buffers == DETEX_MAX_TEMP_PIXEL_BUFFERS)
		return NULL;
	uint8_t *buffer = (uint8_t *)malloc(size);
	info->pixel_buffer[info->nu_buffers] = buffer;
	info->nu_buffers++;
	return buffer;
}

static void FreeTemporaryPixelBuffers(TempPixelBufferInfo *info) {
	for (int i = 0; i < info->nu_buffers; i++)
		free(info->pixel_buffer[i]);
}

// Convert pixels between different formats. Return true if successful.
// If target_pixel_format is NULL, the conversion will be attempted in-place, without
// allocating any temporary buffer.

bool detexConvertPixels(uint8_t * DETEX_RESTRICT source_pixel_buffer, uint32_t nu_pixels,
uint32_t source_pixel_format, uint8_t * DETEX_RESTRICT target_pixel_buffer,
uint32_t target_pixel_format) {
//	printf("Converting between %s and %s (0x%08X and 0x%08X).\n", detexGetTextureFormatText(source_pixel_format),
//		detexGetTextureFormatText(target_pixel_format), source_pixel_format, target_pixel_format);
	if (source_pixel_format == target_pixel_format) {
		if (target_pixel_buffer != NULL)
			memcpy(target_pixel_buffer, source_pixel_buffer, nu_pixels *
				detexGetPixelSize(source_pixel_format));
		return true;
	}
	uint32_t conversion[4];
	int nu_conversions = detexMatchConversion(source_pixel_format, target_pixel_format, conversion);
	if (nu_conversions < 0) {
		detexSetErrorMessage("detexConvertPixels: Unable to find conversion path");
		return false;
	}
	// Count in place/non-place steps.
	int nu_non_in_place_conversions = 0;
	int last_non_in_place_conversion = - 1;
	int first_non_in_place_conversion = - 1;
	for (int i = 0; i < nu_conversions; i++)
		if (detexGetPixelSize(detex_conversion_table[conversion[i]].source_format)
		!= detexGetPixelSize(detex_conversion_table[conversion[i]].target_format)) {
			nu_non_in_place_conversions++;
			last_non_in_place_conversion = i;
			if (first_non_in_place_conversion < 0)
				first_non_in_place_conversion = i;
		}
	if (target_pixel_buffer == NULL && nu_non_in_place_conversions > 0) {
		detexSetErrorMessage("Unable to find in-place conversion path");
		return false;
	}
	// Perform conversions.
	TempPixelBufferInfo temp_pixel_buffer_info;
	InitTemporaryPixelBuffers(&temp_pixel_buffer_info);
	if (first_non_in_place_conversion > 0) {
		// When doing a non-place conversion and the first conversion step is in-place,
		// allocate a temporary buffer to avoid corrupting the source buffer.
		uint8_t *temp_pixel_buffer = AllocateTemporaryPixelBuffer(&temp_pixel_buffer_info,
			detexGetPixelSize(source_pixel_format) * nu_pixels);
		memcpy(temp_pixel_buffer, source_pixel_buffer,
			detexGetPixelSize(source_pixel_format) * nu_pixels);
		source_pixel_buffer = temp_pixel_buffer;
	}
	if (target_pixel_buffer != NULL && nu_non_in_place_conversions == 0) {
		// When doing a non-in-place conversion with only in-place conversion steps,
		// start by copying the source buffer to the target buffer.
		memcpy(target_pixel_buffer, source_pixel_buffer,
			detexGetPixelSize(source_pixel_format) * nu_pixels);
		source_pixel_buffer = target_pixel_buffer;
	}
	for (int i = 0; i < nu_conversions; i++) {
		if (detexGetPixelSize(detex_conversion_table[conversion[i]].source_format)
		== detexGetPixelSize(detex_conversion_table[conversion[i]].target_format)) {
			// In-place conversion step.
			detex_conversion_table[conversion[i]].conversion_func(
				source_pixel_buffer, nu_pixels, NULL);
		}
		else {
			if (i == last_non_in_place_conversion) {
				detex_conversion_table[conversion[i]].conversion_func(
					source_pixel_buffer, nu_pixels, target_pixel_buffer);
				source_pixel_buffer = target_pixel_buffer;
			}
			else {
				uint8_t *temp_pixel_buffer = AllocateTemporaryPixelBuffer(&temp_pixel_buffer_info,
					nu_pixels * detexGetPixelSize(
						detex_conversion_table[conversion[i]].target_format));
				if (temp_pixel_buffer == NULL) {
					// Error: Too many temporary buffers needed.
					detexSetErrorMessage("detexConvertPixels: Too many temporary buffers needed");
					FreeTemporaryPixelBuffers(&temp_pixel_buffer_info);
					return false;
				}
				detex_conversion_table[conversion[i]].conversion_func(
					source_pixel_buffer, nu_pixels, temp_pixel_buffer);
				source_pixel_buffer = temp_pixel_buffer;
			}
		}
	
	}
	FreeTemporaryPixelBuffers(&temp_pixel_buffer_info);
	return true;
}

bool detexConvertPixelsInPlace(uint8_t * DETEX_RESTRICT source_pixel_buffer, uint32_t nu_pixels,
uint32_t source_pixel_format, uint32_t target_pixel_format) {
	return detexConvertPixels(source_pixel_buffer, nu_pixels, source_pixel_format, NULL, target_pixel_format);
}



================================================
FILE: dds.c
================================================
/*

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.

*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "detex.h"
#include "file-info.h"
#include "misc.h"

// Load texture from DDS file with mip-maps. Returns true if successful.
// nu_levels is a return parameter that returns the number of mipmap levels found.
// textures_out is a return parameter for an array of detexTexture pointers that is allocated,
// free with free(). textures_out[i] are allocated textures corresponding to each level, free
// with free();
bool detexLoadDDSFileWithMipmaps(const char *filename, int max_mipmaps, detexTexture ***textures_out,
int *nu_levels_out) {
	FILE *f = fopen(filename, "rb");
	if (f == NULL) {
		detexSetErrorMessage("detexLoadDDSFileWithMipmaps: Could not open file %s", filename);
		return false;
	}
	// Read signature.
	char id[4];
	size_t s = fread(id, 1, 4, f);
	if (s != 4) {
		detexSetErrorMessage("detexLoadDDSFileWithMipmaps: Error reading file %s", filename);
		return false;
	}
	if (id[0] != 'D' || id[1] != 'D' || id[2] != 'S' || id[3] != ' ') {
		detexSetErrorMessage("detexLoadDDSFileWithMipmaps: Couldn't find DDS signature");
		return false;
	}
	uint8_t header[124];
	s = fread(header, 1, 124, f);
	if (s != 124) {
		detexSetErrorMessage("detexLoadDDSFileWithMipmaps: Error reading file %s", filename);
		return false;
	}
	uint8_t *headerp = &header[0];
	int width = *(uint32_t *)(headerp + 12);
	int height = *(uint32_t *)(headerp + 8);
//	int pitch = *(uint32_t *)(headerp + 16);
	int pixel_format_flags = *(uint32_t *)(headerp + 76);
	int block_width = 4;
	int block_height = 4;
	int bitcount = *(uint32_t *)(headerp + 84);
	uint32_t red_mask = *(uint32_t *)(headerp + 88);
	uint32_t green_mask = *(uint32_t *)(headerp + 92);
	uint32_t blue_mask = *(uint32_t *)(headerp + 96);
	uint32_t alpha_mask = *(uint32_t *)(headerp + 100);
	char four_cc[5];
	strncpy(four_cc, (char *)&header[80], 4);
	four_cc[4] = '\0';
	uint32_t dx10_format = 0;
	if (strncmp(four_cc, "DX10", 4) == 0) {
		uint32_t dx10_header[5];
		s = fread(dx10_header, 1, 20, f);
		if (s != 20) {
			detexSetErrorMessage("detexLoadDDSFileWithMipmaps: Error reading file %s", filename);
			return false;
		}
		dx10_format = dx10_header[0];
		uint32_t resource_dimension = dx10_header[1];
		if (resource_dimension != 3) {
			detexSetErrorMessage("detexLoadDDSFileWithMipmaps: Only 2D textures supported for .dds files");
			return false;
		}
	}
	const detexTextureFileInfo *info = detexLookupDDSFileInfo(four_cc, dx10_format, pixel_format_flags, bitcount,
		red_mask, green_mask, blue_mask, alpha_mask);
	if (info == NULL) {
		detexSetErrorMessage("detexLoadDDSFileWithMipmaps: Unsupported format in .dds file (fourCC = %s, "
			"DX10 format = %d).", four_cc, dx10_format);
		return false;
	}
	// Maybe implement option to treat BC1 as BC1A?
	int bytes_per_block;
	if (detexFormatIsCompressed(info->texture_format))
		bytes_per_block = detexGetCompressedBlockSize(info->texture_format);
	else
		bytes_per_block = detexGetPixelSize(info->texture_format);
	block_width = info->block_width;
	block_height = info->block_height;
	int extended_width = ((width + block_width - 1) / block_width) * block_width;
	int extended_height = ((height + block_height - 1) / block_height) * block_height;
	uint32_t flags = *(uint32_t *)(headerp + 4);
	int nu_file_mipmaps = 1;
	if (flags & 0x20000) {
		nu_file_mipmaps = *(uint32_t *)(headerp + 24);
//		if (nu_file_mipmaps > 1 && max_mipmaps == 1) {
//			detexSetErrorMessage("Disregarding mipmaps beyond the first level.\n");
//		}
	}
	int nu_mipmaps;
	if (nu_file_mipmaps > max_mipmaps)
		nu_mipmaps = max_mipmaps;
	else
		nu_mipmaps = nu_file_mipmaps;
	detexTexture **textures = (detexTexture **)malloc(sizeof(detexTexture *) * nu_mipmaps);
	for (int i = 0; i < nu_mipmaps; i++) {
		int n = (extended_height / block_width) * (extended_width / block_height);
		// Allocate texture.
		textures[i] = (detexTexture *)malloc(sizeof(detexTexture));
		textures[i]->format = info->texture_format;
		textures[i]->data = (uint8_t *)malloc(n * bytes_per_block);
		textures[i]->width = width;
		textures[i]->height = height;
		textures[i]->width_in_blocks = extended_width / block_width;
		textures[i]->height_in_blocks = extended_height / block_height;
		size_t r = fread(textures[i]->data, 1, n * bytes_per_block, f);
		if (r < n * bytes_per_block) {
			detexSetErrorMessage("detexLoadDDSFileWithMipmaps: Error reading file %s", filename);
			return false;
		}
		// Divide by two for the next mipmap level, rounding down.
		width >>= 1;
		height >>= 1;
		extended_width = ((width + block_width - 1) / block_width) * block_width;
		extended_height = ((height + block_height - 1) / block_height) * block_height;
	}
	fclose(f);
	*nu_levels_out = nu_mipmaps;
	*textures_out = textures;
	return true;
}


// Load texture from DDS file (first mip-map only). Returns true if successful.
// The texture is allocated, free with free().
bool detexLoadDDSFile(const char *filename, detexTexture **texture_out) {
	int nu_mipmaps;
	detexTexture **textures;
	bool r = detexLoadDDSFileWithMipmaps(filename, 1, &textures, &nu_mipmaps);
	if (!r)
		return false;
	*texture_out = textures[0];
	free(textures);
	return true;
}

static const char dds_id[4] = {
	'D', 'D', 'S', ' '
};

// Save textures to DDS file (multiple mip-maps levels). Return true if succesful.
bool detexSaveDDSFileWithMipmaps(detexTexture **textures, int nu_levels, const char *filename) {
	FILE *f = fopen(filename, "wb");
	if (f == NULL) {
		detexSetErrorMessage("detexSaveDDSFileWithMipmaps: Could not open file %s for writing", filename);
		return false;
	}
	const detexTextureFileInfo *info = detexLookupTextureFormatFileInfo(textures[0]->format);
	if (info == NULL) {
		detexSetErrorMessage("detexSaveDDSFileWithMipmaps: Could not match texture format with file format");
		return false;
	}
	if (!info->dds_support) {
		detexSetErrorMessage("detexSaveDDSFileWithMipmaps: Could not match texture format with DDS file format");
		return false;
	}
	size_t r = fwrite(dds_id, 1, 4, f);
	if (r != 4) {
		detexSetErrorMessage("detexSaveDDSFileWithMipmaps: Error writing to file %s", filename);
		return false;
	}
	int n;
	int block_size;
	if (detexFormatIsCompressed(textures[0]->format)) {
		n = textures[0]->width_in_blocks * textures[0]->height_in_blocks;
		block_size = detexGetCompressedBlockSize(textures[0]->format);
	}
	else {
		n = textures[0]->width * textures[0]->height;
		block_size = detexGetPixelSize(textures[0]->format);
	}
	uint8_t header[124];
	uint8_t dx10_header[20];
	memset(header, 0, 124);
	memset(dx10_header, 0, 20);
	uint32_t *header32 = (uint32_t *)header;
	*(uint32_t *)header32 = 124;
	uint32_t flags = 0x1007;
	if (nu_levels > 1)
		flags |= 0x20000;
	if (!detexFormatIsCompressed(textures[0]->format))
		flags |= 0x8;	// Pitch specified.
	else
		flags |= 0x80000;	// Linear size specified.
	*(uint32_t *)(header + 4) = flags;	// Flags
	*(uint32_t *)(header + 8) = textures[0]->height;
	*(uint32_t *)(header + 12) = textures[0]->width;
	*(uint32_t *)(header + 16) = n * block_size; // Linear size for compressed textures.
	*(uint32_t *)(header + 24) = nu_levels;	// Mipmap count.
	*(uint32_t *)(header + 72) = 32;
	*(uint32_t *)(header + 76) = 0x4;	// Pixel format flags (fourCC present).
	bool write_dx10_header = false;
	if (strncmp(info->dx_four_cc, "DX10", 4) == 0) {
		write_dx10_header = true;
		uint32_t *dx10_header32 = (uint32_t *)dx10_header;
		*(uint32_t *)dx10_header32 = info->dx10_format;
		*(uint32_t *)(dx10_header + 4) = 3;	// Resource dimensions = 2D.
		*(uint32_t *)(dx10_header + 12) = 1;	// Array size.
	}
	if (!detexFormatIsCompressed(info->texture_format)) {
		uint64_t red_mask, green_mask, blue_mask, alpha_mask;
		detexGetComponentMasks(info->texture_format, &red_mask, &green_mask, &blue_mask, &alpha_mask);
		int component_size = detexGetComponentSize(info->texture_format) * 8;
		int nu_components = detexGetNumberOfComponents(info->texture_format);
		// Note: Some readers don't like the absence of other fields (such as the component masks and pixel
		// formats) for uncompressed data with a DX10 header.
		*(uint32_t *)(header + 84) = nu_components * component_size;	// bit count
		*(uint32_t *)(header + 88) = red_mask;
		*(uint32_t *)(header + 92) = green_mask;
		*(uint32_t *)(header + 96) = blue_mask;
		*(uint32_t *)(header + 100) = alpha_mask;
		// Format does not have a FOURCC code (legacy uncompressed format).
		uint32_t pixel_format_flags = 0x40;	// Uncompressed RGB data present.
		if (strlen(info->dx_four_cc) > 0)
			pixel_format_flags |= 0x04;	// FourCC present.
		if (detexFormatHasAlpha(info->texture_format))
			pixel_format_flags |= 0x01;
		*(uint32_t *)(header + 76) = pixel_format_flags;
	}
	if (strlen(info->dx_four_cc) > 0) {
		// In case of DXTn or DX10 fourCC, set it.
		strncpy((char *)(header + 80), info->dx_four_cc, 4);
		// Pixel format field was already set to 0x4 (FourCC present) by default.
	}
	uint32_t caps = 0x1000;
	if (nu_levels > 1)
		caps |= 0x400008;
	*(uint32_t *)(header + 104) = caps;	// Caps.
	int pitch = textures[0]->width * detexGetPixelSize(textures[0]->format);
	if (!detexFormatIsCompressed(textures[0]->format))
		*(uint32_t *)(header + 16) = pitch;
	r = fwrite(header, 1, 124, f);
	if (r != 124) {
		detexSetErrorMessage("detexSaveDDSFileWithMipmaps: Error writing to file %s", filename);
		return false;
	}
	if (write_dx10_header) {
		r = fwrite(dx10_header, 1, 20, f);
		if (r != 20) {
			detexSetErrorMessage("detexSaveDDSFileWithMipmaps: Error writing to file %s", filename);
			return false;
		}
	}
	// Write data.
	for (int i = 0; i < nu_levels; i++) {
		uint32_t pixel_size = detexGetPixelSize(textures[i]->format);
		// Block size is block size for compressed textures and the pixel size for
		// uncompressed textures.
		int n;
		int block_size;
		if (detexFormatIsCompressed(textures[i]->format)) {
			n = textures[i]->width_in_blocks * textures[i]->height_in_blocks;
			block_size = detexGetCompressedBlockSize(textures[i]->format);
		}
		else {
			n = textures[i]->width * textures[i]->height;
			block_size = pixel_size;
		}
		// Write level data.
		r = fwrite(textures[i]->data, 1, n * block_size, f);
		if (r != n * block_size) {
			detexSetErrorMessage("detexSaveDDSFileWithMipmaps: Error writing to file %s", filename);
			return false;
		}
	}
	fclose(f);
	return true;
}

// Save texture to DDS file (single mip-map level). Returns true if succesful.
bool detexSaveDDSFile(detexTexture *texture, const char *filename) {
	detexTexture *textures[1];
	textures[0] = texture;
	return detexSaveDDSFileWithMipmaps(textures, 1, filename);
}



================================================
FILE: decompress-bc.c
================================================
/*

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.

*/

#include "detex.h"

/* Decompress a 64-bit 4x4 pixel texture block compressed using the BC1 */
/* format. */
bool detexDecompressBlockBC1(const uint8_t * DETEX_RESTRICT bitstring, uint32_t mode_mask,
uint32_t flags, uint8_t * DETEX_RESTRICT pixel_buffer) {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ || !defined(__BYTE_ORDER__)
	uint32_t colors = *(uint32_t *)&bitstring[0];
#else
	uint32_t colors = ((uint32_t)bitstring[0] << 24) |
		((uint32_t)bitstring[1] << 16) |
		((uint32_t)bitstring[2] << 8) | bitstring[3];
#endif
	// Decode the two 5-6-5 RGB colors.
	int color_r[4], color_g[4], color_b[4];
	color_b[0] = (colors & 0x0000001F) << 3;
	color_g[0] = (colors & 0x000007E0) >> (5 - 2);
	color_r[0] = (colors & 0x0000F800) >> (11 - 3);
	color_b[1] = (colors & 0x001F0000) >> (16 - 3);
	color_g[1] = (colors & 0x07E00000) >> (21 - 2);
	color_r[1] = (colors & 0xF8000000) >> (27 - 3);
	if ((colors & 0xFFFF) > ((colors & 0xFFFF0000) >> 16)) {
		color_r[2] = detexDivide0To767By3(2 * color_r[0] + color_r[1]);
		color_g[2] = detexDivide0To767By3(2 * color_g[0] + color_g[1]);
		color_b[2] = detexDivide0To767By3(2 * color_b[0] + color_b[1]);
		color_r[3] = detexDivide0To767By3(color_r[0] + 2 * color_r[1]);
		color_g[3] = detexDivide0To767By3(color_g[0] + 2 * color_g[1]);
		color_b[3] = detexDivide0To767By3(color_b[0] + 2 * color_b[1]);
	}
	else {
		color_r[2] = (color_r[0] + color_r[1]) / 2;
		color_g[2] = (color_g[0] + color_g[1]) / 2;
		color_b[2] = (color_b[0] + color_b[1]) / 2;
		color_r[3] = color_g[3] = color_b[3] = 0;
	}
	uint32_t pixels = *(uint32_t *)&bitstring[4];
	for (int i = 0; i < 16; i++) {
		int pixel = (pixels >> (i * 2)) & 0x3;
		*(uint32_t *)(pixel_buffer + i * 4) = detexPack32RGB8Alpha0xFF(
			color_r[pixel], color_g[pixel], color_b[pixel]);
	}
	return true;
}

uint32_t detexGetModeBC1(const uint8_t *bitstring) {
	uint32_t colors = *(uint32_t *)bitstring;
	if ((colors & 0xFFFF) > ((colors & 0xFFFF0000) >> 16))
		return 0;
	else
		return 1;
}

void detexSetModeBC1(uint8_t *bitstring, uint32_t mode, uint32_t flags,
uint32_t *colors) {
	uint32_t colorbits = *(uint32_t *)bitstring;
	uint32_t current_mode;
	if ((colorbits & 0xFFFF) > ((colorbits & 0xFFFF0000) >> 16))
		current_mode = 0;
	else
		current_mode = 1;
	if (current_mode != mode) {
		colorbits = ((colorbits & 0xFFFF) << 16) | (colorbits >> 16);
		*(uint32_t *)bitstring = colorbits;
	}
}

/* Decompress a 64-bit 4x4 pixel texture block compressed using the BC1A */
/* format. */
bool detexDecompressBlockBC1A(const uint8_t * DETEX_RESTRICT bitstring, uint32_t mode_mask,
uint32_t flags, uint8_t * DETEX_RESTRICT pixel_buffer) {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ || !defined(__BYTE_ORDER__)
	uint32_t colors = *(uint32_t *)&bitstring[0];
#else
	uint32_t colors = ((uint32_t)bitstring[0] << 24) |
		((uint32_t)bitstring[1] << 16) |
		((uint32_t)bitstring[2] << 8) | bitstring[3];
#endif
	bool opaque = ((colors & 0xFFFF) > ((colors & 0xFFFF0000) >> 16));
	if (opaque && (flags & DETEX_DECOMPRESS_FLAG_NON_OPAQUE_ONLY))
		return false;
	if (!opaque && (flags & DETEX_DECOMPRESS_FLAG_OPAQUE_ONLY))
		return false;
	// Decode the two 5-6-5 RGB colors.
	int color_r[4], color_g[4], color_b[4], color_a[4];
	color_b[0] = (colors & 0x0000001F) << 3;
	color_g[0] = (colors & 0x000007E0) >> (5 - 2);
	color_r[0] = (colors & 0x0000F800) >> (11 - 3);
	color_b[1] = (colors & 0x001F0000) >> (16 - 3);
	color_g[1] = (colors & 0x07E00000) >> (21 - 2);
	color_r[1] = (colors & 0xF8000000) >> (27 - 3);
	color_a[0] = color_a[1] = color_a[2] = color_a[3] = 0xFF;
	if (opaque) {
		color_r[2] = detexDivide0To767By3(2 * color_r[0] + color_r[1]);
		color_g[2] = detexDivide0To767By3(2 * color_g[0] + color_g[1]);
		color_b[2] = detexDivide0To767By3(2 * color_b[0] + color_b[1]);
		color_r[3] = detexDivide0To767By3(color_r[0] + 2 * color_r[1]);
		color_g[3] = detexDivide0To767By3(color_g[0] + 2 * color_g[1]);
		color_b[3] = detexDivide0To767By3(color_b[0] + 2 * color_b[1]);
	}
	else {
		color_r[2] = (color_r[0] + color_r[1]) / 2;
		color_g[2] = (color_g[0] + color_g[1]) / 2;
		color_b[2] = (color_b[0] + color_b[1]) / 2;
		color_r[3] = color_g[3] = color_b[3] = color_a[3] = 0;
	}
	uint32_t pixels = *(uint32_t *)&bitstring[4];
	for (int i = 0; i < 16; i++) {
		int pixel = (pixels >> (i * 2)) & 0x3;
		*(uint32_t *)(pixel_buffer + i * 4) = detexPack32RGBA8(
			color_r[pixel], color_g[pixel], color_b[pixel],
			color_a[pixel]);
	}
	return true;
}

/* Decompress a 64-bit 4x4 pixel texture block compressed using the BC2 */
/* format. */
bool detexDecompressBlockBC2(const uint8_t * DETEX_RESTRICT bitstring, uint32_t mode_mask,
uint32_t flags, uint8_t * DETEX_RESTRICT pixel_buffer) {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ || !defined(__BYTE_ORDER__)
	uint32_t colors = *(uint32_t *)&bitstring[8];
#else
	uint32_t colors = ((uint32_t)bitstring[8] << 24) |
		((uint32_t)bitstring[9] << 16) |
		((uint32_t)bitstring[10] << 8) | bitstring[11];
#endif
	if ((colors & 0xFFFF) <= ((colors & 0xFFFF0000) >> 16) &&
	(flags & DETEX_DECOMPRESS_FLAG_ENCODE))
		// GeForce 6 and 7 series produce wrong result in this case.
		return false;
	int color_r[4], color_g[4], color_b[4];
	color_b[0] = (colors & 0x0000001F) << 3;
	color_g[0] = (colors & 0x000007E0) >> (5 - 2);
	color_r[0] = (colors & 0x0000F800) >> (11 - 3);
	color_b[1] = (colors & 0x001F0000) >> (16 - 3);
	color_g[1] = (colors & 0x07E00000) >> (21 - 2);
	color_r[1] = (colors & 0xF8000000) >> (27 - 3);
	color_r[2] = detexDivide0To767By3(2 * color_r[0] + color_r[1]);
	color_g[2] = detexDivide0To767By3(2 * color_g[0] + color_g[1]);
	color_b[2] = detexDivide0To767By3(2 * color_b[0] + color_b[1]);
	color_r[3] = detexDivide0To767By3(color_r[0] + 2 * color_r[1]);
	color_g[3] = detexDivide0To767By3(color_g[0] + 2 * color_g[1]);
	color_b[3] = detexDivide0To767By3(color_b[0] + 2 * color_b[1]);
	uint32_t pixels = *(uint32_t *)&bitstring[12];
	uint64_t alpha_pixels = *(uint64_t *)&bitstring[0];
	for (int i = 0; i < 16; i++) {
		int pixel = (pixels >> (i * 2)) & 0x3;
		int alpha = ((alpha_pixels >> (i * 4)) & 0xF) * 255 / 15;
		*(uint32_t *)(pixel_buffer + i * 4) = detexPack32RGBA8(
			color_r[pixel], color_g[pixel], color_b[pixel], alpha);
	}
	return true;
}

/* Decompress a 64-bit 4x4 pixel texture block compressed using the BC3 */
/* format. */
bool detexDecompressBlockBC3(const uint8_t * DETEX_RESTRICT bitstring, uint32_t mode_mask,
uint32_t flags, uint8_t * DETEX_RESTRICT pixel_buffer) {
	int alpha0 = bitstring[0];
	int alpha1 = bitstring[1];
	if (alpha0 > alpha1 && (flags & DETEX_DECOMPRESS_FLAG_OPAQUE_ONLY))
		return false;
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ || !defined(__BYTE_ORDER__)
	uint32_t colors = *(uint32_t *)&bitstring[8];
#else
	uint32_t colors = ((uint32_t)bitstring[8] << 24) |
		((uint32_t)bitstring[9] << 16) |
		((uint32_t)bitstring[10] << 8) | bitstring[11];
#endif
	if ((colors & 0xFFFF) <= ((colors & 0xFFFF0000) >> 16) &&
	(flags & DETEX_DECOMPRESS_FLAG_ENCODE))
		// GeForce 6 and 7 series produce wrong result in this case.
		return false;
	int color_r[4], color_g[4], color_b[4];
	// color_x[] has a value between 0 and 248 with the lower three bits zero.
	color_b[0] = (colors & 0x0000001F) << 3;
	color_g[0] = (colors & 0x000007E0) >> (5 - 2);
	color_r[0] = (colors & 0x0000F800) >> (11 - 3);
	color_b[1] = (colors & 0x001F0000) >> (16 - 3);
	color_g[1] = (colors & 0x07E00000) >> (21 - 2);
	color_r[1] = (colors & 0xF8000000) >> (27 - 3);
	color_r[2] = detexDivide0To767By3(2 * color_r[0] + color_r[1]);
	color_g[2] = detexDivide0To767By3(2 * color_g[0] + color_g[1]);
	color_b[2] = detexDivide0To767By3(2 * color_b[0] + color_b[1]);
	color_r[3] = detexDivide0To767By3(color_r[0] + 2 * color_r[1]);
	color_g[3] = detexDivide0To767By3(color_g[0] + 2 * color_g[1]);
	color_b[3] = detexDivide0To767By3(color_b[0] + 2 * color_b[1]);
	uint32_t pixels = *(uint32_t *)&bitstring[12];
	uint64_t alpha_bits = (uint32_t)bitstring[2] |
		((uint32_t)bitstring[3] << 8) |
		((uint64_t)*(uint32_t *)&bitstring[4] << 16);
	for (int i = 0; i < 16; i++) {
		int pixel = (pixels >> (i * 2)) & 0x3;
		int code = (alpha_bits >> (i * 3)) & 0x7;
		int alpha;
		if (alpha0 > alpha1)
			switch (code) {
			case 0 : alpha = alpha0; break;
			case 1 : alpha = alpha1; break;
			case 2 : alpha = detexDivide0To1791By7(6 * alpha0 + 1 * alpha1); break;
			case 3 : alpha = detexDivide0To1791By7(5 * alpha0 + 2 * alpha1); break;
			case 4 : alpha = detexDivide0To1791By7(4 * alpha0 + 3 * alpha1); break;
			case 5 : alpha = detexDivide0To1791By7(3 * alpha0 + 4 * alpha1); break;
			case 6 : alpha = detexDivide0To1791By7(2 * alpha0 + 5 * alpha1); break;
			case 7 : alpha = detexDivide0To1791By7(1 * alpha0 + 6 * alpha1); break;
			}
		else
			switch (code) {
			case 0 : alpha = alpha0; break;
			case 1 : alpha = alpha1; break;
			case 2 : alpha = detexDivide0To1279By5(4 * alpha0 + 1 * alpha1); break;
			case 3 : alpha = detexDivide0To1279By5(3 * alpha0 + 2 * alpha1); break;
			case 4 : alpha = detexDivide0To1279By5(2 * alpha0 + 3 * alpha1); break;
			case 5 : alpha = detexDivide0To1279By5(1 * alpha0 + 4 * alpha1); break;
			case 6 : alpha = 0; break;
			case 7 : alpha = 0xFF; break;
			}
		*(uint32_t *)(pixel_buffer + i * 4) = detexPack32RGBA8(
			color_r[pixel], color_g[pixel], color_b[pixel], alpha);
	}
	return true;
}



================================================
FILE: decompress-bptc-float.c
================================================
/*

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.

*/

#include "detex.h"
#include "bits.h"
#include "bptc-tables.h"

static const int8_t map_mode_table[32] = {
	0, 1, 2, 10, -1, -1, 3, 11, -1, -1, 4, 12, -1, -1, 5, 13,
	-1, -1, 6, -1, -1, -1, 7, -1, -1, -1, 8, -1, -1, -1, 9, -1
};

static int ExtractMode(detexBlock128 *block) {
	uint32_t mode = detexBlock128ExtractBits(block, 2);
	if (mode < 2)
		return mode;
	return map_mode_table[mode | (detexBlock128ExtractBits(block, 3) << 2)];
}

static int GetPartitionIndex(int nu_subsets, int partition_set_id, int i) {
	if (nu_subsets == 1)
		return 0;
	// nu_subset == 2
	return detex_bptc_table_P2[partition_set_id * 16 + i];
}

static const uint8_t bptc_float_EPB[14] = {
	10, 7, 11, 11, 11, 9, 8, 8, 8, 6, 10, 11, 12, 16 };

static DETEX_INLINE_ONLY int GetAnchorIndex(int partition_set_id, int partition, int nu_subsets) {
	if (partition == 0)
		return 0;
	// nu_subsets = 2, partition = 1.
	return detex_bptc_table_anchor_index_second_subset[partition_set_id];
}

static uint32_t Unquantize(uint16_t x, int mode) {
	int32_t unq;
	if (mode == 13)
		unq = x;
	else if (x == 0)
		unq = 0;
	else if (x == (((int32_t)1 << bptc_float_EPB[mode]) - 1))
		unq = 0xFFFF;
	else
		unq = (((int32_t)x << 15) + 0x4000) >> (bptc_float_EPB[mode] - 1);
	return unq;
}

static int32_t UnquantizeSigned(int16_t x, int mode) {
	int s = 0;
	int32_t unq;
	if (bptc_float_EPB[mode] >= 16)
		unq = x;
	else {
		if (x < 0) {
			s = 1;
			x = -x;
		}
		if (x == 0)
			unq = 0;
		else
		if (x >= (((int32_t)1 << (bptc_float_EPB[mode] - 1)) - 1))
			unq = 0x7FFF;
		else
			unq = (((int32_t)x << 15) + 0x4000) >> (bptc_float_EPB[mode] - 1);
		if (s)
			unq = -unq;
	}
	return unq;
}

static int SignExtend(int value, int source_nu_bits, int target_nu_bits) {
	uint32_t sign_bit = value & (1 << (source_nu_bits - 1));
	if (!sign_bit)
		return value;
	uint32_t sign_extend_bits = 0xFFFFFFFF ^ ((1 << source_nu_bits) - 1);
	sign_extend_bits &= ((uint64_t)1 << target_nu_bits) - 1;
	return value | sign_extend_bits;
}

static int32_t InterpolateFloat(int32_t e0, int32_t e1, int16_t index, uint8_t indexprecision) {
	if (indexprecision == 2)
		return (((64 - detex_bptc_table_aWeight2[index]) * e0
			+ detex_bptc_table_aWeight2[index] * e1 + 32) >> 6);
	else
	if (indexprecision == 3)
		return (((64 - detex_bptc_table_aWeight3[index]) * e0
			+ detex_bptc_table_aWeight3[index] * e1 + 32) >> 6);
	else // indexprecision == 4
		return (((64 - detex_bptc_table_aWeight4[index]) * e0
			+ detex_bptc_table_aWeight4[index] * e1 + 32) >> 6);
}

static bool DecompressBlockBPTCFloatShared(const uint8_t * DETEX_RESTRICT bitstring,
uint32_t mode_mask, uint32_t flags, bool signed_flag,
const uint8_t * DETEX_RESTRICT pixel_buffer) {
	detexBlock128 block;
	block.data0 = *(uint64_t *)&bitstring[0];
	block.data1 = *(uint64_t *)&bitstring[8];
	block.index = 0;
	uint32_t mode = ExtractMode(&block);
	if (mode == - 1)
		return false;
	// Allow compression tied to specific modes (according to mode_mask).
	if (!(mode_mask & ((int)1 << mode)))
		return false;
	int32_t r[4], g[4], b[4];
	int partition_set_id = 0;
	int delta_bits_r, delta_bits_g, delta_bits_b;
	uint64_t data0 = block.data0;
	uint64_t data1 = block.data1;
	switch (mode) {
	case 0 :
		// m[1:0],g2[4],b2[4],b3[4],r0[9:0],g0[9:0],b0[9:0],r1[4:0],g3[4],g2[3:0],
		// g1[4:0],b3[0],g3[3:0],b1[4:0],b3[1],b2[3:0],r2[4:0],b3[2],r3[4:0],b3[3]
		g[2] = detexGetBits64(data0, 2, 2) << 4;
		b[2] = detexGetBits64(data0, 3, 3) << 4;
		b[3] = detexGetBits64(data0, 4, 4) << 4;
		r[0] = detexGetBits64(data0, 5, 14);
		g[0] = detexGetBits64(data0, 15, 24);
		b[0] = detexGetBits64(data0, 25, 34);
		r[1] = detexGetBits64(data0, 35, 39);
		g[3] = detexGetBits64(data0, 40, 40) << 4;
		g[2] |= detexGetBits64(data0, 41, 44);
		g[1] = detexGetBits64(data0, 45, 49);
		b[3] |= detexGetBits64(data0, 50, 50);
		g[3] |= detexGetBits64(data0, 51, 54);
		b[1] = detexGetBits64(data0, 55, 59);
		b[3] |= detexGetBits64(data0, 60, 60) << 1;
		b[2] |= detexGetBits64(data0, 61, 63);
		b[2] |= detexGetBits64(data1, 0, 0) << 3;
		r[2] = detexGetBits64(data1, 1, 5);
		b[3] |= detexGetBits64(data1, 6, 6) << 2;
		r[3] = detexGetBits64(data1, 7, 11);
		b[3] |= detexGetBits64(data1, 12, 12) << 3;
		partition_set_id = detexGetBits64(data1, 13, 17);
		block.index = 64 + 18;
		delta_bits_r = delta_bits_g = delta_bits_b = 5;
		break;
	case 1 :
		// m[1:0],g2[5],g3[4],g3[5],r0[6:0],b3[0],b3[1],b2[4],g0[6:0],b2[5],b3[2],
		// g2[4],b0[6:0],b3[3],b3[5],b3[4],r1[5:0],g2[3:0],g1[5:0],g3[3:0],b1[5:0],
		// b2[3:0],r2[5:0],r3[5:0]
		g[2] = detexGetBits64(data0, 2, 2) << 5;
		g[3] = detexGetBits64(data0, 3, 3) << 4;
		g[3] |= detexGetBits64(data0, 4, 4) << 5;
		r[0] = detexGetBits64(data0, 5, 11);
		b[3] = detexGetBits64(data0, 12, 12);
		b[3] |= detexGetBits64(data0, 13, 13) << 1;
		b[2] = detexGetBits64(data0, 14, 14) << 4;
		g[0] = detexGetBits64(data0, 15, 21);
		b[2] |= detexGetBits64(data0, 22, 22) << 5;
		b[3] |= detexGetBits64(data0, 23, 23) << 2;
		g[2] |= detexGetBits64(data0, 24, 24) << 4;
		b[0] = detexGetBits64(data0, 25, 31);
		b[3] |= detexGetBits64(data0, 32, 32) << 3;
		b[3] |= detexGetBits64(data0, 33, 33) << 5;
		b[3] |= detexGetBits64(data0, 34, 34) << 4;
		r[1] = detexGetBits64(data0, 35, 40);
		g[2] |= detexGetBits64(data0, 41, 44);
		g[1] = detexGetBits64(data0, 45, 50);
		g[3] |= detexGetBits64(data0, 51, 54);
		b[1] = detexGetBits64(data0, 55, 60);
		b[2] |= detexGetBits64(data0, 61, 63);
		b[2] |= detexGetBits64(data1, 0, 0) << 3;
		r[2] = detexGetBits64(data1, 1, 6);
		r[3] = detexGetBits64(data1, 7, 12);
		partition_set_id = detexGetBits64(data1, 13, 17);
		block.index = 64 + 18;
		delta_bits_r = delta_bits_g = delta_bits_b = 6;
		break;
	case 2 :
		// m[4:0],r0[9:0],g0[9:0],b0[9:0],r1[4:0],r0[10],g2[3:0],g1[3:0],g0[10],
		// b3[0],g3[3:0],b1[3:0],b0[10],b3[1],b2[3:0],r2[4:0],b3[2],r3[4:0],b3[3]
		r[0] = detexGetBits64(data0, 5, 14);
		g[0] = detexGetBits64(data0, 15, 24);
		b[0] = detexGetBits64(data0, 25, 34);
		r[1] = detexGetBits64(data0, 35, 39);
		r[0] |= detexGetBits64(data0, 40, 40) << 10;
		g[2] = detexGetBits64(data0, 41, 44);
		g[1] = detexGetBits64(data0, 45, 48);
		g[0] |= detexGetBits64(data0, 49, 49) << 10;
		b[3] = detexGetBits64(data0, 50, 50);
		g[3] = detexGetBits64(data0, 51, 54);
		b[1] = detexGetBits64(data0, 55, 58);
		b[0] |= detexGetBits64(data0, 59, 59) << 10;
		b[3] |= detexGetBits64(data0, 60, 60) << 1;
		b[2] = detexGetBits64(data0, 61, 63);
		b[2] |= detexGetBits64(data1, 0, 0) << 3;
		r[2] = detexGetBits64(data1, 1, 5);
		b[3] |= detexGetBits64(data1, 6, 6) << 2;
		r[3] = detexGetBits64(data1, 7, 11);
		b[3] |= detexGetBits64(data1, 12, 12) << 3;
		partition_set_id = detexGetBits64(data1, 13, 17);
		block.index = 64 + 18;
		delta_bits_r = 5;
		delta_bits_g = delta_bits_b = 4;
		break;
	case 3 :	// Original mode 6.
		// m[4:0],r0[9:0],g0[9:0],b0[9:0],r1[3:0],r0[10],g3[4],g2[3:0],g1[4:0],
		// g0[10],g3[3:0],b1[3:0],b0[10],b3[1],b2[3:0],r2[3:0],b3[0],b3[2],r3[3:0],
		// g2[4],b3[3]
		r[0] = detexGetBits64(data0, 5, 14);
		g[0] = detexGetBits64(data0, 15, 24);
		b[0] = detexGetBits64(data0, 25, 34);
		r[1] = detexGetBits64(data0, 35, 38);
		r[0] |= detexGetBits64(data0, 39, 39) << 10;
		g[3] = detexGetBits64(data0, 40, 40) << 4;
		g[2] = detexGetBits64(data0, 41, 44);
		g[1] = detexGetBits64(data0, 45, 49);
		g[0] |= detexGetBits64(data0, 50, 50) << 10;
		g[3] |= detexGetBits64(data0, 51, 54);
		b[1] = detexGetBits64(data0, 55, 58);
		b[0] |= detexGetBits64(data0, 59, 59) << 10;
		b[3] = detexGetBits64(data0, 60, 60) << 1;
		b[2] = detexGetBits64(data0, 61, 63);
		b[2] |= detexGetBits64(data1, 0, 0) << 3;
		r[2] = detexGetBits64(data1, 1, 4);
		b[3] |= detexGetBits64(data1, 5, 5);
		b[3] |= detexGetBits64(data1, 6, 6) << 2;
		r[3] = detexGetBits64(data1, 7, 10);
		g[2] |= detexGetBits64(data1, 11, 11) << 4;
		b[3] |= detexGetBits64(data1, 12, 12) << 3;
		partition_set_id = detexGetBits64(data1, 13, 17);
		block.index = 64 + 18;
		delta_bits_r = delta_bits_b = 4;
		delta_bits_g = 5;
		break;
	case 4 :	// Original mode 10.
		// m[4:0],r0[9:0],g0[9:0],b0[9:0],r1[3:0],r0[10],b2[4],g2[3:0],g1[3:0],
		// g0[10],b3[0],g3[3:0],b1[4:0],b0[10],b2[3:0],r2[3:0],b3[1],b3[2],r3[3:0],
		// b3[4],b3[3]
		r[0] = detexGetBits64(data0, 5, 14);
		g[0] = detexGetBits64(data0, 15, 24);
		b[0] = detexGetBits64(data0, 25, 34);
		r[1] = detexGetBits64(data0, 35, 38);
		r[0] |= detexGetBits64(data0, 39, 39) << 10;
		b[2] = detexGetBits64(data0, 40, 40) << 4;
		g[2] = detexGetBits64(data0, 41, 44);
		g[1] = detexGetBits64(data0, 45, 48);
		g[0] |= detexGetBits64(data0, 49, 49) << 10;
		b[3] = detexGetBits64(data0, 50, 50);
		g[3] = detexGetBits64(data0, 51, 54);
		b[1] = detexGetBits64(data0, 55, 59);
		b[0] |= detexGetBits64(data0, 60, 60) << 10;
		b[2] |= detexGetBits64(data0, 61, 63);
		b[2] |= detexGetBits64(data1, 0, 0) << 3;
		r[2] = detexGetBits64(data1, 1, 4);
		b[3] |= detexGetBits64(data1, 5, 5) << 1;
		b[3] |= detexGetBits64(data1, 6, 6) << 2;
		r[3] = detexGetBits64(data1, 7, 10);
		b[3] |= detexGetBits64(data1, 11, 11) << 4;
		b[3] |= detexGetBits64(data1, 12, 12) << 3;
		partition_set_id = detexGetBits64(data1, 13, 17);
		block.index = 64 + 18;
		delta_bits_r = delta_bits_g = 4;
		delta_bits_b = 5;
		break;
	case 5 :	// Original mode 14
		// m[4:0],r0[8:0],b2[4],g0[8:0],g2[4],b0[8:0],b3[4],r1[4:0],g3[4],g2[3:0],
		// g1[4:0],b3[0],g3[3:0],b1[4:0],b3[1],b2[3:0],r2[4:0],b3[2],r3[4:0],b3[3]
		r[0] = detexGetBits64(data0, 5, 13);
		b[2] = detexGetBits64(data0, 14, 14) << 4;
		g[0] = detexGetBits64(data0, 15, 23);
		g[2] = detexGetBits64(data0, 24, 24) << 4;
		b[0] = detexGetBits64(data0, 25, 33);
		b[3] = detexGetBits64(data0, 34, 34) << 4;
		r[1] = detexGetBits64(data0, 35, 39);
		g[3] = detexGetBits64(data0, 40, 40) << 4;
		g[2] |= detexGetBits64(data0, 41, 44);
		g[1] = detexGetBits64(data0, 45, 49);
		b[3] |= detexGetBits64(data0, 50, 50);
		g[3] |= detexGetBits64(data0, 51, 54);
		b[1] = detexGetBits64(data0, 55, 59);
		b[3] |= detexGetBits64(data0, 60, 60) << 1;
		b[2] |= detexGetBits64(data0, 61, 63);
		b[2] |= detexGetBits64(data1, 0, 0) << 3;
		r[2] = detexGetBits64(data1, 1, 5);
		b[3] |= detexGetBits64(data1, 6, 6) << 2;
		r[3] = detexGetBits64(data1, 7, 11);
		b[3] |= detexGetBits64(data1, 12, 12) << 3;
		partition_set_id = detexGetBits64(data1, 13, 17);
		block.index = 64 + 18;
		delta_bits_r = delta_bits_g = delta_bits_b = 5;
		break;
	case 6 :	// Original mode 18
		// m[4:0],r0[7:0],g3[4],b2[4],g0[7:0],b3[2],g2[4],b0[7:0],b3[3],b3[4],
		// r1[5:0],g2[3:0],g1[4:0],b3[0],g3[3:0],b1[4:0],b3[1],b2[3:0],r2[5:0],r3[5:0]
		r[0] = detexGetBits64(data0, 5, 12);
		g[3] = detexGetBits64(data0, 13, 13) << 4;
		b[2] = detexGetBits64(data0, 14, 14) << 4;
		g[0] = detexGetBits64(data0, 15, 22);
		b[3] = detexGetBits64(data0, 23, 23) << 2;
		g[2] = detexGetBits64(data0, 24, 24) << 4;
		b[0] = detexGetBits64(data0, 25, 32);
		b[3] |= detexGetBits64(data0, 33, 33) << 3;
		b[3] |= detexGetBits64(data0, 34, 34) << 4;
		r[1] = detexGetBits64(data0, 35, 40);
		g[2] |= detexGetBits64(data0, 41, 44);
		g[1] = detexGetBits64(data0, 45, 49);
		b[3] |= detexGetBits64(data0, 50, 50);
		g[3] |= detexGetBits64(data0, 51, 54);
		b[1] = detexGetBits64(data0, 55, 59);
		b[3] |= detexGetBits64(data0, 60, 60) << 1;
		b[2] |= detexGetBits64(data0, 61, 63);
		b[2] |= detexGetBits64(data1, 0, 0) << 3;
		r[2] = detexGetBits64(data1, 1, 6);
		r[3] = detexGetBits64(data1, 7, 12);
		partition_set_id = detexGetBits64(data1, 13, 17);
		block.index = 64 + 18;
		delta_bits_r = 6;
		delta_bits_g = delta_bits_b = 5;
		break;
	case 7 :	// Original mode 22
		// m[4:0],r0[7:0],b3[0],b2[4],g0[7:0],g2[5],g2[4],b0[7:0],g3[5],b3[4],
		// r1[4:0],g3[4],g2[3:0],g1[5:0],g3[3:0],b1[4:0],b3[1],b2[3:0],r2[4:0],
		// b3[2],r3[4:0],b3[3]
		r[0] = detexGetBits64(data0, 5, 12);
		b[3] = detexGetBits64(data0, 13, 13);
		b[2] = detexGetBits64(data0, 14, 14) << 4;
		g[0] = detexGetBits64(data0, 15, 22);
		g[2] = detexGetBits64(data0, 23, 23) << 5;
		g[2] |= detexGetBits64(data0, 24, 24) << 4;
		b[0] = detexGetBits64(data0, 25, 32);
		g[3] = detexGetBits64(data0, 33, 33) << 5;
		b[3] |= detexGetBits64(data0, 34, 34) << 4;
		r[1] = detexGetBits64(data0, 35, 39);
		g[3] |= detexGetBits64(data0, 40, 40) << 4;
		g[2] |= detexGetBits64(data0, 41, 44);
		g[1] = detexGetBits64(data0, 45, 50);
		g[3] |= detexGetBits64(data0, 51, 54);
		b[1] = detexGetBits64(data0, 55, 59);
		b[3] |= detexGetBits64(data0, 60, 60) << 1;
		b[2] |= detexGetBits64(data0, 61, 63);
		b[2] |= detexGetBits64(data1, 0, 0) << 3;
		r[2] = detexGetBits64(data1, 1, 5);
		b[3] |= detexGetBits64(data1, 6, 6) << 2;
		r[3] = detexGetBits64(data1, 7, 11);
		b[3] |= detexGetBits64(data1, 12, 12) << 3;
		partition_set_id = detexGetBits64(data1, 13, 17);
		block.index = 64 + 18;
		delta_bits_r = delta_bits_b = 5;
		delta_bits_g = 6;
		break;
	case 8 :	// Original mode 26
		// m[4:0],r0[7:0],b3[1],b2[4],g0[7:0],b2[5],g2[4],b0[7:0],b3[5],b3[4],
		// r1[4:0],g3[4],g2[3:0],g1[4:0],b3[0],g3[3:0],b1[5:0],b2[3:0],r2[4:0],
		// b3[2],r3[4:0],b3[3]
		r[0] = detexGetBits64(data0, 5, 12);
		b[3] = detexGetBits64(data0, 13, 13) << 1;
		b[2] = detexGetBits64(data0, 14, 14) << 4;
		g[0] = detexGetBits64(data0, 15, 22);
		b[2] |= detexGetBits64(data0, 23, 23) << 5;
		g[2] = detexGetBits64(data0, 24, 24) << 4;
		b[0] = detexGetBits64(data0, 25, 32);
		b[3] |= detexGetBits64(data0, 33, 33) << 5;
		b[3] |= detexGetBits64(data0, 34, 34) << 4;
		r[1] = detexGetBits64(data0, 35, 39);
		g[3] = detexGetBits64(data0, 40, 40) << 4;
		g[2] |= detexGetBits64(data0, 41, 44);
		g[1] = detexGetBits64(data0, 45, 49);
		b[3] |= detexGetBits64(data0, 50, 50);
		g[3] |= detexGetBits64(data0, 51, 54);
		b[1] = detexGetBits64(data0, 55, 60);
		b[2] |= detexGetBits64(data0, 61, 63);
		b[2] |= detexGetBits64(data1, 0, 0) << 3;
		r[2] = detexGetBits64(data1, 1, 5);
		b[3] |= detexGetBits64(data1, 6, 6) << 2;
		r[3] = detexGetBits64(data1, 7, 11);
		b[3] |= detexGetBits64(data1, 12, 12) << 3;
		partition_set_id = detexGetBits64(data1, 13, 17);
		block.index = 64 + 18;
		delta_bits_r = delta_bits_g = 5;
		delta_bits_b = 6;
		break;
	case 9 :	// Original mode 30
		// m[4:0],r0[5:0],g3[4],b3[0],b3[1],b2[4],g0[5:0],g2[5],b2[5],b3[2],
		// g2[4],b0[5:0],g3[5],b3[3],b3[5],b3[4],r1[5:0],g2[3:0],g1[5:0],g3[3:0],
		// b1[5:0],b2[3:0],r2[5:0],r3[5:0]
		r[0] = detexGetBits64(data0, 5, 10);
		g[3] = detexGetBits64(data0, 11, 11) << 4;
		b[3] = detexGetBits64(data0, 12, 13);
		b[2] = detexGetBits64(data0, 14, 14) << 4;
		g[0] = detexGetBits64(data0, 15, 20);
		g[2] = detexGetBits64(data0, 21, 21) << 5;
		b[2] |= detexGetBits64(data0, 22, 22) << 5;
		b[3] |= detexGetBits64(data0, 23, 23) << 2;
		g[2] |= detexGetBits64(data0, 24, 24) << 4;
		b[0] = detexGetBits64(data0, 25, 30);
		g[3] |= detexGetBits64(data0, 31, 31) << 5;
		b[3] |= detexGetBits64(data0, 32, 32) << 3;
		b[3] |= detexGetBits64(data0, 33, 33) << 5;
		b[3] |= detexGetBits64(data0, 34, 34) << 4;
		r[1] = detexGetBits64(data0, 35, 40);
		g[2] |= detexGetBits64(data0, 41, 44);
		g[1] = detexGetBits64(data0, 45, 50);
		g[3] |= detexGetBits64(data0, 51, 54);
		b[1] = detexGetBits64(data0, 55, 60);
		b[2] |= detexGetBits64(data0, 61, 63);
		b[2] |= detexGetBits64(data1, 0, 0) << 3;
		r[2] = detexGetBits64(data1, 1, 6);
		r[3] = detexGetBits64(data1, 7, 12);
		partition_set_id = detexGetBits64(data1, 13, 17);
		block.index = 64 + 18;
//		delta_bits_r = delta_bits_g = delta_bits_b = 6;
		break;
	case 10 :	// Original mode 3
		// m[4:0],r0[9:0],g0[9:0],b0[9:0],r1[9:0],g1[9:0],b1[9:0]
		r[0] = detexGetBits64(data0, 5, 14);
		g[0] = detexGetBits64(data0, 15, 24);
		b[0] = detexGetBits64(data0, 25, 34);
		r[1] = detexGetBits64(data0, 35, 44);
		g[1] = detexGetBits64(data0, 45, 54);
		b[1] = detexGetBits64(data0, 55, 63);
		b[1] |= detexGetBits64(data1, 0, 0) << 9;
		partition_set_id = 0;
		block.index = 65;
//		delta_bits_r = delta_bits_g = delta_bits_b = 10;
		break;
	case 11 :	// Original mode 7
		// m[4:0],r0[9:0],g0[9:0],b0[9:0],r1[8:0],r0[10],g1[8:0],g0[10],b1[8:0],b0[10]
		r[0] = detexGetBits64(data0, 5, 14);
		g[0] = detexGetBits64(data0, 15, 24);
		b[0] = detexGetBits64(data0, 25, 34);
		r[1] = detexGetBits64(data0, 35, 43);
		r[0] |= detexGetBits64(data0, 44, 44) << 10;
		g[1] = detexGetBits64(data0, 45, 53);
		g[0] |= detexGetBits64(data0, 54, 54) << 10;
		b[1] = detexGetBits64(data0, 55, 63);
		b[0] |= detexGetBits64(data1, 0, 0) << 10;
		partition_set_id = 0;
		block.index = 65;
		delta_bits_r = delta_bits_g = delta_bits_b = 9;
		break;
	case 12 :	// Original mode 11
		// m[4:0],r0[9:0],g0[9:0],b0[9:0],r1[7:0],r0[10:11],g1[7:0],g0[10:11],
		// b1[7:0],b0[10:11]
		r[0] = detexGetBits64(data0, 5, 14);
		g[0] = detexGetBits64(data0, 15, 24);
		b[0] = detexGetBits64(data0, 25, 34);
		r[1] = detexGetBits64(data0, 35, 42);
		r[0] |= detexGetBits64Reversed(data0, 44, 43) << 10;	// Reversed.
		g[1] = detexGetBits64(data0, 45, 52);
		g[0] |= detexGetBits64Reversed(data0, 54, 53) << 10;	// Reversed.
		b[1] = detexGetBits64(data0, 55, 62);
		b[0] |= detexGetBits64(data0, 63, 63) << 11;	// MSB
		b[0] |= detexGetBits64(data1, 0, 0) << 10;	// LSB
		partition_set_id = 0;
		block.index = 65;
		delta_bits_r = delta_bits_g = delta_bits_b = 8;
		break;
	case 13 :	// Original mode 15
		// m[4:0],r0[9:0],g0[9:0],b0[9:0],r1[3:0],r0[10:15],g1[3:0],g0[10:15],
		// b1[3:0],b0[10:15]
		r[0] = detexGetBits64(data0, 5, 14);
		g[0] = detexGetBits64(data0, 15, 24);
		b[0] = detexGetBits64(data0, 25, 34);
		r[1] = detexGetBits64(data0, 35, 38);
		r[0] |= detexGetBits64Reversed(data0, 44, 39) << 10;	// Reversed.
		g[1] = detexGetBits64(data0, 45, 48);
		g[0] |= detexGetBits64Reversed(data0, 54, 49) << 10;	// Reversed.
		b[1] = detexGetBits64(data0, 55, 58);
		b[0] |= detexGetBits64Reversed(data0, 63, 59) << 11;	// Reversed.
		b[0] |= detexGetBits64(data1, 0, 0) << 10;
		partition_set_id = 0;
		block.index = 65;
		delta_bits_r = delta_bits_g = delta_bits_b = 4;
		break;
	}
	int nu_subsets;
	if (mode >= 10)
		nu_subsets = 1;
	else
		nu_subsets = 2;
	if (signed_flag) {
		r[0] = SignExtend(r[0], bptc_float_EPB[mode], 32);
		g[0] = SignExtend(g[0], bptc_float_EPB[mode], 32);
		b[0] = SignExtend(b[0], bptc_float_EPB[mode], 32);
	}
	if (mode != 9 && mode != 10) {
		// Transformed endpoints.
		for (int i = 1; i < nu_subsets * 2; i++) {
			r[i] = SignExtend(r[i], delta_bits_r, 32);
			r[i] = (r[0] + r[i]) & (((uint32_t)1 << bptc_float_EPB[mode]) - 1);
			g[i] = SignExtend(g[i], delta_bits_g, 32);
			g[i] = (g[0] + g[i]) & (((uint32_t)1 << bptc_float_EPB[mode]) - 1);
			b[i] = SignExtend(b[i], delta_bits_b, 32);
			b[i] = (b[0] + b[i]) & (((uint32_t)1 << bptc_float_EPB[mode]) - 1);
			if (signed_flag) {
				r[i] = SignExtend(r[i], bptc_float_EPB[mode], 32);
				g[i] = SignExtend(g[i], bptc_float_EPB[mode], 32);
				b[i] = SignExtend(b[i], bptc_float_EPB[mode], 32);
			}
		}
	}
	else	// Mode 9 or 10, no transformed endpoints.
	if (signed_flag)
		for (int i = 1; i < nu_subsets * 2; i++) {
			r[i] = SignExtend(r[i], bptc_float_EPB[mode], 32);
			g[i] = SignExtend(g[i], bptc_float_EPB[mode], 32);
			b[i] = SignExtend(b[i], bptc_float_EPB[mode], 32);
		}

	// Unquantize endpoints.
	if (signed_flag)
		for (int i = 0; i < 2 * nu_subsets; i++) {
			r[i] = UnquantizeSigned(r[i], mode);
			g[i] = UnquantizeSigned(g[i], mode);
			b[i] = UnquantizeSigned(b[i], mode);
		}
	else
		for (int i = 0; i < 2 * nu_subsets; i++) {
			r[i] = Unquantize(r[i], mode);
			g[i] = Unquantize(g[i], mode);
			b[i] = Unquantize(b[i], mode);
		}

	uint8_t subset_index[16];
	for (int i = 0; i < 16; i++) {
		// subset_index[i] is a number from 0 to 1, depending on the number of subsets.
		subset_index[i] = GetPartitionIndex(nu_subsets, partition_set_id, i);
	}
	uint8_t anchor_index[4];	// Only need max. 2 elements
	for (int i = 0; i < nu_subsets; i++)
		anchor_index[i] = GetAnchorIndex(partition_set_id, i, nu_subsets);
	uint8_t color_index[16];
	// Extract index bits.
	int color_index_bit_count = 3;
	if ((bitstring[0] & 3) == 3)	// This defines original modes 3, 7, 11, 15
		color_index_bit_count = 4;
	// Because the index bits are all in the second 64-bit word, there is no need to use
	// block_extract_bits().
	data1 >>= (block.index - 64);
	uint8_t mask1 = (1 << color_index_bit_count) - 1;
	uint8_t mask2 = (1 << (color_index_bit_count - 1)) - 1;
	for (int i = 0; i < 16; i++) {
		if (i == anchor_index[subset_index[i]]) {
			// Highest bit is zero.
//			color_index[i] = block_extract_bits(&block, color_index_bit_count - 1);
			color_index[i] = data1 & mask2;
			data1 >>= color_index_bit_count - 1;
		}
		else {
//			color_index[i] = block_extract_bits(&block, color_index_bit_count);
			color_index[i] = data1 & mask1;	
			data1 >>= color_index_bit_count;
		}
	}

	for (int i = 0; i < 16; i++) {
		int32_t endpoint_start_r, endpoint_start_g, endpoint_start_b;
		int32_t endpoint_end_r, endpoint_end_g, endpoint_end_b;
		endpoint_start_r = r[2 * subset_index[i]];
		endpoint_end_r = r[2 * subset_index[i] + 1];
		endpoint_start_g = g[2 * subset_index[i]];
		endpoint_end_g = g[2 * subset_index[i] + 1];
		endpoint_start_b = b[2 * subset_index[i]];
		endpoint_end_b = b[2 * subset_index[i] + 1];
		uint64_t output;
		if (signed_flag) {
			int32_t r16 = InterpolateFloat(endpoint_start_r, endpoint_end_r, color_index[i],
				color_index_bit_count);
			if (r16 < 0)
				r16 = - (((- r16) * 31) >> 5);
			else
				r16 = (r16 * 31) >> 5;
			int s = 0;
			if (r16 < 0) {
				s = 0x8000;
				r16 = - r16;
			}
			r16 |= s;
			int32_t g16 = InterpolateFloat(endpoint_start_g, endpoint_end_g, color_index[i],
				color_index_bit_count);
			if (g16 < 0)
				g16 = - (((- g16) * 31) >> 5);
			else
				g16 = (g16 * 31) >> 5;
			s = 0;
			if (g16 < 0) {
				s = 0x8000;
				g16 = - g16;
			}
			g16 |= s;
			int32_t b16 = InterpolateFloat(endpoint_start_b, endpoint_end_b, color_index[i],
				color_index_bit_count);
			if (b16 < 0)
				b16 = - (((- b16) * 31) >> 5);
			else
				b16 = (b16 * 31) >> 5;
			s = 0;
			if (b16 < 0) {
				s = 0x8000;
				b16 = - b16;
			}
			b16 |= s;
			output = detexPack64RGB16(r16, g16, b16);
		}
		else {
			output = detexPack64R16(InterpolateFloat(endpoint_start_r, endpoint_end_r, color_index[i],
				color_index_bit_count) * 31 / 64);
			output |= detexPack64G16(InterpolateFloat(endpoint_start_g, endpoint_end_g, color_index[i],
				color_index_bit_count) * 31 / 64);
			output |= detexPack64B16(InterpolateFloat(endpoint_start_b, endpoint_end_b, color_index[i],
				color_index_bit_count) * 31 / 64);
		}
		*(uint64_t *)&pixel_buffer[i * 8] = output;
	}
	return true;
}

/* Decompress a 128-bit 4x4 pixel texture block compressed using the */
/* BPTC_FLOAT (BC6H) format. The output format is */
/* DETEX_PIXEL_FORMAT_FLOAT_RGBX16. */
bool detexDecompressBlockBPTC_FLOAT(const uint8_t * DETEX_RESTRICT bitstring, uint32_t mode_mask,
uint32_t flags, uint8_t * DETEX_RESTRICT pixel_buffer) {
	return DecompressBlockBPTCFloatShared(bitstring, mode_mask, flags, false,
		pixel_buffer);
}

/* Decompress a 128-bit 4x4 pixel texture block compressed using the */
/* BPTC_FLOAT (BC6H_FLOAT) format. The output format is */
/* DETEX_PIXEL_FORMAT_SIGNED_FLOAT_RGBX16. */
bool detexDecompressBlockBPTC_SIGNED_FLOAT(const uint8_t * DETEX_RESTRICT bitstring,
uint32_t mode_mask, uint32_t flags, uint8_t * DETEX_RESTRICT pixel_buffer) {
	return DecompressBlockBPTCFloatShared(bitstring, mode_mask, flags, true,
		pixel_buffer);
}

/* Return the internal mode of the BPTC_FLOAT block. */
uint32_t detexGetModeBPTC_FLOAT(const uint8_t *bitstring) {
	detexBlock128 block;
	block.data0 = *(uint64_t *)&bitstring[0];
	block.data1 = *(uint64_t *)&bitstring[8];
	block.index = 0;
	int mode = ExtractMode(&block);
	return mode;
}

uint32_t detexGetModeBPTC_SIGNED_FLOAT(const uint8_t *bitstring) {
	return detexGetModeBPTC_FLOAT(bitstring);
}

static const uint8_t bptc_float_set_mode_table[14] = {
	0, 1, 2, 6, 10, 14, 18, 22, 26, 30, 3, 7, 11, 15
};

void detexSetModeBPTC_FLOAT(uint8_t *bitstring, uint32_t mode, uint32_t flags,
uint32_t *colors) {
	if (mode <= 1) {
		// Set mode 0 or 1.
		bitstring[0] = (bitstring[0] & 0xFC) | mode;
		return;
	}
	uint8_t byte0 = bitstring[0];
	byte0 &= 0xE0;
	byte0 |= bptc_float_set_mode_table[mode];
	bitstring[0] = byte0;
}



================================================
FILE: decompress-bptc.c
================================================
/*

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.

*/

#include "detex.h"
#include "bits.h"
#include "bptc-tables.h"

// BPTC mode layout:
//
// Number of subsets = { 3, 2, 3, 2, 1, 1, 1, 2 };
// Partition bits = { 4, 6, 6, 6, 0, 0, 0, 6 };
// Rotation bits = { 0, 0, 0, 0, 2, 2, 0, 0 };
// Mode 4 has one index selection bit.
//
//      #subsets color alpha before color   index after color	 index after	  After	     Index
//                                                               alpha		  pbits	     bits (*)
// Mode 0   3	  4	0    1 + 4 = 5	    5 + 6 * 3 * 4 = 77	 77		  + 6 = 83   + 48 - 3 = 128
// Mode 1   2	  6	0    2 + 6 = 8	    8 + 4 * 3 * 6 = 80	 80		  + 2 = 82   + 48 - 2 = 128
// Mode 2   3	  5	0    3 + 6 = 9	    9 + 6 * 3 * 5 = 99	 99		  99	     + 32 - 3 = 128
// Mode 3   2	  7	0    4 + 6 = 10	    10 + 4 * 3 * 7 = 94	 94		  + 4 = 98   + 32 - 2 = 128
// Mode 4   1	  5	6    5 + 2 + 1 = 8  8 + 2 * 3 * 5 = 38	 37 + 2 * 6 = 50  50	     + 80 - 2 = 128
// Mode 5   1	  7	8    6 + 2 = 8	    8 + 2 * 3 * 7 = 50	 50 + 2 * 8 = 66  66	     + 64 - 2 = 128
// Mode	6   1	  7	7    7		    7 + 2 * 3 * 7 = 49	 49 + 2 * 7 = 63  + 2 = 65   + 64 - 1 = 128
// Mode 7   2	  5	5    8 + 6 = 14     14 + 4 * 3 * 5 = 74	 74 + 4 * 5 = 94  + 4 = 98   + 32 - 2 = 128
//
// (*) For formats without alpha, the number of index bits is reduced by #subsets anchor bits.
//     For formats with alpha, the number of index bits is reduced by 2 * #subsets by the anchor bits.


static const uint8_t color_precision_table[8] = { 4, 6, 5, 7, 5, 7, 7, 5 };

// Note: precision includes P-bits!
static const uint8_t color_precision_plus_pbit_table[8] = { 5, 7, 5, 8, 5, 7, 8, 6 };

static DETEX_INLINE_ONLY uint8_t GetColorComponentPrecision(int mode) {
	return color_precision_table[mode];
}

static DETEX_INLINE_ONLY uint8_t GetColorComponentPrecisionPlusPbit(int mode) {
	return color_precision_plus_pbit_table[mode];
}

static const int8_t alpha_precision_table[8] = { 0, 0, 0, 0, 6, 8, 7, 5 };

// Note: precision include P-bits!
static const uint8_t alpha_precision_plus_pbit_table[8] = { 0, 0, 0, 0, 6, 8, 8, 6 };

static DETEX_INLINE_ONLY uint8_t GetAlphaComponentPrecision(int mode) {
	return alpha_precision_table[mode];
}

static DETEX_INLINE_ONLY uint8_t GetAlphaComponentPrecisionPlusPbit(int mode) {
	return alpha_precision_plus_pbit_table[mode];
}

static const int8_t components_in_qword0_table[8] = { 2, -1, 1, 1, 3, 3, 3, 2 };

/* Extract endpoint colors. */
static void ExtractEndpoints(int mode, int nu_subsets, detexBlock128 * DETEX_RESTRICT block,
uint8_t * DETEX_RESTRICT endpoint_array) {
	// Optimized version avoiding the use of block_extract_bits().
	int components_in_qword0 = components_in_qword0_table[mode];
	uint64_t data = block->data0 >> block->index;
	uint8_t precision = GetColorComponentPrecision(mode);
	uint8_t mask = (1 << precision) - 1;
	int total_bits_per_component = nu_subsets * 2 * precision;
	for (int i = 0; i < components_in_qword0; i++)	// For each color component.
		for (int j = 0; j < nu_subsets; j++)	// For each subset.
			for (int k = 0; k < 2; k++) {	// For each endpoint.
				endpoint_array[j * 8 + k * 4 + i] = data & mask;
				data >>= precision;
			}
	block->index += components_in_qword0 * total_bits_per_component;
	if (components_in_qword0 < 3) {
		// Handle the color component that crosses the boundary between data0 and data1
		data = block->data0 >> block->index;
		data |= block->data1 << (64 - block->index);
		int i = components_in_qword0;
		for (int j = 0; j < nu_subsets; j++)	// For each subset.
			for (int k = 0; k < 2; k++) {	// For each endpoint.
				endpoint_array[j * 8 + k * 4 + i] = data & mask;
				data >>= precision;
			}
		block->index += total_bits_per_component;
	}
	if (components_in_qword0 < 2) {
		// Handle the color component that is wholly in data1.
		data = block->data1 >> (block->index - 64);
		int i = 2;
		for (int j = 0; j < nu_subsets; j++)	// For each subset.
			for (int k = 0; k < 2; k++) {	// For each endpoint.
				endpoint_array[j * 8 + k * 4 + i] = data & mask;
				data >>= precision;
			}
		block->index += total_bits_per_component;
	}
	// Alpha component.
	if (GetAlphaComponentPrecision(mode) > 0) {
		// For mode 7, the alpha data is wholly in data1.
		// For modes 4 and 6, the alpha data is wholly in data0.
		// For mode 5, the alpha data is in data0 and data1.
		if (mode == 7)
			data = block->data1 >> (block->index - 64);
		else if (mode == 5)
			data = (block->data0 >> block->index) | ((block->data1 & 0x3) << 14);
		else
			data = block->data0 >> block->index;
		uint8_t alpha_precision = GetAlphaComponentPrecision(mode);
		uint8_t mask = (1 << alpha_precision) - 1;
		for (int j = 0; j < nu_subsets; j++)
			for (int k = 0; k < 2; k++) {	// For each endpoint.
				endpoint_array[j * 8 + k * 4 + 3] = data & mask;
				data >>= alpha_precision;
			}
		block->index += nu_subsets * 2 * alpha_precision;
	}
}

static const uint8_t mode_has_p_bits[8] = { 1, 1, 0, 1, 0, 0, 1, 1 };

static void FullyDecodeEndpoints(uint8_t * DETEX_RESTRICT endpoint_array, int nu_subsets,
int mode, detexBlock128 * DETEX_RESTRICT block) {
	if (mode_has_p_bits[mode]) {
		// Mode 1 (shared P-bits) handled elsewhere.
		// Extract end-point P-bits. Take advantage of the fact that they don't cross the
		// 64-bit word boundary in any mode.
		uint32_t bits;
		if (block->index < 64)
			bits = block->data0 >> block->index;
		else
			bits = block->data1 >> (block->index - 64);
		for (int i = 0; i < nu_subsets * 2; i++) {
			endpoint_array[i * 4 + 0] <<= 1;
 			endpoint_array[i * 4 + 1] <<= 1;
			endpoint_array[i * 4 + 2] <<= 1;
			endpoint_array[i * 4 + 3] <<= 1;
			endpoint_array[i * 4 + 0] |= (bits & 1);
			endpoint_array[i * 4 + 1] |= (bits & 1);
			endpoint_array[i * 4 + 2] |= (bits & 1);
			endpoint_array[i * 4 + 3] |= (bits & 1);
			bits >>= 1;
		}
		block->index += nu_subsets * 2;
	}
	int color_prec = GetColorComponentPrecisionPlusPbit(mode);
	int alpha_prec = GetAlphaComponentPrecisionPlusPbit(mode);
	for (int i = 0; i < nu_subsets * 2; i++) {
		// Color_component_precision & alpha_component_precision includes pbit
		// left shift endpoint components so that their MSB lies in bit 7
		endpoint_array[i * 4 + 0] <<= (8 - color_prec);
		endpoint_array[i * 4 + 1] <<= (8 - color_prec);
		endpoint_array[i * 4 + 2] <<= (8 - color_prec);
		endpoint_array[i * 4 + 3] <<= (8 - alpha_prec);

	        // Replicate each component's MSB into the LSBs revealed by the left-shift operation above.
		endpoint_array[i * 4 + 0] |= (endpoint_array[i * 4 + 0] >> color_prec);
		endpoint_array[i * 4 + 1] |= (endpoint_array[i * 4 + 1] >> color_prec);
		endpoint_array[i * 4 + 2] |= (endpoint_array[i * 4 + 2] >> color_prec);
		endpoint_array[i * 4 + 3] |= (endpoint_array[i * 4 + 3] >> alpha_prec);
	}
	if (mode <= 3) {
		for (int i = 0; i < nu_subsets * 2; i++)
			endpoint_array[i * 4 + 3] = 0xFF;
 	}
}

static uint8_t Interpolate(uint8_t e0, uint8_t e1, uint8_t index, uint8_t indexprecision) {
	if (indexprecision == 2)
		return (uint8_t) (((64 - detex_bptc_table_aWeight2[index]) * (uint16_t)e0
			+ detex_bptc_table_aWeight2[index] * (uint16_t)e1 + 32) >> 6);
	else
	if (indexprecision == 3)
		return (uint8_t) (((64 - detex_bptc_table_aWeight3[index]) * (uint16_t)e0
			+ detex_bptc_table_aWeight3[index] * (uint16_t)e1 + 32) >> 6);
	else // indexprecision == 4
		return (uint8_t) (((64 - detex_bptc_table_aWeight4[index]) * (uint16_t)e0
			+ detex_bptc_table_aWeight4[index] * (uint16_t)e1 + 32) >> 6);
}

static const uint8_t bptc_color_index_bitcount[8] = { 3, 3, 2, 2, 2, 2, 4, 2 };

static DETEX_INLINE_ONLY int GetColorIndexBitcount(int mode, int index_selection_bit) {
	// If the index selection bit is set for mode 4, return 3, otherwise 2.
	return bptc_color_index_bitcount[mode] + index_selection_bit;
}

static uint8_t bptc_alpha_index_bitcount[8] = { 3, 3, 2, 2, 3, 2, 4, 2};

static DETEX_INLINE_ONLY int GetAlphaIndexBitcount(int mode, int index_selection_bit) {
	// If the index selection bit is set for mode 4, return 2, otherwise 3.
	return bptc_alpha_index_bitcount[mode] - index_selection_bit;
}

static const uint8_t bptc_NS[8] = { 3, 2, 3, 2, 1, 1, 1, 2 };

static DETEX_INLINE_ONLY int GetNumberOfSubsets(int mode) {
	return bptc_NS[mode];
}

static const uint8_t PB[8] = { 4, 6, 6, 6, 0, 0, 0, 6 };

static DETEX_INLINE_ONLY int GetNumberOfPartitionBits(int mode) {
	return PB[mode];
}

static const uint8_t RB[8] = { 0, 0, 0, 0, 2, 2, 0, 0 };

static DETEX_INLINE_ONLY int GetNumberOfRotationBits(int mode) {
	return RB[mode];
}

// Functions to extract parameters. */

static int ExtractMode(detexBlock128 *block) {
	for (int i = 0; i < 8; i++)
		if (block->data0 & ((uint64_t)1 << i)) {
			block->index = i + 1;
			return i;
		}
	// Illegal.
	return - 1;
}

static DETEX_INLINE_ONLY int ExtractPartitionSetID(detexBlock128 *block, int mode) {
	return detexBlock128ExtractBits(block, GetNumberOfPartitionBits(mode));
}

static DETEX_INLINE_ONLY int GetPartitionIndex(int nu_subsets, int partition_set_id, int i) {
	if (nu_subsets == 1)
		return 0;
	if (nu_subsets == 2)
		return detex_bptc_table_P2[partition_set_id * 16 + i];
	return detex_bptc_table_P3[partition_set_id * 16 + i];
}

static DETEX_INLINE_ONLY int ExtractRotationBits(detexBlock128 *block, int mode) {
	return detexBlock128ExtractBits(block, GetNumberOfRotationBits(mode));
}

static DETEX_INLINE_ONLY int GetAnchorIndex(int partition_set_id, int partition, int nu_subsets) {
	if (partition == 0)
		return 0;
	if (nu_subsets == 2)
		return detex_bptc_table_anchor_index_second_subset[partition_set_id];
	if (partition == 1)
		return detex_bptc_table_anchor_index_second_subset_of_three[partition_set_id];
	return detex_bptc_table_anchor_index_third_subset[partition_set_id];
}

static const uint8_t IB[8] = { 3, 3, 2, 2, 2, 2, 4, 2 };
static const uint8_t IB2[8] = { 0, 0, 0, 0, 3, 2, 0, 0 };
static const uint8_t mode_has_partition_bits[8] = { 1, 1, 1, 1, 0, 0, 0, 1 };

/* Decompress a 128-bit 4x4 pixel texture block compressed using BPTC mode 1. */

static bool DecompressBlockBPTCMode1(detexBlock128 * DETEX_RESTRICT block,
uint8_t * DETEX_RESTRICT pixel_buffer) {
	uint64_t data0 = block->data0;
	uint64_t data1 = block->data1;
	int partition_set_id = detexGetBits64(data0, 2, 7);
	uint8_t endpoint[2 * 2 * 3];	// 2 subsets.
	endpoint[0] = detexGetBits64(data0, 8, 13);	// red, subset 0, endpoint 0
	endpoint[3] = detexGetBits64(data0, 14, 19);	// red, subset 0, endpoint 1
	endpoint[6] = detexGetBits64(data0, 20, 25);	// red, subset 1, endpoint 0
	endpoint[9] = detexGetBits64(data0, 26, 31);	// red, subset 1, endpoint 1
	endpoint[1] = detexGetBits64(data0, 32, 37);	// green, subset 0, endpoint 0
	endpoint[4] = detexGetBits64(data0, 38, 43);	// green, subset 0, endpoint 1
	endpoint[7] = detexGetBits64(data0, 44, 49);	// green, subset 1, endpoint 0
	endpoint[10] = detexGetBits64(data0, 50, 55);	// green, subset 1, endpoint 1
	endpoint[2] = detexGetBits64(data0, 56, 61);	// blue, subset 0, endpoint 0
	endpoint[5] = detexGetBits64(data0, 62, 63)	// blue, subset 0, endpoint 1
		| (detexGetBits64(data1, 0, 3) << 2);
	endpoint[8] = detexGetBits64(data1, 4, 9);	// blue, subset 1, endpoint 0
	endpoint[11] = detexGetBits64(data1, 10, 15);	// blue, subset 1, endpoint 1
	// Decode endpoints.
	for (int i = 0; i < 2 * 2; i++) {
		//component-wise left-shift
		endpoint[i * 3 + 0] <<= 2;
		endpoint[i * 3 + 1] <<= 2;
		endpoint[i * 3 + 2] <<= 2;
       	}
	// P-bit is shared.
	uint8_t pbit_zero = detexGetBits64(data1, 16, 16) << 1;
	uint8_t pbit_one = detexGetBits64(data1, 17, 17) << 1;
        // RGB only pbits for mode 1, one for each subset.
	for (int j = 0; j < 3; j++) {
		endpoint[0 * 3 + j] |= pbit_zero;
		endpoint[1 * 3 + j] |= pbit_zero;
		endpoint[2 * 3 + j] |= pbit_one;
		endpoint[3 * 3 + j] |= pbit_one;
	}
	for (int i = 0; i < 2 * 2; i++) {
	        // Replicate each component's MSB into the LSB.
		endpoint[i * 3 + 0] |= endpoint[i * 3 + 0] >> 7;
		endpoint[i * 3 + 1] |= endpoint[i * 3 + 1] >> 7;
		endpoint[i * 3 + 2] |= endpoint[i * 3 + 2] >> 7;
	}
 
	uint8_t subset_index[16];
	for (int i = 0; i < 16; i++)
		// subset_index[i] is a number from 0 to 1.
		subset_index[i] = detex_bptc_table_P2[partition_set_id * 16 + i];
	uint8_t anchor_index[2];
	anchor_index[0] = 0;
	anchor_index[1] = detex_bptc_table_anchor_index_second_subset[partition_set_id];
	uint8_t color_index[16];
	// Extract primary index bits.
	data1 >>= 18;
	for (int i = 0; i < 16; i++)
		if (i == anchor_index[subset_index[i]]) {
			// Highest bit is zero.
			color_index[i] = data1 & 3; // Get two bits.
			data1 >>= 2;
		}
		else {
			color_index[i] = data1 & 7;	// Get three bits.
			data1 >>= 3;
		}
	uint32_t *pixel32_buffer = (uint32_t *)pixel_buffer;
	for (int i = 0; i < 16; i++) {
		uint8_t endpoint_start[3];
		uint8_t endpoint_end[3];
		for (int j = 0; j < 3; j++) {
			 endpoint_start[j] = endpoint[2 * subset_index[i] * 3 + j];
			 endpoint_end[j] = endpoint[(2 * subset_index[i] + 1) * 3 + j];
		}
		uint32_t output;
		output = detexPack32R8(Interpolate(endpoint_start[0], endpoint_end[0], color_index[i], 3));
		output |= detexPack32G8(Interpolate(endpoint_start[1], endpoint_end[1], color_index[i], 3));
		output |= detexPack32B8(Interpolate(endpoint_start[2], endpoint_end[2], color_index[i], 3));
		output |= detexPack32A8(0xFF);
		pixel32_buffer[i] = output;
	}
	return true;
}

/* Decompress a 128-bit 4x4 pixel texture block compressed using the BPTC */
/* (BC7) format. */
bool detexDecompressBlockBPTC(const uint8_t * DETEX_RESTRICT bitstring, uint32_t mode_mask,
uint32_t flags, uint8_t * DETEX_RESTRICT pixel_buffer) {
	detexBlock128 block;
	block.data0 = *(uint64_t *)&bitstring[0];
	block.data1 = *(uint64_t *)&bitstring[8];
	block.index = 0;
	int mode = ExtractMode(&block);
	if (mode == - 1)
		return 0;
	// Allow compression tied to specific modes (according to mode_mask).
	if (!(mode_mask & ((int)1 << mode)))
		return 0;
	if (mode >= 4 && (flags & DETEX_DECOMPRESS_FLAG_OPAQUE_ONLY))
		return 0;
	if (mode < 4 && (flags & DETEX_DECOMPRESS_FLAG_NON_OPAQUE_ONLY))
		return 0;
	if (mode == 1)
		return DecompressBlockBPTCMode1(&block, pixel_buffer);

	int nu_subsets = 1;
	int partition_set_id = 0;
	if (mode_has_partition_bits[mode]) {
		nu_subsets = GetNumberOfSubsets(mode);
		partition_set_id = ExtractPartitionSetID(&block, mode);
	}
	int rotation = ExtractRotationBits(&block, mode);
	int index_selection_bit = 0;
	if (mode == 4)
		index_selection_bit = detexBlock128ExtractBits(&block, 1);

	int alpha_index_bitcount = GetAlphaIndexBitcount(mode, index_selection_bit);
	int color_index_bitcount = GetColorIndexBitcount(mode, index_selection_bit);

	uint8_t endpoint_array[3 * 2 * 4];	// Max. 3 subsets.
	ExtractEndpoints(mode, nu_subsets, &block, endpoint_array);
	FullyDecodeEndpoints(endpoint_array, nu_subsets, mode, &block);

	uint8_t subset_index[16];
	for (int i = 0; i < 16; i++)
		// subset_index[i] is a number from 0 to 2, or 0 to 1, or 0 depending on the number of subsets.
		subset_index[i] = GetPartitionIndex(nu_subsets, partition_set_id, i);
	uint8_t anchor_index[4];	// Only need max. 3 elements.
	for (int i = 0; i < nu_subsets; i++)
		anchor_index[i] = GetAnchorIndex(partition_set_id, i, nu_subsets);
	uint8_t color_index[16];
	uint8_t alpha_index[16];
	// Extract primary index bits.
	uint64_t data1;
	if (block.index >= 64) {
		// Because the index bits are all in the second 64-bit word, there is no need to use
		// block_extract_bits().
		// This implies the mode is not 4.
		data1 = block.data1 >> (block.index - 64);
		uint8_t mask1 = (1 << IB[mode]) - 1;
		uint8_t mask2 = (1 << (IB[mode] - 1)) - 1;
		for (int i = 0; i < 16; i++)
			if (i == anchor_index[subset_index[i]]) {
				// Highest bit is zero.
				color_index[i] = data1 & mask2;
				data1 >>= IB[mode] - 1;
				alpha_index[i] = color_index[i];
			}
			else {
				color_index[i] = data1 & mask1;
				data1 >>= IB[mode];
				alpha_index[i] = color_index[i];
			}
	}
	else {	// Implies mode 4.
		// Because the bits cross the 64-bit word boundary, we have to be careful.
		// Block index is 50 at this point.
		uint64_t data = block.data0 >> 50;
		data |= block.data1 << 14;
		for (int i = 0; i < 16; i++)
			if (i == anchor_index[subset_index[i]]) {
				// Highest bit is zero.
				if (index_selection_bit) {	// Implies mode == 4.
					alpha_index[i] = data & 0x1;
					data >>= 1;
				}
				else {
					color_index[i] = data & 0x1;
					data >>= 1;
				}
			}
			else {
				if (index_selection_bit) {	// Implies mode == 4.
					alpha_index[i] = data & 0x3;
					data >>= 2;
				}
				else {
					color_index[i] = data & 0x3;
					data >>= 2;
				}
			}
		// Block index is 81 at this point.
		data1 = block.data1 >> (81 - 64);
	}
	// Extract secondary index bits.
	if (IB2[mode] > 0) {
		uint8_t mask1 = (1 << IB2[mode]) - 1;
		uint8_t mask2 = (1 << (IB2[mode] - 1)) - 1;
		for (int i = 0; i < 16; i++)
			if (i == anchor_index[subset_index[i]]) {
				// Highest bit is zero.
				if (index_selection_bit) {
					color_index[i] = data1 & 0x3;
					data1 >>= 2;
				}
				else {
//					alpha_index[i] = block_extract_bits(&block, IB2[mode] - 1);
					alpha_index[i] = data1 & mask2;
					data1 >>= IB2[mode] - 1;
				}
			}
			else {
				if (index_selection_bit) {
					color_index[i] = data1 & 0x7;
					data1 >>= 3;
				}
				else {
//					alpha_index[i] = block_extract_bits(&block, IB2[mode]);
					alpha_index[i] = data1 & mask1;
					data1 >>= IB2[mode];
				}
			}
	}

	uint32_t *pixel32_buffer = (uint32_t *)pixel_buffer;
	for (int i = 0; i < 16; i++) {
		uint8_t endpoint_start[4];
		uint8_t endpoint_end[4];
		for (int j = 0; j < 4; j++) {
			 endpoint_start[j] = endpoint_array[2 * subset_index[i] * 4 + j];
			 endpoint_end[j] = endpoint_array[(2 * subset_index[i] + 1) * 4 + j];
		}

		uint32_t output = 0;
		output = detexPack32R8(Interpolate(endpoint_start[0], endpoint_end[0], color_index[i], color_index_bitcount));
		output |= detexPack32G8(Interpolate(endpoint_start[1], endpoint_end[1], color_index[i], color_index_bitcount));
		output |= detexPack32B8(Interpolate(endpoint_start[2], endpoint_end[2], color_index[i], color_index_bitcount));
		output |= detexPack32A8(Interpolate(endpoint_start[3], endpoint_end[3], alpha_index[i], alpha_index_bitcount));
   
		if (rotation > 0) {
			if (rotation == 1)
				output = detexPack32RGBA8(detexPixel32GetA8(output), detexPixel32GetG8(output),
					detexPixel32GetB8(output), detexPixel32GetR8(output));
			else
			if (rotation == 2)
				output = detexPack32RGBA8(detexPixel32GetR8(output), detexPixel32GetA8(output),
					detexPixel32GetB8(output), detexPixel32GetG8(output));
			else // rotation == 3
				output = detexPack32RGBA8(detexPixel32GetR8(output), detexPixel32GetG8(output),
					detexPixel32GetA8(output), detexPixel32GetB8(output));
		}
		pixel32_buffer[i] = output;
	}
	return true;
}

#if 0
/* Modify compressed block to use specific colors. For later use. */
static void SetBlockColors(uint8_t * DETEX_RESTRICT bitstring, uint32_t flags,
uint32_t * DETEX_RESTRICT colors) {
	if ((flags & TWO_COLORS) == 0)
		return;
	uint64_t data0 = *(uint64_t *)&bitstring[0];
	uint64_t data1 = *(uint64_t *)&bitstring[8];
	if ((flags & BPTC_MODE_ALLOWED_ALL) == (1 << 3)) {
		// Mode 3, 7 color bits.
		// Color bits at index: 10
		// Color bits end before index: 10 + 4 * 3 * 7 = 94
		uint32_t r0 = detexPixel32GetR8(colors[0]);
		uint32_t g0 = detexPixel32GetG8(colors[0]);
		uint32_t b0 = detexPixel32GetB8(colors[0]);
		uint32_t r1 = detexPixel32GetR8(colors[1]);
		uint32_t g1 = detexPixel32GetG8(colors[1]);
		uint32_t b1 = detexPixel32GetB8(colors[1]);
		data0 = detexSetBits64(data0, 10, 16, r0 >> 1);
		data0 = detexSetBits64(data0, 17, 23, r0 >> 1);
		data0 = detexSetBits64(data0, 24, 30, r1 >> 1);
		data0 = detexSetBits64(data0, 31, 37, r1 >> 1);
		data0 = detexSetBits64(data0, 38, 44, g0 >> 1);
		data0 = detexSetBits64(data0, 45, 51, g0 >> 1);
		data0 = detexSetBits64(data0, 52, 58, g1 >> 1);
		data0 = detexSetBits64(data0, 59, 63, (g1 >> 1) & 0x1F);
		data1 = detexSetBits64(data1, 0, 1, ((g1 >> 1) & 0x60) >> 5);
		data1 = detexSetBits64(data1, 2, 8, b0 >> 1);
		data1 = detexSetBits64(data1, 9, 15, b0 >> 1);
		data1 = detexSetBits64(data1, 16, 22, b1 >> 1);
		data1 = detexSetBits64(data1, 23, 29, b1 >> 1);
		*(uint64_t *)&bitstring[0] = data0;
		*(uint64_t *)&bitstring[8] = data1;
//		printf("bptc_set_block_colors: Colors set for mode 3.\n");
	}
	else if ((flags & BPTC_MODE_ALLOWED_ALL) == (1 << 5)) {
		// Mode 5, 7 color bits, 8 alpha bits.
		// Color bits at index: 6 + 2 = 8
		// Alpha bits at index: 8 + 2 * 3 * 7 = 50
		// Alpha bits end before index: 50 + 2 * 8 = 66
		uint32_t r0 = detexPixel32GetR8(colors[0]);
		uint32_t g0 = detexPixel32GetG8(colors[0]);
		uint32_t b0 = detexPixel32GetB8(colors[0]);
		uint32_t r1 = detexPixel32GetR8(colors[1]);
		uint32_t g1 = detexPixel32GetG8(colors[1]);
		uint32_t b1 = detexPixel32GetB8(colors[1]);
		data0 = detexSetBits64(data0, 8, 14, r0 >> 1);
		data0 = detexSetBits64(data0, 15, 21, r1 >> 1);
		data0 = detexSetBits64(data0, 22, 28, g0 >> 1);
		data0 = detexSetBits64(data0, 29, 35, g0 >> 1);
		data0 = detexSetBits64(data0, 36, 42, b0 >> 1);
		data0 = detexSetBits64(data0, 43, 49, b1 >> 1);
		if (flags & (MODES_ALLOWED_PUNCHTHROUGH_ONLY)) {
			data0 = detexSetBits64(data0, 50, 57, 0x00);
			data0 = detexSetBits64(data0, 58, 63, 0x3F);
			data1 = detexSetBits64(data1, 0, 1, 0x3);
		}
		*(uint64_t *)&bitstring[0] = data0;
		*(uint64_t *)&bitstring[8] = data1;
//		printf("bptc_set_block_colors: Colors set for mode 5.\n");
	}
	else if ((flags & BPTC_MODE_ALLOWED_ALL) == (1 << 6)) {
		// Mode 5, 7 color bits, 7 alpha bits.
		// Color bits at index 7.
		// Alpha bits at index: 7 + 2 * 3 * 7 = 49
		// Alpha bits end before index: 49 + 2 * 7 = 63
		uint32_t r0 = detexPixel32GetR8(colors[0]);
		uint32_t g0 = detexPixel32GetG8(colors[0]);
		uint32_t b0 = detexPixel32GetB8(colors[0]);
		uint32_t r1 = detexPixel32GetR8(colors[1]);
		uint32_t g1 = detexPixel32GetG8(colors[1]);
		uint32_t b1 = detexPixel32GetB8(colors[1]);
		data0 = detexSetBits64(data0, 7, 13, r0 >> 1);
		data0 = detexSetBits64(data0, 14, 20, r1 >> 1);
		data0 = detexSetBits64(data0, 21, 27, g0 >> 1);
		data0 = detexSetBits64(data0, 28, 34, g1 >> 1);
		data0 = detexSetBits64(data0, 35, 41, b0 >> 1);
		data0 = detexSetBits64(data0, 42, 48, b1 >> 1);
		if (flags & (MODES_ALLOWED_PUNCHTHROUGH_ONLY)) {
			data0 = detexSetBits64(data0, 49, 55, 0x00);
			data0 = detexSetBits64(data0, 56, 62, 0x7F);
		}
		*(uint64_t *)&bitstring[0] = data0;
//		printf("bptc_set_block_colors: Colors set for mode 6.\n");
	}
}
#endif

/* Return the internal mode of the BPTC block. */
uint32_t detexGetModeBPTC(const uint8_t *bitstring) {
	detexBlock128 block;
	block.data0 = *(uint64_t *)&bitstring[0];
	block.data1 = *(uint64_t *)&bitstring[8];
	block.index = 0;
	int mode = ExtractMode(&block);
	return mode;
}

void detexSetModeBPTC(uint8_t *bitstring, uint32_t mode, uint32_t flags,
uint32_t *colors) {
	// Mode 0 starts with 1
	// Mode 1 starts with 01
	// ...
	// Mode 7 starts with 00000001
	int bit = 0x1 << mode;
	bitstring[0] &= ~(bit - 1);
	bitstring[0] |= bit;
	return;
}



================================================
FILE: decompress-eac.c
================================================
/*

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.

*/

#include "detex.h"

static const int8_t eac_modifier_table[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 DETEX_INLINE_ONLY int modifier_times_multiplier(int modifier, int multiplier) {
	return modifier * multiplier;
}

static DETEX_INLINE_ONLY void ProcessPixelEAC(uint8_t i, uint64_t pixels,
const int8_t * DETEX_RESTRICT modifier_table, int base_codeword, int multiplier,
uint8_t * DETEX_RESTRICT pixel_buffer) {
	int modifier = modifier_table[(pixels >> (45 - i * 3)) & 7];
	pixel_buffer[((i & 3) * 4 + ((i & 12) >> 2)) * 4 + DETEX_PIXEL32_ALPHA_BYTE_OFFSET] =
		detexClamp0To255(base_codeword + modifier_times_multiplier(modifier, multiplier));
}

/* Decompress a 128-bit 4x4 pixel texture block compressed using the ETC2_EAC */
/* format. */
bool detexDecompressBlockETC2_EAC(const uint8_t * DETEX_RESTRICT bitstring, uint32_t mode_mask,
uint32_t flags, uint8_t * DETEX_RESTRICT pixel_buffer) {
	bool r = detexDecompressBlockETC2(&bitstring[8], mode_mask, flags, pixel_buffer);
	if (!r)
		return false;
	// Decode the alpha part.
	int base_codeword = bitstring[0];
	const int8_t *modifier_table = eac_modifier_table[(bitstring[1] & 0x0F)];
	int multiplier = (bitstring[1] & 0xF0) >> 4;
	if (multiplier == 0 && (flags & DETEX_DECOMPRESS_FLAG_ENCODE))
		// Not allowed in encoding. Decoder should handle it.
		return false;
	uint64_t pixels = ((uint64_t)bitstring[2] << 40) | ((uint64_t)bitstring[3] << 32) |
		((uint64_t)bitstring[4] << 24)
		| ((uint64_t)bitstring[5] << 16) | ((uint64_t)bitstring[6] << 8) | bitstring[7];
	ProcessPixelEAC(0, pixels, modifier_table, base_codeword, multiplier, pixel_buffer);
	ProcessPixelEAC(1, pixels, modifier_table, base_codeword, multiplier, pixel_buffer);
	ProcessPixelEAC(2, pixels, modifier_table, base_codeword, multiplier, pixel_buffer);
	ProcessPixelEAC(3, pixels, modifier_table, base_codeword, multiplier, pixel_buffer);
	ProcessPixelEAC(4, pixels, modifier_table, base_codeword, multiplier, pixel_buffer);
	ProcessPixelEAC(5, pixels, modifier_table, base_codeword, multiplier, pixel_buffer);
	ProcessPixelEAC(6, pixels, modifier_table, base_codeword, multiplier, pixel_buffer);
	ProcessPixelEAC(7, pixels, modifier_table, base_codeword, multiplier, pixel_buffer);
	ProcessPixelEAC(8, pixels, modifier_table, base_codeword, multiplier, pixel_buffer);
	ProcessPixelEAC(9, pixels, modifier_table, base_codeword, multiplier, pixel_buffer);
	ProcessPixelEAC(10, pixels, modifier_table, base_codeword, multiplier, pixel_buffer);
	ProcessPixelEAC(11, pixels, modifier_table, base_codeword, multiplier, pixel_buffer);
	ProcessPixelEAC(12, pixels, modifier_table, base_codeword, multiplier, pixel_buffer);
	ProcessPixelEAC(13, pixels, modifier_table, base_codeword, multiplier, pixel_buffer);
	ProcessPixelEAC(14, pixels, modifier_table, base_codeword, multiplier, pixel_buffer);
	ProcessPixelEAC(15, pixels, modifier_table, base_codeword, multiplier, pixel_buffer);
	return true;
}

/* Return the internal mode of a ETC2_EAC block. */
uint32_t detexGetModeETC2_EAC(const uint8_t *bitstring) {
	return detexGetModeETC2(&bitstring[8]);
}

void detexSetModeETC2_EAC(uint8_t *bitstring, uint32_t mode, uint32_t flags,
uint32_t *colors) {
	detexSetModeETC2(&bitstring[8], mode, flags, colors);
}

static DETEX_INLINE_ONLY int Clamp0To2047(int x) {
	if (x < 0)
		return 0;
	if (x > 2047)
		return 2047;
	return x;
}

// For each pixel, decode an 11-bit integer and store as follows:
// If shift and offset are zero, store each value in consecutive 16 bit values in pixel_buffer.
// If shift is one, store each value in consecutive 32-bit words in pixel_buffer; if offset
// is zero, store it in the first 16 bits, if offset is one store it in the last 16 bits of each
// 32-bit word.
static DETEX_INLINE_ONLY void DecodeBlockEAC11Bit(uint64_t qword, int shift, int offset,
uint8_t * DETEX_RESTRICT pixel_buffer) {
	int base_codeword_times_8_plus_4 = ((qword & 0xFF00000000000000) >> (56 - 3)) | 0x4;
	int modifier_index = (qword & 0x000F000000000000) >> 48;
	const int8_t *modifier_table = eac_modifier_table[modifier_index];
	int multiplier_times_8 = (qword & 0x00F0000000000000) >> (52 - 3);
	if (multiplier_times_8 == 0)
		multiplier_times_8 = 1;
	uint16_t *buffer = (uint16_t *)pixel_buffer;
	for (int i = 0; i < 16; i++) {
		int pixel_index = (qword & (0x0000E00000000000 >> (i * 3))) >> (45 - i * 3);
		int modifier = modifier_table[pixel_index];
		uint32_t value = Clamp0To2047(base_codeword_times_8_plus_4 +
			modifier * multiplier_times_8);
		buffer[(((i & 3) * 4 + ((i & 12) >> 2)) << shift) + offset] =
			(value << 5) | (value >> 6);	// Replicate bits to 16-bit.
	}
}

/* Decompress a 64-bit 4x4 pixel texture block compressed using the */
/* EAC_R11 format. */
bool detexDecompressBlockEAC_R11(const uint8_t * DETEX_RESTRICT bitstring, uint32_t mode_mask,
uint32_t flags, uint8_t * DETEX_RESTRICT pixel_buffer) {
	uint64_t qword = ((uint64_t)bitstring[0] << 56) | ((uint64_t)bitstring[1] << 48) |
		((uint64_t)bitstring[2] << 40) |
		((uint64_t)bitstring[3] << 32) | ((uint64_t)bitstring[4] << 24) |
		((uint64_t)bitstring[5] << 16) | ((uint64_t)bitstring[6] << 8) | bitstring[7];
	DecodeBlockEAC11Bit(qword, 0, 0, pixel_buffer);
	return true;
}

/* Decompress a 128-bit 4x4 pixel texture block compressed using the */
/* EAC_RG11 format. */
bool detexDecompressBlockEAC_RG11(const uint8_t * DETEX_RESTRICT bitstring, uint32_t mode_mask,
uint32_t flags, uint8_t * DETEX_RESTRICT pixel_buffer) {
	uint64_t red_qword = ((uint64_t)bitstring[0] << 56) | ((uint64_t)bitstring[1] << 48) |
		((uint64_t)bitstring[2] << 40) |
		((uint64_t)bitstring[3] << 32) | ((uint64_t)bitstring[4] << 24) |
		((uint64_t)bitstring[5] << 16) | ((uint64_t)bitstring[6] << 8) | bitstring[7];
	DecodeBlockEAC11Bit(red_qword, 1, 0, pixel_buffer);
	uint64_t green_qword = ((uint64_t)bitstring[8] << 56) | ((uint64_t)bitstring[9] << 48) |
		((uint64_t)bitstring[10] << 40) |
		((uint64_t)bitstring[11] << 32) | ((uint64_t)bitstring[12] << 24) |
		((uint64_t)bitstring[13] << 16) | ((uint64_t)bitstring[14] << 8) | bitstring[15];
	DecodeBlockEAC11Bit(green_qword, 1, 1, pixel_buffer);
	return true;
}

static DETEX_INLINE_ONLY int ClampMinus1023To1023(int x) {
	if (x < - 1023)
		return - 1023;
	if (x > 1023)
		return 1023;
	return x;
}

static DETEX_INLINE_ONLY uint32_t ReplicateSigned11BitsTo16Bits(int value) {
	if (value >= 0)
		return (value << 5) | (value >> 5);
	value = - value;
	value = (value << 5) | (value >> 5);
	return - value;
}

// For each pixel, decode an 11-bit signed integer and store as follows:
// If shift and offset are zero, store each value in consecutive 16 bit values in pixel_buffer.
// If shift is one, store each value in consecutive 32-bit words in pixel_buffer; if offset
// is zero, store it in the first 16 bits, if offset is one store it in the last 16 bits of each
// 32-bit word.
static DETEX_INLINE_ONLY bool DecodeBlockEACSigned11Bit(uint64_t qword, int shift, int offset,
uint8_t *pixel_buffer) {
	int base_codeword = (int8_t)((qword & 0xFF00000000000000) >> 56);	// Signed 8 bits.
	if (base_codeword == - 128)
		// Not allowed in encoding. Decoder should handle it but we don't do that yet.
		return false;
	int base_codeword_times_8 = base_codeword << 3;				// Arithmetic shift.
	int modifier_index = (qword & 0x000F000000000000) >> 48;
	const int8_t *modifier_table = eac_modifier_table[modifier_index];
	int multiplier_times_8 = (qword & 0x00F0000000000000) >> (52 - 3);
	if (multiplier_times_8 == 0)
		multiplier_times_8 = 1;
	uint16_t *buffer = (uint16_t *)pixel_buffer;
	for (int i = 0; i < 16; i++) {
		int pixel_index = (qword & (0x0000E00000000000 >> (i * 3))) >> (45 - i * 3);
		int modifier = modifier_table[pixel_index];
		int value = ClampMinus1023To1023(base_codeword_times_8 +
			modifier * multiplier_times_8);
		uint32_t bits = ReplicateSigned11BitsTo16Bits(value);
		buffer[(((i & 3) * 4 + ((i & 12) >> 2)) << shift) + offset] = bits;
	}
	return true;
}

/* Decompress a 64-bit 4x4 pixel texture block compressed using the */
/* EAC_SIGNED_R11 format. */
bool detexDecompressBlockEAC_SIGNED_R11(const uint8_t * DETEX_RESTRICT bitstring,
uint32_t mode_mask, uint32_t flags, uint8_t * DETEX_RESTRICT pixel_buffer) {
	uint64_t qword = ((uint64_t)bitstring[0] << 56) | ((uint64_t)bitstring[1] << 48) |
		((uint64_t)bitstring[2] << 40) |
		((uint64_t)bitstring[3] << 32) | ((uint64_t)bitstring[4] << 24) |
		((uint64_t)bitstring[5] << 16) | ((uint64_t)bitstring[6] << 8) | bitstring[7];
	return DecodeBlockEACSigned11Bit(qword, 0, 0, pixel_buffer);
}

/* Decompress a 128-bit 4x4 pixel texture block compressed using the */
/* EAC_SIGNED_RG11 format. */
bool detexDecompressBlockEAC_SIGNED_RG11(const uint8_t * DETEX_RESTRICT bitstring,
uint32_t mode_mask, uint32_t flags, uint8_t * DETEX_RESTRICT pixel_buffer) {
	uint64_t red_qword = ((uint64_t)bitstring[0] << 56) | ((uint64_t)bitstring[1] << 48) |
		((uint64_t)bitstring[2] << 40) |
		((uint64_t)bitstring[3] << 32) | ((uint64_t)bitstring[4] << 24) |
		((uint64_t)bitstring[5] << 16) | ((uint64_t)bitstring[6] << 8) | bitstring[7];
	int r = DecodeBlockEACSigned11Bit(red_qword, 1, 0, pixel_buffer);
	if (!r)
		return false;
	uint64_t green_qword = ((uint64_t)bitstring[8] << 56) | ((uint64_t)bitstring[9] << 48) |
		((uint64_t)bitstring[10] << 40) |
		((uint64_t)bitstring[11] << 32) | ((uint64_t)bitstring[12] << 24) |
		((uint64_t)bitstring[13] << 16) | ((uint64_t)bitstring[14] << 8) | bitstring[15];
	return DecodeBlockEACSigned11Bit(green_qword, 1, 1, pixel_buffer);
}



================================================
FILE: decompress-etc.c
================================================
/*

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.

*/

#include "detex.h"

static const int complement3bitshifted_table[8] = {
	0, 8, 16, 24, -32, -24, -16, -8
};

static const int modifier_table[8][4] = {
	{ 2, 8, -2, -8 },
	{ 5, 17, -5, -17 },
	{ 9, 29, -9, -29 },
	{ 13, 42, -13, -42 },
	{ 18, 60, -18, -60 },
	{ 24, 80, -24, -80 },
	{ 33, 106, -33, -106 },
	{ 47, 183, -47, -183 }
};

static DETEX_INLINE_ONLY int clamp2047(int x) {
	if (x < 0)
		return 0;
	if (x > 2047)
		return 2047;
	return x;
}

static DETEX_INLINE_ONLY int clamp1023_signed(int x) {
	if (x < - 1023)
		return - 1023;
	if (x > 1023)
		return 1023;
	return x;
}

// This function calculates the 3-bit complement value in the range -4 to 3 of a three bit
// representation. The result is arithmetically shifted 3 places to the left before returning.
static DETEX_INLINE_ONLY int complement3bitshifted(int x) {
	return complement3bitshifted_table[x];
}

static DETEX_INLINE_ONLY int complement3bitshifted_slow(int x) {
	if (x & 4)
		return ((x & 3) - 4) << 3;	// Note: shift is arithmetic.
	return x << 3;
}

static DETEX_INLINE_ONLY int complement3bit(int x) {
	if (x & 4)
		return ((x & 3) - 4);
	return x;
}

// Define inline function to speed up ETC1 decoding.

static DETEX_INLINE_ONLY void ProcessPixelETC1(uint8_t i, uint32_t pixel_index_word,
uint32_t table_codeword, int * DETEX_RESTRICT base_color_subblock,
uint8_t * DETEX_RESTRICT pixel_buffer) {
	int pixel_index = ((pixel_index_word & (1 << i)) >> i)
		| ((pixel_index_word & (0x10000 << i)) >> (16 + i - 1));
	int r, g, b;
	int modifier = modifier_table[table_codeword][pixel_index];
	r = detexClamp0To255(base_color_subblock[0] + modifier);
	g = detexClamp0To255(base_color_subblock[1] + modifier);
	b = detexClamp0To255(base_color_subblock[2] + modifier);
	uint32_t *buffer = (uint32_t *)pixel_buffer;
	buffer[(i & 3) * 4 + ((i & 12) >> 2)] =
		detexPack32RGB8Alpha0xFF(r, g, b);
}

/* Decompress a 64-bit 4x4 pixel texture block compressed using the ETC1 */
/* format. */
bool detexDecompressBlockETC1(const uint8_t * DETEX_RESTRICT bitstring, uint32_t mode_mask,
uint32_t flags, uint8_t * DETEX_RESTRICT pixel_buffer) {
	int differential_mode = bitstring[3] & 2;
	if (differential_mode) {
		if ((mode_mask & DETEX_MODE_MASK_ETC_DIFFERENTIAL) == 0)
			return false;
	}
	else
		if ((mode_mask & DETEX_MODE_MASK_ETC_INDIVIDUAL) == 0)
			return false;
	int flipbit = bitstring[3] & 1;
	int base_color_subblock1[3];
	int base_color_subblock2[3];
	if (differential_mode) {
		base_color_subblock1[0] = (bitstring[0] & 0xF8);
		base_color_subblock1[0] |= ((base_color_subblock1[0] & 224) >> 5);
		base_color_subblock1[1] = (bitstring[1] & 0xF8);
		base_color_subblock1[1] |= (base_color_subblock1[1] & 224) >> 5;
		base_color_subblock1[2] = (bitstring[2] & 0xF8);
		base_color_subblock1[2] |= (base_color_subblock1[2] & 224) >> 5;
		base_color_subblock2[0] = (bitstring[0] & 0xF8);			// 5 highest order bits.
		base_color_subblock2[0] += complement3bitshifted(bitstring[0] & 7);	// Add difference.
		if (base_color_subblock2[0] & 0xFF07)					// Check for overflow.
			return false;
		base_color_subblock2[0] |= (base_color_subblock2[0] & 224) >> 5;	// Replicate.
		base_color_subblock2[1] = (bitstring[1] & 0xF8);
		base_color_subblock2[1] += complement3bitshifted(bitstring[1] & 7);
		if (base_color_subblock2[1] & 0xFF07)
			return false;
		base_color_subblock2[1] |= (base_color_subblock2[1] & 224) >> 5;
		base_color_subblock2[2] = (bitstring[2] & 0xF8);
		base_color_subblock2[2] += complement3bitshifted(bitstring[2] & 7);
		if (base_color_subblock2[2] & 0xFF07)
			return false;
		base_color_subblock2[2] |= (base_color_subblock2[2] & 224) >> 5;
	}
	else {
		base_color_subblock1[0] = (bitstring[0] & 0xF0);
		base_color_subblock1[0] |= base_color_subblock1[0] >> 4;
		base_color_subblock1[1] = (bitstring[1] & 0xF0);
		base_color_subblock1[1] |= base_color_subblock1[1] >> 4;
		base_color_subblock1[2] = (bitstring[2] & 0xF0);
		base_color_subblock1[2] |= base_color_subblock1[2] >> 4;
		base_color_subblock2[0] = (bitstring[0] & 0x0F);
		base_color_subblock2[0] |= base_color_subblock2[0] << 4;
		base_color_subblock2[1] = (bitstring[1] & 0x0F);
		base_color_subblock2[1] |= base_color_subblock2[1] << 4;
		base_color_subblock2[2] = (bitstring[2] & 0x0F);
		base_color_subblock2[2] |= base_color_subblock2[2] << 4;
	}
	uint32_t table_codeword1 = (bitstring[3] & 224) >> 5;
	uint32_t table_codeword2 = (bitstring[3] & 28) >> 2;
	uint32_t pixel_index_word = ((uint32_t)bitstring[4] << 24) | ((uint32_t)bitstring[5] << 16) |
		((uint32_t)bitstring[6] << 8) | bitstring[7];
	if (flipbit == 0) {
		ProcessPixelETC1(0, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC1(1, pixel_index_word, table_codeword1,base_color_subblock1, pixel_buffer);
		ProcessPixelETC1(2, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC1(3, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC1(4, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC1(5, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC1(6, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC1(7, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC1(8, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC1(9, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC1(10, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC1(11, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC1(12, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC1(13, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC1(14, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC1(15, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
	}
	else {
		ProcessPixelETC1(0, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC1(1, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC1(2, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC1(3, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC1(4, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC1(5, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC1(6, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC1(7, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC1(8, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC1(9, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC1(10, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC1(11, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC1(12, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC1(13, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC1(14, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC1(15, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
	}
	return true;
}

/* Return the internal mode of a ETC1 block. */
uint32_t detexGetModeETC1(const uint8_t *bitstring) {
	// Figure out the mode.
	if ((bitstring[3] & 2) == 0)
		// Individual mode.
		return 0;
	else
		return 1;
}

void detexSetModeETC1(uint8_t *bitstring, uint32_t mode, uint32_t flags,
uint32_t *colors) {
	if (mode == 0)
		bitstring[3] &= ~0x2;
	else
		bitstring[3] |= 0x2;
}

static const int etc2_distance_table[8] = { 3, 6, 11, 16, 23, 32, 41, 64 };

static void ProcessBlockETC2TOrHMode(const uint8_t * DETEX_RESTRICT bitstring, int mode,
uint8_t * DETEX_RESTRICT pixel_buffer) {
	int base_color1_R, base_color1_G, base_color1_B;
	int base_color2_R, base_color2_G, base_color2_B;
	int paint_color_R[4], paint_color_G[4], paint_color_B[4];
	int distance;
	if (mode == DETEX_MODE_MASK_ETC_T) {
		// T mode.
		base_color1_R = ((bitstring[0] & 0x18) >> 1) | (bitstring[0] & 0x3);
		base_color1_R |= base_color1_R << 4;
		base_color1_G = bitstring[1] & 0xF0;
		base_color1_G |= base_color1_G >> 4;
		base_color1_B = bitstring[1] & 0x0F;
		base_color1_B |= base_color1_B << 4;
		base_color2_R = bitstring[2] & 0xF0;
		base_color2_R |= base_color2_R >> 4;
		base_color2_G = bitstring[2] & 0x0F;
		base_color2_G |= base_color2_G << 4;
		base_color2_B = bitstring[3] & 0xF0;
		base_color2_B |= base_color2_B >> 4;
		// index = (da << 1) | db
		distance = etc2_distance_table[((bitstring[3] & 0x0C) >> 1) | (bitstring[3] & 0x1)];
		paint_color_R[0] = base_color1_R;
		paint_color_G[0] = base_color1_G;
		paint_color_B[0] = base_color1_B;
		paint_color_R[2] = base_color2_R;
		paint_color_G[2] = base_color2_G;
		paint_color_B[2] = base_color2_B;
		paint_color_R[1] = detexClamp0To255(base_color2_R + distance);
		paint_color_G[1] = detexClamp0To255(base_color2_G + distance);
		paint_color_B[1] = detexClamp0To255(base_color2_B + distance);
		paint_color_R[3] = detexClamp0To255(base_color2_R - distance);
		paint_color_G[3] = detexClamp0To255(base_color2_G - distance);
		paint_color_B[3] = detexClamp0To255(base_color2_B - distance);
	}
	else {
		// H mode.
		base_color1_R = (bitstring[0] & 0x78) >> 3;
		base_color1_R |= base_color1_R << 4;
		base_color1_G = ((bitstring[0] & 0x07) << 1) | ((bitstring[1] & 0x10) >> 4);
		base_color1_G |= base_color1_G << 4;
		base_color1_B = (bitstring[1] & 0x08) | ((bitstring[1] & 0x03) << 1) | ((bitstring[2] & 0x80) >> 7);
		base_color1_B |= base_color1_B << 4;
		base_color2_R = (bitstring[2] & 0x78) >> 3;
		base_color2_R |= base_color2_R << 4;
		base_color2_G = ((bitstring[2] & 0x07) << 1) | ((bitstring[3] & 0x80) >> 7);
		base_color2_G |= base_color2_G << 4;
		base_color2_B = (bitstring[3] & 0x78) >> 3;
		base_color2_B |= base_color2_B << 4;
		// da is most significant bit, db is middle bit, least significant bit is
		// (base_color1 value >= base_color2 value).
		int base_color1_value = (base_color1_R << 16) + (base_color1_G << 8) + base_color1_B;
		int base_color2_value = (base_color2_R << 16) + (base_color2_G << 8) + base_color2_B;
		int bit;
		if (base_color1_value >= base_color2_value)
			bit = 1;
		else
			bit = 0;
		distance = etc2_distance_table[(bitstring[3] & 0x04) | ((bitstring[3] & 0x01) << 1) | bit];
		paint_color_R[0] = detexClamp0To255(base_color1_R + distance);
		paint_color_G[0] = detexClamp0To255(base_color1_G + distance);
		paint_color_B[0] = detexClamp0To255(base_color1_B + distance);
		paint_color_R[1] = detexClamp0To255(base_color1_R - distance);
		paint_color_G[1] = detexClamp0To255(base_color1_G - distance);
		paint_color_B[1] = detexClamp0To255(base_color1_B - distance);
		paint_color_R[2] = detexClamp0To255(base_color2_R + distance);
		paint_color_G[2] = detexClamp0To255(base_color2_G + distance);
		paint_color_B[2] = detexClamp0To255(base_color2_B + distance);
		paint_color_R[3] = detexClamp0To255(base_color2_R - distance);
		paint_color_G[3] = detexClamp0To255(base_color2_G - distance);
		paint_color_B[3] = detexClamp0To255(base_color2_B - distance);
	}
	uint32_t pixel_index_word = ((uint32_t)bitstring[4] << 24) | ((uint32_t)bitstring[5] << 16) |
		((uint32_t)bitstring[6] << 8) | bitstring[7];
	uint32_t *buffer = (uint32_t *)pixel_buffer;
	for (int i = 0; i < 16; i++) {
		int pixel_index = ((pixel_index_word & (1 << i)) >> i)			// Least significant bit.
			| ((pixel_index_word & (0x10000 << i)) >> (16 + i - 1));	// Most significant bit.
		int r = paint_color_R[pixel_index];
		int g = paint_color_G[pixel_index];
		int b = paint_color_B[pixel_index];
		buffer[(i & 3) * 4 + ((i & 12) >> 2)] = detexPack32RGB8Alpha0xFF(r, g, b);
	}
}

static void ProcessBlockETC2PlanarMode(const uint8_t * DETEX_RESTRICT bitstring,
uint8_t * DETEX_RESTRICT pixel_buffer) {
	// Each color O, H and V is in 6-7-6 format.
	int RO = (bitstring[0] & 0x7E) >> 1;
	int GO = ((bitstring[0] & 0x1) << 6) | ((bitstring[1] & 0x7E) >> 1);
	int BO = ((bitstring[1] & 0x1) << 5) | (bitstring[2] & 0x18) | ((bitstring[2] & 0x03) << 1) |
		((bitstring[3] & 0x80) >> 7);
	int RH = ((bitstring[3] & 0x7C) >> 1) | (bitstring[3] & 0x1);
	int GH = (bitstring[4] & 0xFE) >> 1;
	int BH = ((bitstring[4] & 0x1) << 5) | ((bitstring[5] & 0xF8) >> 3);
	int RV = ((bitstring[5] & 0x7) << 3) | ((bitstring[6] & 0xE0) >> 5);
	int GV = ((bitstring[6] & 0x1F) << 2) | ((bitstring[7] & 0xC0) >> 6);
	int BV = bitstring[7] & 0x3F;
	RO = (RO << 2) | ((RO & 0x30) >> 4);	// Replicate bits.
	GO = (GO << 1) | ((GO & 0x40) >> 6);
	BO = (BO << 2) | ((BO & 0x30) >> 4);
	RH = (RH << 2) | ((RH & 0x30) >> 4);
	GH = (GH << 1) | ((GH & 0x40) >> 6);
	BH = (BH << 2) | ((BH & 0x30) >> 4);
	RV = (RV << 2) | ((RV & 0x30) >> 4);
	GV = (GV << 1) | ((GV & 0x40) >> 6);
	BV = (BV << 2) | ((BV & 0x30) >> 4);
	uint32_t *buffer = (uint32_t *)pixel_buffer;
	for (int y = 0; y < 4; y++)
		for (int x = 0; x < 4; x++) {
			int r = detexClamp0To255((x * (RH - RO) + y * (RV - RO) + 4 * RO + 2) >> 2);
			int g = detexClamp0To255((x * (GH - GO) + y * (GV - GO) + 4 * GO + 2) >> 2);
			int b = detexClamp0To255((x * (BH - BO) + y * (BV - BO) + 4 * BO + 2) >> 2);
			buffer[y * 4 + x] = detexPack32RGB8Alpha0xFF(r, g, b);
		}
}

/* Decompress a 64-bit 4x4 pixel texture block compressed using the ETC2 */
/* format. */
bool detexDecompressBlockETC2(const uint8_t * DETEX_RESTRICT bitstring, uint32_t mode_mask,
uint32_t flags, uint8_t * DETEX_RESTRICT pixel_buffer) {
	// Figure out the mode.
	if ((bitstring[3] & 2) == 0) {
		// Individual mode.
		return detexDecompressBlockETC1(bitstring, mode_mask, flags,
			pixel_buffer);
	}
	if ((mode_mask & (~DETEX_MODE_MASK_ETC_INDIVIDUAL)) == 0)
		return false;
	int R = (bitstring[0] & 0xF8);
	R += complement3bitshifted(bitstring[0] & 7);
	int G = (bitstring[1] & 0xF8);
	G += complement3bitshifted(bitstring[1] & 7);
	int B = (bitstring[2] & 0xF8);
	B += complement3bitshifted(bitstring[2] & 7);
	if (R & 0xFF07) {
		// T mode.
		if ((mode_mask & DETEX_MODE_MASK_ETC_T) == 0)
			return false;
		ProcessBlockETC2TOrHMode(bitstring, DETEX_MODE_MASK_ETC_T,
			pixel_buffer);
		return true;
	}
	else
	if (G & 0xFF07) {
		// H mode.
		if ((mode_mask & DETEX_MODE_MASK_ETC_H) == 0)
			return false;
		ProcessBlockETC2TOrHMode(bitstring, DETEX_MODE_MASK_ETC_H,
			pixel_buffer);
		return true;
	}
	else
	if (B & 0xFF07) {
		// Planar mode.
		if ((mode_mask & DETEX_MODE_MASK_ETC_PLANAR) == 0)
			return false;
		ProcessBlockETC2PlanarMode(bitstring, pixel_buffer);
		return true;
	}
	else {
		// Differential mode.
		return detexDecompressBlockETC1(bitstring, mode_mask, flags,
			pixel_buffer);
	}
}

/* Return the internal mode of a ETC2 block. */
uint32_t detexGetModeETC2(const uint8_t *bitstring) {
	// Figure out the mode.
	if ((bitstring[3] & 2) == 0)
		// Individual mode.
		return 0;
	int R = (bitstring[0] & 0xF8);
	R += complement3bitshifted(bitstring[0] & 7);
	int G = (bitstring[1] & 0xF8);
	G += complement3bitshifted(bitstring[1] & 7);
	int B = (bitstring[2] & 0xF8);
	B += complement3bitshifted(bitstring[2] & 7);
	if (R & 0xFF07)
		// T mode.
		return 2;
	else
	if (G & 0xFF07)
		// H mode.
		return 3;
	else
	if (B & 0xFF07)
		// Planar mode.
		return 4;
	else
		// Differential mode.
		return 1;
}

static void SetModeETC2THP(uint8_t *bitstring, uint32_t mode) {
	if (mode == 2) {
		// bitstring[0] bits 0, 1, 3, 4 are used.
		// Bits 2, 5, 6, 7 can be modified.
		// Modify bits 2, 5, 6, 7 so that R < 0 or R > 31.
		int R_bits_5_to_7_clear = (bitstring[0] & 0x18) >> 3;
		int R_compl_bit_2_clear = complement3bit(bitstring[0] & 0x3);
		if (R_bits_5_to_7_clear + 0x1C + R_compl_bit_2_clear > 31) {
			// Set bits 5, 6, 7 and clear bit 2.
			bitstring[0] &= ~0x04;
			bitstring[0] |= 0xE0;
		}
		else {
			int R_compl_bit_2_set = complement3bit((bitstring[0] & 0x3) | 0x4);
			if (R_bits_5_to_7_clear + R_compl_bit_2_set < 0) {
				// Clear bits 5, 6, 7 and set bit 2.
				bitstring[0] &= ~0xE0;
				bitstring[0] |= 0x04;
			}
			else
				; // Shouldn't happen.
		}
	}
	else if (mode == 3) {
		int G_bits_5_to_7_clear = (bitstring[1] & 0x18) >> 3;
		int G_compl_bit_2_clear = complement3bit(bitstring[1] & 0x3);
		if (G_bits_5_to_7_clear + 0x1C + G_compl_bit_2_clear > 31) {
			// Set bits 5, 6, 7 and clear bit 2.
			bitstring[1] &= ~0x04;
			bitstring[1] |= 0xE0;
		}
		else {
			int G_compl_bit_2_set = complement3bit((bitstring[1] & 0x3) | 0x4);
			if (G_bits_5_to_7_clear + G_compl_bit_2_set < 0) {
				// Clear bits 5, 6, 7 and set bit 2.
				bitstring[1] &= ~0xE0;
				bitstring[1] |= 0x04;
			}
			else
				; // Shouldn't happen.
		}
	}
	else if (mode == 4) {
		int B_bits_5_to_7_clear = (bitstring[2] & 0x18) >> 3;
		int B_compl_bit_2_clear = complement3bit(bitstring[2] & 0x3);
		if (B_bits_5_to_7_clear + 0x1C + B_compl_bit_2_clear > 31) {
			// Set bits 5, 6, 7 and clear bit 2.
			bitstring[2] &= ~0x04;
			bitstring[2] |= 0xE0;
		}
		else {
			int B_compl_bit_2_set = complement3bit((bitstring[2] & 0x3) | 0x4);
			if (B_bits_5_to_7_clear + B_compl_bit_2_set < 0) {
				// Clear bits 5, 6, 7 and set bit 2.
				bitstring[2] &= ~0xE0;
				bitstring[2] |= 0x04;
			}
			else
				; // Shouldn't happen.
		}
	}
}

void detexSetModeETC2(uint8_t *bitstring, uint32_t mode, uint32_t flags,
uint32_t *colors) {
	if (mode == 0)
		// Set Individual mode.
		bitstring[3] &= ~0x2;
	else {
		// Set Differential, T, H or P mode.
		bitstring[3] |= 0x2;
		SetModeETC2THP(bitstring, mode);
	}
}

static const int punchthrough_modifier_table[8][4] = {
	{ 0, 8, 0, -8 },
	{ 0, 17, 0, -17 },
	{ 0, 29, 0, -29 },
	{ 0, 42, 0, -42 },
	{ 0, 60, 0, -60 },
	{ 0, 80, 0, -80 },
	{ 0, 106, 0, -106 },
	{ 0, 183, 0, -183 }
};

static const uint32_t punchthrough_mask_table[4] = {
	0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF };

static DETEX_INLINE_ONLY void ProcessPixelETC2Punchthrough(uint8_t i,
uint32_t pixel_index_word, uint32_t table_codeword,
int * DETEX_RESTRICT base_color_subblock, uint8_t * DETEX_RESTRICT pixel_buffer) {
	int pixel_index = ((pixel_index_word & (1 << i)) >> i)
		| ((pixel_index_word & (0x10000 << i)) >> (16 + i - 1));
	int r, g, b;
	int modifier = punchthrough_modifier_table[table_codeword][pixel_index];
	r = detexClamp0To255(base_color_subblock[0] + modifier);
	g = detexClamp0To255(base_color_subblock[1] + modifier);
	b = detexClamp0To255(base_color_subblock[2] + modifier);
	uint32_t mask = punchthrough_mask_table[pixel_index];
	uint32_t *buffer = (uint32_t *)pixel_buffer;
	buffer[(i & 3) * 4 + ((i & 12) >> 2)] =
		detexPack32RGB8Alpha0xFF(r, g, b) & mask;
}


void ProcessBlockETC2PunchthroughDifferentialMode(const uint8_t * DETEX_RESTRICT bitstring,
uint8_t * DETEX_RESTRICT pixel_buffer) {
	int flipbit = bitstring[3] & 1;
	int base_color_subblock1[3];
	int base_color_subblock2[3];
	base_color_subblock1[0] = (bitstring[0] & 0xF8);
	base_color_subblock1[0] |= ((base_color_subblock1[0] & 224) >> 5);
	base_color_subblock1[1] = (bitstring[1] & 0xF8);
	base_color_subblock1[1] |= (base_color_subblock1[1] & 224) >> 5;
	base_color_subblock1[2] = (bitstring[2] & 0xF8);
	base_color_subblock1[2] |= (base_color_subblock1[2] & 224) >> 5;
	base_color_subblock2[0] = (bitstring[0] & 0xF8);				// 5 highest order bits.
	base_color_subblock2[0] += complement3bitshifted(bitstring[0] & 7);	// Add difference.
	base_color_subblock2[0] |= (base_color_subblock2[0] & 224) >> 5;		// Replicate.
	base_color_subblock2[1] = (bitstring[1] & 0xF8);
	base_color_subblock2[1] += complement3bitshifted(bitstring[1] & 7);
	base_color_subblock2[1] |= (base_color_subblock2[1] & 224) >> 5;
	base_color_subblock2[2] = (bitstring[2] & 0xF8);
	base_color_subblock2[2] += complement3bitshifted(bitstring[2] & 7);
	base_color_subblock2[2] |= (base_color_subblock2[2] & 224) >> 5;
	uint32_t table_codeword1 = (bitstring[3] & 224) >> 5;
	uint32_t table_codeword2 = (bitstring[3] & 28) >> 2;
	uint32_t pixel_index_word = ((uint32_t)bitstring[4] << 24) | ((uint32_t)bitstring[5] << 16) |
		((uint32_t)bitstring[6] << 8) | bitstring[7];
	if (flipbit == 0) {
		ProcessPixelETC2Punchthrough(0, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC2Punchthrough(1, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC2Punchthrough(2, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC2Punchthrough(3, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC2Punchthrough(4, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC2Punchthrough(5, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC2Punchthrough(6, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC2Punchthrough(7, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC2Punchthrough(8, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC2Punchthrough(9, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC2Punchthrough(10, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC2Punchthrough(11, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC2Punchthrough(12, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC2Punchthrough(13, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC2Punchthrough(14, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC2Punchthrough(15, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
	}
	else {
		ProcessPixelETC2Punchthrough(0, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC2Punchthrough(1, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC2Punchthrough(2, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC2Punchthrough(3, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC2Punchthrough(4, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC2Punchthrough(5, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC2Punchthrough(6, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC2Punchthrough(7, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC2Punchthrough(8, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC2Punchthrough(9, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC2Punchthrough(10, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC2Punchthrough(11, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC2Punchthrough(12, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC2Punchthrough(13, pixel_index_word, table_codeword1, base_color_subblock1, pixel_buffer);
		ProcessPixelETC2Punchthrough(14, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
		ProcessPixelETC2Punchthrough(15, pixel_index_word, table_codeword2, base_color_subblock2, pixel_buffer);
	}
}

static void ProcessBlockETC2PunchthroughTOrHMode(const uint8_t * DETEX_RESTRICT bitstring,
int mode, uint8_t * DETEX_RESTRICT pixel_buffer) {
	int base_color1_R, base_color1_G, base_color1_B;
	int base_color2_R, base_color2_G, base_color2_B;
	int paint_color_R[4], paint_color_G[4], paint_color_B[4];
	int distance;
	if (mode == DETEX_MODE_MASK_ETC_T) {
		// T mode.
		base_color1_R = ((bitstring[0] & 0x18) >> 1) | (bitstring[0] & 0x3);
		base_color1_R |= base_color1_R << 4;
		base_color1_G = bitstring[1] & 0xF0;
		base_color1_G |= base_color1_G >> 4;
		base_color1_B = bitstring[1] & 0x0F;
		base_color1_B |= base_color1_B << 4;
		base_color2_R = bitstring[2] & 0xF0;
		base_color2_R |= base_color2_R >> 4;
		base_color2_G = bitstring[2] & 0x0F;
		base_color2_G |= base_color2_G << 4;
		base_color2_B = bitstring[3] & 0xF0;
		base_color2_B |= base_color2_B >> 4;
		// index = (da << 1) | db
		distance = etc2_distance_table[((bitstring[3] & 0x0C) >> 1) | (bitstring[3] & 0x1)];
		paint_color_R[0] = base_color1_R;
		paint_color_G[0] = base_color1_G;
		paint_color_B[0] = base_color1_B;
		paint_color_R[2] = base_color2_R;
		paint_color_G[2] = base_color2_G;
		paint_color_B[2] = base_color2_B;
		paint_color_R[1] = detexClamp0To255(base_color2_R + distance);
		paint_color_G[1] = detexClamp0To255(base_color2_G + distance);
		paint_color_B[1] = detexClamp0To255(base_color2_B + distance);
		paint_color_R[3] = detexClamp0To255(base_color2_R - distance);
		paint_color_G[3] = detexClamp0To255(base_color2_G - distance);
		paint_color_B[3] = detexClamp0To255(base_color2_B - distance);
	}
	else {
		// H mode.
		base_color1_R = (bitstring[0] & 0x78) >> 3;
		base_color1_R |= base_color1_R << 4;
		base_color1_G = ((bitstring[0] & 0x07) << 1) | ((bitstring[1] & 0x10) >> 4);
		base_color1_G |= base_color1_G << 4;
		base_color1_B = (bitstring[1] & 0x08) | ((bitstring[1] & 0x03) << 1) | ((bitstring[2] & 0x80) >> 7);
		base_color1_B |= base_color1_B << 4;
		base_color2_R = (bitstring[2] & 0x78) >> 3;
		base_color2_R |= base_color2_R << 4;
		base_color2_G = ((bitstring[2] & 0x07) << 1) | ((bitstring[3] & 0x80) >> 7);
		base_color2_G |= base_color2_G << 4;
		base_color2_B = (bitstring[3] & 0x78) >> 3;
		base_color2_B |= base_color2_B << 4;
		// da is most significant bit, db is middle bit, least significant bit is
		// (base_color1 value >= base_color2 value).
		int base_color1_value = (base_color1_R << 16) + (base_color1_G << 8) + base_color1_B;
		int base_color2_value = (base_color2_R << 16) + (base_color2_G << 8) + base_color2_B;
		int bit;
		if (base_color1_value >= base_color2_value)
			bit = 1;
		else
			bit = 0;
		distance = etc2_distance_table[(bitstring[3] & 0x04) | ((bitstring[3] & 0x01) << 1) | bit];
		paint_color_R[0] = detexClamp0To255(base_color1_R + distance);
		paint_color_G[0] = detexClamp0To255(base_color1_G + distance);
		paint_color_B[0] = detexClamp0To255(base_color1_B + distance);
		paint_color_R[1] = detexClamp0To255(base_color1_R - distance);
		paint_color_G[1] = detexClamp0To255(base_color1_G - distance);
		paint_color_B[1] = detexClamp0To255(base_color1_B - distance);
		paint_color_R[2] = detexClamp0To255(base_color2_R + distance);
		paint_color_G[2] = detexClamp0To255(base_color2_G + distance);
		paint_color_B[2] = detexClamp0To255(base_color2_B + distance);
		paint_color_R[3] = detexClamp0To255(base_color2_R - distance);
		paint_color_G[3] = detexClamp0To255(base_color2_G - distance);
		paint_color_B[3] = detexClamp0To255(base_color2_B - distance);
	}
	uint32_t pixel_index_word = ((uint32_t)bitstring[4] << 24) | ((uint32_t)bitstring[5] << 16) |
		((uint32_t)bitstring[6] << 8) | bitstring[7];
	uint32_t *buffer = (uint32_t *)pixel_buffer;
	for (int i = 0; i < 16; i++) {
		int pixel_index = ((pixel_index_word & (1 << i)) >> i)			// Least significant bit.
			| ((pixel_index_word & (0x10000 << i)) >> (16 + i - 1));	// Most significant bit.
		int r = paint_color_R[pixel_index];
		int g = paint_color_G[pixel_index];
		int b = paint_color_B[pixel_index];
		uint32_t mask = punchthrough_mask_table[pixel_index];
		buffer[(i & 3) * 4 + ((i & 12) >> 2)] = (detexPack32RGB8Alpha0xFF(r, g, b)) & mask;
	}
}

/* Decompress a 64-bit 4x4 pixel texture block compressed using the */
/* ETC2_PUNCHTROUGH format. */
bool detexDecompressBlockETC2_PUNCHTHROUGH(const uint8_t * DETEX_RESTRICT bitstring,
uint32_t mode_mask, uint32_t flags, uint8_t * DETEX_RESTRICT pixel_buffer) {
	int R = (bitstring[0] & 0xF8);
	R += complement3bitshifted(bitstring[0] & 7);
	int G = (bitstring[1] & 0xF8);
	G += complement3bitshifted(bitstring[1] & 7);
	int B = (bitstring[2] & 0xF8);
	B += complement3bitshifted(bitstring[2] & 7);
	int opaque = bitstring[3] & 2;
	if (opaque && (flags & DETEX_DECOMPRESS_FLAG_NON_OPAQUE_ONLY))
		return false;
	if (!opaque && (flags & DETEX_DECOMPRESS_FLAG_OPAQUE_ONLY))
		return false;
	if (R & 0xFF07) {
		// T mode.
		if ((mode_mask & DETEX_MODE_MASK_ETC_T) == 0)
			return false;
		if (opaque) {
			ProcessBlockETC2TOrHMode(bitstring, DETEX_MODE_MASK_ETC_T,
				pixel_buffer);
			return true;
		}
		// T mode with punchthrough alpha.
		ProcessBlockETC2PunchthroughTOrHMode(bitstring,
			DETEX_MODE_MASK_ETC_T, pixel_buffer);
		return true;
	}
	else
	if (G & 0xFF07) {
		// H mode.
		if ((mode_mask & DETEX_MODE_MASK_ETC_H) == 0)
			return false;
		if (opaque) {
			ProcessBlockETC2TOrHMode(bitstring, DETEX_MODE_MASK_ETC_H,
				pixel_buffer);
			return true;
		}
		// H mode with punchthrough alpha.
		ProcessBlockETC2PunchthroughTOrHMode(bitstring, DETEX_MODE_MASK_ETC_H,
			pixel_buffer);
		return true;
	}
	else
	if (B & 0xFF07) {
		// Planar mode.
		if ((mode_mask & DETEX_MODE_MASK_ETC_PLANAR) == 0)
			return false;
		// Opaque always set.
		if (flags & DETEX_DECOMPRESS_FLAG_NON_OPAQUE_ONLY)
			return false;
		ProcessBlockETC2PlanarMode(bitstring, pixel_buffer);
		return true;
	}
	else {
		// Differential mode.
		if (opaque)
			return detexDecompressBlockETC1(bitstring, mode_mask, flags,
				pixel_buffer);
		// Differential mode with punchthrough alpha.
		if ((mode_mask & DETEX_MODE_MASK_ETC_DIFFERENTIAL) == 0)
			return false;
		ProcessBlockETC2PunchthroughDifferentialMode(bitstring, pixel_buffer);
		return true;
	}
}


/* Return the internal mode of a ETC2_PUNCHTROUGH block. */
uint32_t detexGetModeETC2_PUNCHTHROUGH(const uint8_t *bitstring) {
	// Figure out the mode.
//	int opaque = bitstring[3] & 2;
	int R = (bitstring[0] & 0xF8);
	R += complement3bitshifted(bitstring[0] & 7);
	int G = (bitstring[1] & 0xF8);
	G += complement3bitshifted(bitstring[1] & 7);
	int B = (bitstring[2] & 0xF8);
	B += complement3bitshifted(bitstring[2] & 7);
	if (R & 0xFF07)
		// T mode.
		return 2;
	else if (G & 0xFF07)
		// H mode.
		return 3;
	else if (B & 0xFF07)
		// Planar mode.
		return 4;
	else
		// Differential mode.
		return 1;
}

void detexSetModeETC2_PUNCHTHROUGH(uint8_t *bitstring, uint32_t mode, uint32_t flags,
uint32_t *colors) {
	if (flags & DETEX_DECOMPRESS_FLAG_NON_OPAQUE_ONLY)
		bitstring[3] &= ~0x2;
	if (flags & DETEX_DECOMPRESS_FLAG_OPAQUE_ONLY)
		bitstring[3] |= 0x2;
	SetModeETC2THP(bitstring, flags);
}



================================================
FILE: decompress-rgtc.c
================================================
/*

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.

*/

#include "detex.h"

// For each pixel, decode an 8-bit integer and store as follows:
// If shift and offset are zero, store each value in consecutive 8 bit values in pixel_buffer.
// If shift is one, store each value in consecutive 16-bit words in pixel_buffer; if offset
// is zero, store it in the first 8 bits, if offset is one store it in the last 8 bits of each
// 16-bit word.
static DETEX_INLINE_ONLY void DecodeBlockRGTC(const uint8_t * DETEX_RESTRICT bitstring, int shift,
int offset, uint8_t * DETEX_RESTRICT pixel_buffer) {
	// LSBFirst byte order only.
	uint64_t bits = (*(uint64_t *)&bitstring[0]) >> 16;
	int lum0 = bitstring[0];
	int lum1 = bitstring[1];
	for (int i = 0; i < 16; i++) {
		int control_code = bits & 0x7;
		uint8_t output;
		if (lum0 > lum1)
			switch (control_code) {
			case 0 : output = lum0; break;
			case 1 : output = lum1; break;
			case 2 : output = detexDivide0To1791By7(6 * lum0 + lum1); break;
			case 3 : output = detexDivide0To1791By7(5 * lum0 + 2 * lum1); break;
			case 4 : output = detexDivide0To1791By7(4 * lum0 + 3 * lum1); break;
			case 5 : output = detexDivide0To1791By7(3 * lum0 + 4 * lum1); break;
			case 6 : output = detexDivide0To1791By7(2 * lum0 + 5 * lum1); break;
			case 7 : output = detexDivide0To1791By7(lum0 + 6 * lum1); break;
			}
		else
			switch (control_code) {
			case 0 : output = lum0; break;
			case 1 : output = lum1; break;
			case 2 : output = detexDivide0To1279By5(4 * lum0 + lum1); break;
			case 3 : output = detexDivide0To1279By5(3 * lum0 + 2 * lum1); break;
			case 4 : output = detexDivide0To1279By5(2 * lum0 + 3 * lum1); break;
			case 5 : output = detexDivide0To1279By5(lum0 + 4 * lum1); break;
			case 6 : output = 0; break;
			case 7 : output = 0xFF; break;
			}
		pixel_buffer[(i << shift) + offset] = output;
		bits >>= 3;
	}
}

/* Decompress a 64-bit 4x4 pixel texture block compressed using the */
/* unsigned RGTC1 (BC4) format. */
bool detexDecompressBlockRGTC1(const uint8_t * DETEX_RESTRICT bitstring, uint32_t mode_mask,
uint32_t flags, uint8_t * DETEX_RESTRICT pixel_buffer) {
	DecodeBlockRGTC(bitstring, 0, 0, pixel_buffer);
	return true;
}

/* Decompress a 128-bit 4x4 pixel texture block compressed using the */
/* unsigned RGTC2 (BC5) format. */
bool detexDecompressBlockRGTC2(const uint8_t * DETEX_RESTRICT bitstring, uint32_t mode_mask,
uint32_t flags, uint8_t * DETEX_RESTRICT pixel_buffer) {
	DecodeBlockRGTC(bitstring, 1, 0, pixel_buffer);
	DecodeBlockRGTC(&bitstring[8], 1, 1, pixel_buffer);
	return true;
}

// For each pixel, decode an 16-bit integer and store as follows:
// If shift and offset are zero, store each value in consecutive 16 bit values in pixel_buffer.
// If shift is one, store each value in consecutive 32-bit words in pixel_buffer; if offset
// is zero, store it in the first 16 bits, if offset is one store it in the last 16 bits of each
// 32-bit word. Returns true if the compressed block is valid.
static DETEX_INLINE_ONLY bool DecodeBlockSignedRGTC(const uint8_t * DETEX_RESTRICT bitstring, int shift,
int offset, uint8_t * DETEX_RESTRICT pixel_buffer) {
	// LSBFirst byte order only.
	uint64_t bits = (*(uint64_t *)&bitstring[0]) >> 16;
	int lum0 = (int8_t)bitstring[0];
	int lum1 = (int8_t)bitstring[1];
	if (lum0 == - 127 && lum1 == - 128)
		// Not allowed.
		return false;
	if (lum0 == - 128)
		lum0 = - 127;
	if (lum1 == - 128)
		lum1 = - 127;
	// Note: values are mapped to a red value of -127 to 127.
	uint16_t *pixel16_buffer = (uint16_t *)pixel_buffer;
	for (int i = 0; i < 16; i++) {
		int control_code = bits & 0x7;
		int32_t result;
		if (lum0 > lum1)
			switch (control_code) {
			case 0 : result = lum0; break;
			case 1 : result = lum1; break;
			case 2 : result = detexDivideMinus895To895By7(6 * lum0 + lum1); break;
			case 3 : result = detexDivideMinus895To895By7(5 * lum0 + 2 * lum1); break;
			case 4 : result = detexDivideMinus895To895By7(4 * lum0 + 3 * lum1); break;
			case 5 : result = detexDivideMinus895To895By7(3 * lum0 + 4 * lum1); break;
			case 6 : result = detexDivideMinus895To895By7(2 * lum0 + 5 * lum1); break;
			case 7 : result = detexDivideMinus895To895By7(lum0 + 6 * lum1); break;
			}
		else
			switch (control_code) {
			case 0 : result = lum0; break;
			case 1 : result = lum1; break;
			case 2 : result = detexDivideMinus639To639By5(4 * lum0 + lum1); break;
			case 3 : result = detexDivideMinus639To639By5(3 * lum0 + 2 * lum1); break;
			case 4 : result = detexDivideMinus639To639By5(2 * lum0 + 3 * lum1); break;
			case 5 : result = detexDivideMinus639To639By5(lum0 + 4 * lum1); break;
			case 6 : result = - 127; break;
			case 7 : result = 127; break;
			}
		// Map from [-127, 127] to [-32768, 32767].
		pixel16_buffer[(i << shift) + offset] = (uint16_t)(int16_t)
			((result + 127) * 65535 / 254 - 32768);
		bits >>= 3;
	}
	return true;
}

/* Decompress a 64-bit 4x4 pixel texture block compressed using the */
/* signed RGTC1 (signed BC4) format. */
bool detexDecompressBlockSIGNED_RGTC1(const uint8_t * DETEX_RESTRICT bitstring, uint32_t mode_mask,
uint32_t flags, uint8_t * DETEX_RESTRICT pixel_buffer) {
	return DecodeBlockSignedRGTC(bitstring, 0, 0, pixel_buffer);
}

/* Decompress a 128-bit 4x4 pixel texture block compressed u
gitextract_zmo0o45s/

├── .gitignore
├── LICENSE
├── Makefile
├── Makefile.conf
├── README
├── README.md
├── bits.c
├── bits.h
├── bptc-tables.c
├── bptc-tables.h
├── clamp.c
├── convert.c
├── dds.c
├── decompress-bc.c
├── decompress-bptc-float.c
├── decompress-bptc.c
├── decompress-eac.c
├── decompress-etc.c
├── decompress-rgtc.c
├── detex-convert.c
├── detex-png.h
├── detex-view.c
├── detex.h
├── division-tables.c
├── file-info.c
├── file-info.h
├── half-float.c
├── half-float.h
├── hdr.c
├── hdr.h
├── ktx.c
├── misc.c
├── misc.h
├── png.c
├── raw.c
├── test-texture-BC1.ktx
├── test-texture-BC1A.ktx
├── test-texture-BC2.ktx
├── test-texture-BC3.ktx
├── test-texture-BPTC.ktx
├── test-texture-BPTC_FLOAT.ktx
├── test-texture-EAC_R11.ktx
├── test-texture-EAC_RG11.ktx
├── test-texture-EAC_SIGNED_R11.ktx
├── test-texture-ETC1.ktx
├── test-texture-ETC2.ktx
├── test-texture-ETC2_EAC.ktx
├── test-texture-ETC2_PUNCHTHROUGH.ktx
├── test-texture-FLOAT_RGB16.ktx
├── test-texture-FLOAT_RGBA16.ktx
├── test-texture-RGB8.dds
├── test-texture-RGB8.ktx
├── test-texture-RGBA8.dds
├── test-texture-RGBA8.ktx
├── test-texture-RGTC1.ktx
├── test-texture-RGTC2.ktx
├── test-texture-SIGNED_RGTC1.ktx
├── test-texture-SIGNED_RGTC2.ktx
├── texture.c
└── validate.c
Condensed preview — 37 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (384K chars).
[
  {
    "path": ".gitignore",
    "chars": 289,
    "preview": "# Object files\n*.o\n*.ko\n*.obj\n*.elf\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Libraries\n*.lib\n*.a\n*.la\n*.lo\n\n# Shared object..."
  },
  {
    "path": "LICENSE",
    "chars": 754,
    "preview": "Copyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this software..."
  },
  {
    "path": "Makefile",
    "chars": 4574,
    "preview": "\n# Do not edit normally. Configuration settings are in Makefile.conf.\n\nTARGET_MACHINE := $(shell gcc -dumpmachine)\ninclu..."
  },
  {
    "path": "Makefile.conf",
    "chars": 1007,
    "preview": "# LIBRARY_CONFIGURATION determines whether a shared or static library will\n# be built. Supported values are SHARED, STAT..."
  },
  {
    "path": "README",
    "chars": 3407,
    "preview": "\n---- Introduction ----\n\nDetex is a low-level library that can be used to decompress textures and\ntexture blocks, as wel..."
  },
  {
    "path": "README.md",
    "chars": 677,
    "preview": "# detex\nLow-level library that can be used to decompress textures and texture blocks compressed using formats such as BC..."
  },
  {
    "path": "bits.c",
    "chars": 1519,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "bits.h",
    "chars": 2152,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "bptc-tables.c",
    "chars": 6838,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "bptc-tables.h",
    "chars": 1242,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "clamp.c",
    "chars": 4183,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "convert.c",
    "chars": 50729,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "dds.c",
    "chars": 11355,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "decompress-bc.c",
    "chars": 10081,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "decompress-bptc-float.c",
    "chars": 25018,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "decompress-bptc.c",
    "chars": 23911,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "decompress-eac.c",
    "chars": 10797,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "decompress-etc.c",
    "chars": 31763,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "decompress-rgtc.c",
    "chars": 6341,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "detex-convert.c",
    "chars": 11533,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "detex-png.h",
    "chars": 1189,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "detex-view.c",
    "chars": 6793,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "detex.h",
    "chars": 44266,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "division-tables.c",
    "chars": 19067,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "file-info.c",
    "chars": 17009,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "file-info.h",
    "chars": 1900,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "half-float.c",
    "chars": 13865,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "half-float.h",
    "chars": 1299,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "hdr.c",
    "chars": 7214,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "hdr.h",
    "chars": 884,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "ktx.c",
    "chars": 11989,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "misc.c",
    "chars": 4106,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "misc.h",
    "chars": 816,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "png.c",
    "chars": 6669,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "raw.c",
    "chars": 2593,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "texture.c",
    "chars": 5164,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  },
  {
    "path": "validate.c",
    "chars": 8194,
    "preview": "/*\n\nCopyright (c) 2015 Harm Hanemaaijer <fgenfb@yahoo.com>\n\nPermission to use, copy, modify, and/or distribute this soft..."
  }
]

About this extraction

This page contains the full source code of the hglm/detex GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 60 files (352.7 KB), approximately 126.1k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!