├── .gitignore ├── CHANGES ├── LICENSE ├── Makefile ├── README ├── README.md ├── Release └── texgenpack.exe ├── astc.c ├── bptc.c ├── calibrate.c ├── compare.c ├── compress.c ├── decode.h ├── dxtc.c ├── etc2.c ├── file.c ├── filelist.txt ├── gtk.c ├── half_float.c ├── image.c ├── mipmap.c ├── packing.h ├── rgtc.c ├── strcasecmp.h ├── texgenpack.c ├── texgenpack.h ├── texgenpack.sln ├── texgenpack.vcxproj ├── texgenpack.vcxproj.filters ├── texture.c ├── texview ├── texview.vcxproj └── texview.vcxproj.filters ├── viewer.c └── viewer.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | 34 | # Misc. 35 | .depend 36 | texgenpack 37 | texgenpack-gui 38 | texview/texview 39 | *.png 40 | *.jpg 41 | *.ktx 42 | *.dds 43 | *~ 44 | TODO 45 | RESULTS 46 | -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | 2 | Version 0.6.1 3 | 4 | - Update window less often in Windows version of viewer program to improve compression speed. 5 | - Support for 8-bit and 16-bit images and texture formats with one or two components. 6 | - Support for RGTC1/2 (BC4/BC5) unsigned and signed texture compression formats with one or two components. 7 | - Fix the block compare functions for 16-bit unsigned and signed components. 8 | - Allow conversion in viewer program between one and two-component 8-bit and 16-bit signed and unsigned image 9 | formats. 10 | - Optimize the bptc_float decode function so that bptc_float compression is 10% faster. 11 | - Optimize the bptc decode function so that bptc compression is 10% faster. 12 | - Support using half-float source image for RGB(A)8 compression formats, resulting in higher quality compression 13 | if the source image truly has more precision that a regular RGB(A)8 image (i.e. is not just a converted regular 14 | image). 15 | - Similarly, support using a 16-bit one or two component source image for 8-bit component compression formats such 16 | as unsigned RGTC1/2. 17 | - Add more conversion options to the viewer program between 8-bit and 16-bit formats and add an option to extend 18 | half-float images from one or two components to three. 19 | - Allow mipmaps to be generated even when there is already more than one mipmap present. 20 | - Support generation of mipmaps for 8-bit and 16-bit signed and unsigned formats, and for half-float formats. 21 | - Improve support for converting images to uncompressed textures. 22 | 23 | Version 0.6 24 | 25 | - Debug support for calibration of genetic algorithm parameters. 26 | - Fix difference mode for half-float images in viewer program. 27 | - Change --fast genetic algorithm parameters: population size 64 instead of 128, 200 generations instead of 100. 28 | - Implement mode statistics in verbose mode for ETC2 and BPTC_FLOAT. 29 | - Implement modal optimization (specific modes tied to islands) for BPTC_FLOAT compression, resulting in quality 30 | improvement. 31 | - Implement adaptive optimization, the GA is run twice as long when the RMSE of a block is above a certain threshold. 32 | - Add --hdr option for gamma corrected HDR half-float texture compression. Optimize half-float comparison functions 33 | using tables. 34 | - Change compare_images to use the same block comparison functions used during compression. 35 | - Fix bptc_signed_float (bc6h_sf16) decoding and compression. 36 | - Added menu items to remove or add alpha component in viewer program. 37 | - Fix R11_EAC and RG11_EAC compression and provide menu items to convert to r16 or rg16 format in viewer program. 38 | - Add HDR display settings dialog to viewer program. 39 | 40 | Version 0.5.1 41 | 42 | - Correct typo in glInternalFormat id for DXT1. 43 | - Fix major bug in writing DDS files (Caps field wasn't properly written). 44 | - Properly support uncompressed DDS files with a DX10 header. 45 | - Fix loading of uncompressed DDS textures. 46 | - Finally fix BPTC (BC7) compression and decompression. 47 | - Fix BPTC_FLOAT (BC6H) compression and decompression. 48 | 49 | Version 0.5 50 | 51 | - Make distinction between encoding and decoding in decoding functions. This can be relevant for behaviour being exactly 52 | according to specifications. 53 | - Fix bug in half-float conversion. 54 | - Port to Windows (Visual C++ or MinGW). 55 | - Fix byte order check bug in dxtc.c. 56 | - Fix alpha handling in BPTC compression. 57 | 58 | Version 0.4 59 | 60 | - Support for the high quality BPTC (BC7) 128-bit format. 61 | - Support some more PC formats in .ktx files. 62 | - Added a table of texture format information and use it in various places. This should allow more formats to be 63 | read and written. 64 | - Make the --decompress command more versatile by allowing decompression to uncompressed texture files with optional 65 | mipmaps instead of images only. 66 | - Split block comparison function into own source file. 67 | - Support half-float internal image format for compression and comparison (previously the only internal image format was 68 | 32-bit RGBA). 69 | - Work-in-progress on BPTC_FLOAT (BC6H). 70 | - Implement mipmap generation for power-of-two half-float textures. 71 | - Fix bug in --ultra mode when the texture width was not a multiple of the block size. 72 | - Add simple texture file viewer program "texview" using GTK+ 3. 73 | 74 | Version 0.3 75 | 76 | - Tweaked the GA parameters (mutation rate) for 128-bit block compression, improving quality. 77 | - Added DXT3 and DXT5 compression. 78 | - Added support for loading/saving multiple mipmap levels for DDS files. 79 | - Added manual optimizing of alpha values for texture types with a limited alpha range (DXT3 and ETC2 punchthrough). 80 | - During mipmap generation no longer blend alpha values in 1-bit (on/off) alpha images. 81 | - Add --generations and --islands options for custom speed settings. 82 | - When comparing pixels where both alpha components are zero, disregard the difference in RGB colors. This improves the 83 | quality of compressed textures with sharp alpha transitions. 84 | - Preliminary support for one channel (R11_EAC) and dual channel (RG11_EAC) texture formats and their signed variants. 85 | - Added --flip-vertical and --quiet options. 86 | - Split some source files. 87 | - Added support for sRGB ETC2 textures. They are treated as regular RGB textures by the compressor, except for mipmap 88 | generation. 89 | - Use uint64_t in for 64-bit unsigned integers. 90 | - Generalized block size (although it is currently 4x4 for all supported formats in the GA). 91 | - Reported RMSE and PSNR was slightly off due to calculation error (the number of pixels was taken as the extended 92 | dimensions, not the real dimensions), now fixed. 93 | - Support for decompression of half-float RGB and RGBA textures as well packed-pixel uncompressed textures from .ktx 94 | files. 95 | - Support for conversion (via "compression") to uncompressed packed-pixel RGB and RGBA and half-float RGB and RGBA and 96 | storing them in .ktx files. 97 | - Support for compressing a texture file that already contains mipmaps. 98 | - Fix bugs in .pkm file loading. 99 | - Preliminary support for ASTC file conversion via ARM's encoder via system shell commands to and from .ktx files and 100 | to and from ARM's atsc format. 101 | - Support for some uncompressed .dds formats. 102 | 103 | 104 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015 Harm Hanemaaijer 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | */ 18 | 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for texgenpack. 2 | 3 | # Target directory when installing. 4 | INSTALL_DIR = /usr/bin 5 | 6 | CFLAGS = -std=gnu99 -Ofast 7 | LFLAGS = -O 8 | #CFLAGS = -std=gnu99 -ggdb 9 | #LFLAGS = -ggdb 10 | PKG_CONFIG_CFLAGS = `pkg-config --cflags gtk+-3.0` 11 | PKG_CONFIG_LFLAGS = `pkg-config --libs gtk+-3.0` 12 | # For MinGW with GTK installed, uncomment the following line. 13 | #PNG_LIB_LOCATION = `pkg-config --libs gtk+-3.0` 14 | SHARED_MODULE_OBJECTS = image.o compress.o mipmap.o file.o texture.o etc2.o dxtc.o astc.o bptc.o half_float.o \ 15 | compare.o rgtc.o 16 | TEXGENPACK_MODULE_OBJECTS = texgenpack.o calibrate.o 17 | TEXVIEW_MODULE_OBJECTS = viewer.o gtk.o 18 | 19 | all : texgenpack texgenpack-gui 20 | 21 | texgenpack : $(TEXGENPACK_MODULE_OBJECTS) $(SHARED_MODULE_OBJECTS) 22 | $(CC) $(LFLAGS) $(TEXGENPACK_MODULE_OBJECTS) $(SHARED_MODULE_OBJECTS) -o texgenpack -lm -lpng -lfgen -lpthread $(PNG_LIB_LOCATION) 23 | 24 | texgenpack-gui : $(TEXVIEW_MODULE_OBJECTS) $(SHARED_MODULE_OBJECTS) 25 | $(CC) $(LFLAGS) $(TEXVIEW_MODULE_OBJECTS) $(SHARED_MODULE_OBJECTS) -o texgenpack-gui -lm -lpng -lfgen -lpthread $(PKG_CONFIG_LFLAGS) 26 | 27 | install : texgenpack texgenpack-gui 28 | install -m 0755 texgenpack $(INSTALL_DIR)/texgenpack 29 | install -m 0755 texgenpack-gui $(INSTALL_DIR)/texgenpack-gui 30 | 31 | clean : 32 | rm -f $(TEXGENPACK_MODULE_OBJECTS) $(TEXVIEW_MODULE_OBJECTS) $(SHARED_MODULE_OBJECTS) 33 | rm -f texgenpack 34 | rm -f texgenpack-gui 35 | 36 | gtk.o : gtk.c 37 | $(CC) -c $(CFLAGS) $(PKG_CONFIG_CFLAGS) gtk.c -o gtk.o 38 | 39 | file.o : file.c 40 | $(CC) -c $(CFLAGS) $(PKG_CONFIG_CFLAGS) file.c -o file.o 41 | 42 | .c.o : 43 | $(CC) -c $(CFLAGS) $< -o $@ 44 | 45 | .c.s : 46 | $(CC) -S $(CFLAGS) $< -o $@ 47 | 48 | dep: 49 | rm -f .depend 50 | make .depend 51 | 52 | .depend: 53 | echo '# Module dependencies' >>.depend 54 | $(CC) -MM $(patsubst %.o,%.c,$(TEXGENPACK_MODULE_OBJECTS)) >>.depend 55 | $(CC) -MM $(patsubst %.o,%.c,$(TEXVIEW_MODULE_OBJECTS)) >>.depend 56 | $(CC) -MM $(patsubst %.o,%.c,$(SHARED_MODULE_OBJECTS)) >>.depend 57 | 58 | include .depend 59 | 60 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 2 | texgenpack v0.9.6 -- texture compression using a genetic algorithm. 3 | 4 | **** Introduction **** 5 | 6 | This program is texture compression and conversion utility that uses a genetic 7 | algorithm to compress textures. It uses the GA implementation in libfgen to 8 | compress textures. Using a general genetic algorithm is a slow method, but is 9 | robust and inherently multi-threaded so it can use all available cores in the 10 | CPU. It also has the advantage that numerous formats can be supported, since 11 | only a decompression function has to be provided to support a new format. The 12 | program can also convert to and from several compressed and uncompressed 13 | texture formats and file types such as KTX and DDS, including support for 14 | multiple mipmaps and automatic mipmap generation. 15 | 16 | Although deleveloped on and mainly targeting Linux, there is also some (dated) 17 | support for Windows (for instructions, see the end of the file). 18 | 19 | **** Linux installation instructions **** 20 | 21 | On Linux, run "make" to compile which generated the executables texgenpack 22 | (command line) and texgenpack-gui (GUI texture viewer and compressor using GTK+) 23 | and optionally "sudo make install" to install to the default installation 24 | directory. Edit the Makefile to change the installation directory or compilation 25 | flags. 26 | 27 | Prerequisites for compilation: 28 | 29 | libpng (development version) 30 | GTK+ 3 development libraries (for GUI program) 31 | libfgen (https://github.com/hglm/libfgen.git) >= version 0.2.2 32 | 33 | For example, the complete compilation and installation process on a Debian- 34 | ased system might look like this: 35 | 36 | sudo apt-get install libpng12-dev 37 | sudo apt-get install libgtk-3-dev 38 | git clone https://github.com/hglm/libfgen.git 39 | cd libfgen 40 | make 41 | sudo make install 42 | cd .. 43 | # Clone texgenpack if not already done. 44 | git clone https://github.com/hglm/texgenpack.git 45 | cd texgenpack 46 | make 47 | sudo make install 48 | 49 | **** Features and description **** 50 | 51 | The following texture formats are supported: 52 | 53 | - ETC1, the mobile standard and compatible with the new standardized ETC2 54 | format in OpenGL 4.3 and OpenGL ES 3.0. 55 | - ETC2, an improved version of ETC1. RGB8 as well as RGBA8 and RGB8 with 56 | punchthrough alpha are supported. 57 | - Preliminary support for one or two component textures (stored as one or 58 | two 16-bit values in an image file): R11_EAC and RG11_EAC and their signed 59 | variants. These are part of the ETC2 family. 60 | - DXT1, DXT3, DXT5 and DXT1A (also known as BC1/2/3 or S3TC). 61 | - RGTC1/RGTC2 (BC4/BC5) for one and two component textures. 62 | - BPTC (BC7), a high-quality RGBA compression format. 63 | - BPTC_FLOAT and BPTC_SIGNED_FLOAT (BC6H_UF16 and BC6H_SF16) for both linear 64 | and HDR textures. 65 | - ASTC via ARM's encoder. 66 | 67 | The following file formats are supported: 68 | 69 | - KTX, an OpenGL standard, multiple mipmap levels supported with multitude 70 | of texture formats. 71 | - PKM, for ETC textures containing a single mipmap level. 72 | - DDS, for DXTC, RGTC, BPTC (BC1-7) or uncompressed textures, multiple mipmap 73 | levels supported. 74 | 75 | PNG is the only supported image format. You'll have to convert images to PNG 76 | before using the compress function (you can also use KTX or DDS files, either 77 | compressed or uncompressed as source). 78 | 79 | The program supports compression, decompression and comparison of image files 80 | and textures, reporting RMSE and PSNR statistics of the difference between 81 | images, and handles mipmaps. 82 | 83 | Because of the general nature of the method used, it is relatively easy to add 84 | new compression formats to the code; only a decompression function is required 85 | to enable compression of that format. 86 | 87 | Run the commmand line utility texgenpack without arguments for instructions. 88 | 89 | Basically, one command should be specified as the first argument, either 90 | --compress, --decompress or --compare. The last two arguments on the command 91 | line should be the source and destination files in that order. Various options 92 | are available. 93 | 94 | The, speed/quality level is set using --level , with in the 95 | range 0 to 50. The options --ultra, --fast (default), --medium and --slow 96 | correspond to quality level presets of 0, 8, 16 and 32, respectively. 97 | 98 | **** Formats **** 99 | 100 | Use --format to select the format for compression (the default when the 101 | destination is a KTX or PKM file is ETC1, for DDS it is DXT1). The --progress 102 | option will display a progress percentage as the algorithm runs. 103 | 104 | Examples: 105 | 106 | texgenpack --compress --format etc2_rgb8 --progress image.png texture.ktx 107 | texgenpack --compare image.png texture.ktx 108 | texgenpack --decompress texture.ktx decompressed.png 109 | 110 | Formats supported by the --format option include: 111 | 112 | - "etc1": ETC1, an RGB8 format. 64-bit per block (4x4 pixels). 113 | - "etc2_rgb8": The ETC2 RGB8 format that is backwards compatible with 114 | ETC1. 64-bit. 115 | - "etc2_eac": The ETC2 RGBA8 format with alpha values ranging from 0 116 | to 255. 128-bit blocks. 117 | - "etc2_punchthrough": RGB8 + alpha that is either on or off (0 or 118 | 255). 64-bit blocks. 119 | - "dxt1": RGB8, the most used standard on PCs. 120 | - "dxt3": RGBA8. 121 | - "dxt5": RGBA8. 122 | - "dxt1a": Variant of DXT1 with on/off alpha. When decompressing or 123 | comparing a DDS file containing a DXT1A texture the option --format 124 | dxt1a should be specified. 125 | - "r11_eac": A single 16-bit component. Stored in the lower 16-bits 126 | (R and G) with little endian byte order of a 32-bit pixel of a PNG 127 | image with alpha. 128 | - "rg11_eac": Two 16-bit components. Stored as two 16-bit values, 129 | in the lower (R and G) and upper (B and alpha) 16-bits of a PNG 130 | image with alpha. 131 | - "signed_r11_eac": A single signed 16-bit component. 132 | - "signed_rg11_eac": Two signed 16-bit components. 133 | - "rgb8": Uncompressed RGB8. 134 | - "rgba8": Uncompressed RGBA8. 135 | - "rgb_half_float": Uncompressed format where each color component is 136 | stored in a 16-bits floating point number. 137 | - "rgba_half_float": Includes alpha. 138 | - "bptc": High quality recent 128-bit compressed format, also known 139 | as BC7. Supports alpha. 140 | - "bptc_float": Half-float RGB format, also known as BC6H_UF16. --hdr 141 | option supported for HDR textures. 142 | - "bptc_signed_float": Signed half-float format, also known as 143 | BC6H_SF16. 144 | - "rgtc1": Single-channel (red) compression format. Equivalent to BC4. 145 | - "rgtc2": Two channel (red/green) compression format. Equivalent to 146 | BC5. 147 | - "signed_rgtc1": Signed version of RGTC1. 148 | - "signed_rgtc2 : Signed version of RGTC2. 149 | 150 | The program supports the use of higher precision source formats than the base 151 | format of the compression format. For example, it is possible to use half- 152 | float images or textures as a source for 8-bit RGB compression formats. 153 | Provided the source image does indeed have higher precision (and is not just 154 | an 8-bit image converted to half-float format), this should result in improved 155 | quality. The same applies to the use of 16-bit source formats for one or two 156 | component compression with RGTC. 157 | 158 | **** Quality level settings **** 159 | 160 | The first class of quality levels (0 to 7) only performs a single try at 161 | compressing a particular 4x4 pixel block. Eight blocks are compressed in 162 | parallel. Due the nature of the genetic algorithm, the compression quality 163 | can vary so this class has relatively high chance of blocks with a low 164 | compression quality being present. For levels 0 to 7, the number of 165 | generations of the genetic agorithm increases from 100 to 275. The --ultra 166 | quality preset corresponds to level 0. 167 | 168 | The second class of quality levels (8 to 32) performs multiple tries at 169 | compressing a particular 4x4 pixel block. The best result is used. The 170 | number of tries is equal to the quality level settings, so for levels 8 171 | to 32, the number of tries per block ranges from 8 to 32. A higher number 172 | of tries increases the quality of the resulting compressed texture. The 173 | --fast preset corresponds to level 8, --medium to level 16, and --slow 174 | to level 32. 175 | 176 | The third class of quality levels (33 to 50) uses 32 tries, like level 32 177 | (equal to --slow setting), but increases the number of generations of the 178 | genetic algorithm proportional to the quality level. For levels 33 to 50, 179 | the initial number of generations increases from 60 to 230 with a step size 180 | of 10. 181 | 182 | For compression levels in the range 8 to 50, an adaptive scheme has been 183 | implemented whereby the number of generations of the genetic algorithm is 184 | doubled or tripled when the compressed block is not of sufficient quality. 185 | 186 | These levels also utilize a perceptive quality strategy by default that 187 | incorporates differences between adjacent pixels and can result in better 188 | looking results, even when the absolute pixel component differences are 189 | a little greater than the standard method. The perceptive quality has only 190 | been implemented for RGB8 and RGBA8 texture formats. 191 | 192 | For all compression levels, a short second-pass is performed that uses 193 | a genetic algorithm with a small population size to fine-tune the end result. 194 | 195 | **** Graphical interface (texgenpack-gui) **** 196 | 197 | The graphical viewer and compression program texgenpack-gui uses GTK+ 3. It 198 | accepts zero, one or two image or texture filenames as arguments. 199 | 200 | The GUI program can be use to compress or convert images and textures. Amongst 201 | other features, it has options to adjust the number of components, convert 202 | components to to 8 and 16-bit unsigned or signed formats or to half-float 203 | format, generate mipmaps. Apart from texture compression, the program (just 204 | like the command line utility) can be used to convert uncompressed textures 205 | from one format to another. 206 | 207 | Advantages of the GUI program include immediate quality indication and 208 | information about the duration of the compression process. 209 | 210 | **** Miscelleanous remarks about texgenpack **** 211 | 212 | - The compression is fairly slow at the default setting (--fast). The --ultra 213 | preset is faster but sacrifices quality. The higher quality settings 214 | --medium and --slow produce better quality but are slow. 215 | - The code has not been tested on big-endian systems although they are 216 | supported. Loading of KTX files created on a big-endian system should work 217 | correctly. 218 | - The ETC1 and ETC2 RGB8 compression has been debugged, and seems work 219 | correctly. I'd like to hear about any incompabilities. 220 | - There is an option (--mipmaps) to automatically generate mipmap levels 221 | both for power-of-two and non-power-of-two textures, and save them in a 222 | KTX or DDS file. The quality of the results has not been extensively 223 | verified. 224 | - The program can be used to generate or extract mipmaps with or without 225 | compression or decompression. 226 | - Because of the nature of the algorithm, which only requires a decoding 227 | function of a particular compression format to perform compression, this 228 | program could help to evaluate or research texture compression schemes in 229 | general. 230 | 231 | Also included is the viewer and conversion program texgenpack-gui which can 232 | view, compare, convert and also compress all formats that texgenpack handles. 233 | It has zooming support, support for showing differences between textures/ 234 | images, and support for HDR textures. It requires GTK+ 3 or GTK+ 2 to build. 235 | 236 | **** Windows instructions **** 237 | 238 | The Windows port is out of date. The included binary does not support the 239 | --level option. 240 | 241 | The supplied 32-bit texgenpack.exe v0.6.1 (in the Release directory) was built 242 | with Visual C++ 2010 with static libary versions of libfgen and libpng 1.60. As 243 | such, the only requirements are the presence of the C run-time library 244 | msvcrt100.dll and pthreadVC2.dll from the pthread-win32 project. The latter 245 | file can be found on the pthread_win32 site project's pages at 246 | http://sourceware.org/pthreads-win32/. It is also included in the libfgen 247 | distribution. 248 | 249 | A Visual C++ 2010 project file is also provided. To build the texview program, 250 | a Windows GTK 2 or 3 installation is required and there are some details to 251 | consider. The GTK installation may provide header and .lib files for libpng. 252 | Because the older C run-time msvcrt.dll, linked with the "official" GTK 2 253 | Windows distribution, conflicts with the msvcrt100.dll found in Visual C++ 254 | 2010, use of that GTK distribution is not recommended unless you are able to 255 | compile libfgen and texview itself with an old version of Visual C++ so that 256 | only msvcrt.dll is used. 257 | 258 | Because the program uses 64-bit instructions extensively in some compression 259 | modes, compiling a 64-bit version should result in marked performance 260 | improvement on 64-bit systems. 261 | 262 | The author can be reached at fgenfb at yahoo.com. 263 | 264 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | texgenpack 2 | ========== 3 | 4 | Compress, decompress and convert texure files using a genetic algorithm. 5 | Supports KTX and DDS containers, and texture formats such as the DXT 6 | family, ETC1/2, BC6/BC7 etc. Contains graphical user interface (designed 7 | for Linux). Advantage is versatility and extendability, disadvantage is 8 | slowness compared to less general texture compressors. 9 | 10 | Primarily developed for Linux, it uses the libfgen genetic algorithm 11 | library. It also contains a Windows port (which requires some 12 | configuration to provide pthreads and GTK+ for GUI). 13 | 14 | The detex repository (https://github.com/hglm/detex.git) contains a 15 | stand-alone texture decompression library derived from texgenpack that 16 | also contains a viewer program. It superseeds texgenpack for this 17 | purpose as it is more complete and flexible, but does not contain 18 | texture compression functionality. It is the intention that a future 19 | version of texgenpack will make use of detex. 20 | 21 | See the file README for more detailed instructions. 22 | 23 | -------------------------------------------------------------------------------- /Release/texgenpack.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hglm/texgenpack/cf548ef583ca9592a55ea217b0ec43a2e25b9cbe/Release/texgenpack.exe -------------------------------------------------------------------------------- /astc.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015 Harm Hanemaaijer 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include "texgenpack.h" 25 | #include "packing.h" 26 | #include "decode.h" 27 | 28 | static char astc_block_size_table[14][2] = { 29 | { 4, 4 }, { 5, 4 }, { 5, 5 }, { 6, 5 }, { 6, 6 }, { 8, 5 }, { 8, 6 }, { 8, 8 }, { 10, 5 }, { 10, 6 }, 30 | { 10, 8 }, { 10, 10 }, { 12, 10 }, { 12, 12 } 31 | }; 32 | 33 | int match_astc_block_size(int w, int h) { 34 | int i; 35 | for (i = 0; i < 14; i++) 36 | if (w == astc_block_size_table[i][0] && h == astc_block_size_table[i][1]) 37 | break; 38 | if (i == 14) 39 | return - 1; 40 | return i; 41 | } 42 | 43 | int get_astc_block_size_width(int astc_block_type) { 44 | return astc_block_size_table[astc_block_type][0]; 45 | } 46 | 47 | int get_astc_block_size_height(int astc_block_type) { 48 | return astc_block_size_table[astc_block_type][1]; 49 | } 50 | 51 | int draw_block_rgba_astc(const unsigned char *bitstring, unsigned int *image_buffer, int flags) { 52 | printf("Error -- ASTC block decoding not implemented.\n"); 53 | exit(1); 54 | } 55 | 56 | void convert_astc_texture_to_image(Texture *texture, Image *image) { 57 | // Create temporary .astc filename. 58 | char *tmp_filename = tmpnam(NULL); 59 | char *astc_filename = (char *)alloca(strlen(tmp_filename) + 6); 60 | strcpy(astc_filename, tmp_filename); 61 | strcat(astc_filename, ".astc"); 62 | // Save the texture to the file. 63 | save_astc_file(texture, astc_filename); 64 | // Create temporary .ktx filename. 65 | tmp_filename = tmpnam(NULL); 66 | char *ktx_filename = (char *)alloca(strlen(tmp_filename) + 5); 67 | strcpy(ktx_filename, tmp_filename); 68 | strcat(ktx_filename, ".ktx"); 69 | // Execute decoding command. 70 | char *s = (char *)malloc(strlen(astc_filename) + strlen(ktx_filename) + 40); 71 | sprintf(s, "astcenc -d %s %s -silentmode", astc_filename, ktx_filename); 72 | printf("Executing command %s\n", s); 73 | int r = system(s); 74 | // Remove the temporary .astc filename, 75 | remove(astc_filename); 76 | if (r == - 1) { 77 | printf("Error executing command during ASTC decoding.\n"); 78 | exit(1); 79 | } 80 | free(s); 81 | // Load the .ktx file. 82 | load_image(ktx_filename, FILE_TYPE_KTX, image); 83 | // Remove the temporary .ktx filename. 84 | remove(ktx_filename); 85 | // astcenc flips the image during decoding to an uncompressed .ktx file; revert that. 86 | flip_image_vertical(image); 87 | } 88 | 89 | void decompress_astc_file(const char *filename, Image *image) { 90 | char *tmp_filename = tmpnam(NULL); 91 | char *ktx_filename = (char *)alloca(strlen(tmp_filename) + 5); 92 | strcpy(ktx_filename, tmp_filename); 93 | strcat(ktx_filename, ".ktx"); 94 | // Decode to an uncompressed .ktx file. 95 | char *s = (char *)malloc(strlen(filename) + strlen(ktx_filename) + 40); 96 | sprintf(s, "astcenc -d %s %s -silentmode", filename, ktx_filename); 97 | printf("Executing command %s\n", s); 98 | int r = system(s); 99 | if (r == - 1) { 100 | printf("Error executing command during ASTC decoding.\n"); 101 | exit(1); 102 | } 103 | free(s); 104 | load_image(ktx_filename, FILE_TYPE_KTX, image); 105 | remove(ktx_filename); 106 | // astcenc flip the image during decoding to an uncompressed .ktx file; revert that. 107 | flip_image_vertical(image); 108 | } 109 | 110 | void compress_image_to_astc_texture(Image *image, int texture_type, Texture *texture) { 111 | // Create temporary .png filename 112 | char *tmp_filename = tmpnam(NULL); 113 | char *png_filename = (char *)alloca(strlen(tmp_filename) + 5); 114 | strcpy(png_filename, tmp_filename); 115 | strcat(png_filename, ".png"); 116 | // Save the image to the file. 117 | save_png_file(image, png_filename); 118 | // Create temporary .astc filename 119 | tmp_filename = tmpnam(NULL); 120 | char *astc_filename = (char *)alloca(strlen(tmp_filename) + 6); 121 | strcpy(astc_filename, tmp_filename); 122 | strcat(astc_filename, ".astc"); 123 | // Execute encoding command. 124 | char *s = (char *)malloc(strlen(png_filename) + strlen(astc_filename) + 40); 125 | char *astcenc_speed_option; 126 | if (option_compression_level < SPEED_FAST) 127 | astcenc_speed_option = "-fast"; 128 | else if (option_compression_level < SPEED_MEDIUM) 129 | astcenc_speed_option = "-medium"; 130 | else if (option_compression_level < SPEED_SLOW) 131 | astcenc_speed_option = "-thorough"; 132 | else 133 | astcenc_speed_option = "-exhaustive"; 134 | TextureInfo *info = match_texture_type(texture_type); 135 | sprintf(s, "astcenc -c %s %s %dx%d %s -silentmode", png_filename, astc_filename, info->block_width, 136 | info->block_height, astcenc_speed_option); 137 | printf("Executing command %s\n", s); 138 | int r = system(s); 139 | remove(png_filename); 140 | if (r == - 1) { 141 | printf("Error executing command during ASTC encoding.\n"); 142 | exit(1); 143 | } 144 | free(s); 145 | // Load the created .astc texture. 146 | load_astc_file(astc_filename, texture); 147 | remove(astc_filename); 148 | } 149 | 150 | -------------------------------------------------------------------------------- /calibrate.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015 Harm Hanemaaijer 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "texgenpack.h" 26 | 27 | // Calibrate genetic algorithm parameters. 28 | 29 | static const int fixed_crossover_probability = 1; 30 | static const int real_valued_ga = 0; 31 | static const int deterministic = 0; 32 | 33 | typedef struct { 34 | Image *image; 35 | int texture_format; 36 | } FitUserData; 37 | 38 | static FitUserData fit_user_data; 39 | 40 | typedef struct { 41 | double cumulative_rmse; 42 | int count; 43 | } MutationBin; 44 | 45 | static MutationBin mutation_bin[100]; 46 | 47 | static void mutation_bin_initialize() { 48 | for (int i = 0; i < 100; i++) { 49 | mutation_bin[i].cumulative_rmse = 0; 50 | mutation_bin[i].count = 0; 51 | } 52 | } 53 | 54 | static void mutation_bin_add_measurement(double mutation_prob, double rmse) { 55 | int bin = floor(mutation_prob * 1000.0); 56 | if (bin >= 100) 57 | bin = 99; 58 | mutation_bin[bin].cumulative_rmse += rmse; 59 | mutation_bin[bin].count++; 60 | } 61 | 62 | static void mutation_bin_report() { 63 | printf("Binned results:\n"); 64 | for (int i = 0; i < 100; i++) { 65 | if (mutation_bin[i].count > 0) 66 | printf("Range %.4lf - %.4lf: n = %3d RMSE %.4lf\n", 67 | (double)i / 1000.0, ((double)i + 1) / 1000.0, mutation_bin[i].count, 68 | mutation_bin[i].cumulative_rmse / mutation_bin[i].count); 69 | } 70 | } 71 | 72 | static void compress_callback(BlockUserData *user_data) { 73 | } 74 | 75 | static double calibrate_calculate_error(const Ffit *fit, const double *param) { 76 | FitUserData *user_data = &fit_user_data; 77 | Texture texture; 78 | float crossover_prob = param[1]; 79 | if (fixed_crossover_probability) 80 | crossover_prob = 0.7; 81 | compress_image(user_data->image, user_data->texture_format, compress_callback, &texture, 1, 82 | param[0], crossover_prob); 83 | Image image2; 84 | convert_texture_to_image(&texture, &image2); 85 | double rmse = compare_images(user_data->image, &image2); 86 | destroy_image(&image2); 87 | destroy_texture(&texture); 88 | printf("Mut = %.4lf, Cross = %.3lf, RMSE = %.3lf\n", param[0], crossover_prob, rmse); 89 | fflush(stdout); 90 | mutation_bin_add_measurement(param[0], rmse); 91 | return rmse * rmse; 92 | } 93 | 94 | static void calibrate_generation_callback(Ffit *fit, int generation, const double *best_param, double best_error) { 95 | float crossover_prob = best_param[1]; 96 | if (fixed_crossover_probability) 97 | crossover_prob = 0.7; 98 | printf("Generation %d: Mut = %.4lf, Cross = %.3lf, RMSE = %.3lf\n", generation, 99 | best_param[0], crossover_prob, sqrt(best_error)); 100 | if (generation == 5) 101 | ffit_signal_stop(fit); 102 | if (!deterministic) 103 | ffit_signal_model_change(fit); 104 | } 105 | 106 | void calibrate_genetic_parameters(Image *image, int texture_type) { 107 | option_deterministic = deterministic; // Whether to initialize random function seed with timer. 108 | option_quiet = 1; 109 | mutation_bin_initialize(); 110 | #if 0 111 | Ffit *fit = ffit_create(2, calibrate_generation_callback, calibrate_calculate_error); 112 | ffit_set_parameter_range_and_mapping(fit, 0, 0, 0.1, FFIT_MAPPING_LINEAR); // Mutation rate. 113 | ffit_set_parameter_range_and_mapping(fit, 1, 0, 0.9, FFIT_MAPPING_LINEAR); // Crossover rate. 114 | fit_user_data.image = image; 115 | fit_user_data.texture_format = texture_type; 116 | if (real_valued_ga) 117 | ffit_run_fgen_real_valued(fit, 16, FGEN_ELITIST_SUS, 0.6, 0.4, 0.1); 118 | else 119 | ffit_run_fgen(fit, 16, 16, FGEN_ELITIST_SUS, fgen_crossover_uniform_per_element, 0.6, 0.05, 0.1); 120 | // ffit_run_fgen(fit, 16, 32, FGEN_ELITIST_SUS, fgen_crossover_uniform_per_element, 0.6, 0.025, 0.1); 121 | #else 122 | double mut = 0.005; 123 | for (; mut < 0.050; mut += 0.001) { 124 | int n = 2; 125 | if (mut > 0.008 && mut <= 0.035) 126 | n = 5; 127 | for (int i = 0; i < n; i++) { 128 | Texture texture; 129 | compress_image(image, texture_type, compress_callback, &texture, 1, mut, 0.7); 130 | Image image2; 131 | convert_texture_to_image(&texture, &image2); 132 | double rmse = compare_images(image, &image2); 133 | destroy_image(&image2); 134 | destroy_texture(&texture); 135 | printf("Mut = %.4lf, Cross = %.3lf, RMSE = %.3lf\n", mut, 0.7, rmse); 136 | fflush(stdout); 137 | mutation_bin_add_measurement(mut, rmse); 138 | } 139 | } 140 | #endif 141 | mutation_bin_report(); 142 | } 143 | 144 | -------------------------------------------------------------------------------- /decode.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015 Harm Hanemaaijer 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | */ 18 | 19 | 20 | // Flags values for the decoding functions. 21 | 22 | enum { 23 | ETC_MODE_ALLOWED_INDIVIDUAL = 0x1, 24 | ETC_MODE_ALLOWED_DIFFERENTIAL = 0x2, 25 | ETC2_MODE_ALLOWED_T = 0x4, 26 | ETC2_MODE_ALLOWED_H = 0x8, 27 | ETC2_MODE_ALLOWED_PLANAR = 0x10, 28 | ETC_MODE_ALLOWED_ALL = 0x3, 29 | ETC2_MODE_ALLOWED_ALL = 0x1F, 30 | ETC2_PUNCHTHROUGH_MODE_ALLOWED_ALL = 0x1E, 31 | BPTC_MODE_ALLOWED_ALL = 0xFF, 32 | MODES_ALLOWED_OPAQUE_ONLY = 0x100, 33 | MODES_ALLOWED_NON_OPAQUE_ONLY = 0x200, 34 | MODES_ALLOWED_PUNCHTHROUGH_ONLY = 0x400, 35 | BPTC_FLOAT_MODE_ALLOWED_ALL = 0x3FFF, 36 | ENCODE_BIT = 0x10000, 37 | TWO_COLORS = 0x20000, 38 | }; 39 | 40 | // Functions defined in etc2.c. 41 | 42 | // Draw (decompress) a 64-bit 4x4 pixel block. 43 | int draw_block4x4_etc1(const unsigned char *bitstring, unsigned int *image_buffer, int modes_allowed); 44 | int draw_block4x4_etc2_rgb8(const unsigned char *bitstring, unsigned int *image_buffer, int modes_allowed); 45 | int draw_block4x4_etc2_eac(const unsigned char *bitstring, unsigned int *image_buffer, int modes_allowed); 46 | int draw_block4x4_etc2_punchthrough(const unsigned char *bitstring, unsigned int *image_buffer, int modes_allowed); 47 | int draw_block4x4_r11_eac(const unsigned char *bitstring, unsigned int *image_buffer, int flags); 48 | int draw_block4x4_rg11_eac(const unsigned char *bitstring, unsigned int *image_buffer, int flags); 49 | int draw_block4x4_signed_r11_eac(const unsigned char *bitstring, unsigned int *image_buffer, int flags); 50 | int draw_block4x4_signed_rg11_eac(const unsigned char *bitstring, unsigned int *image_buffer, int flags); 51 | // Return ETC1 mode (0 or 1). 52 | int block4x4_etc1_get_mode(const unsigned char *bitstring); 53 | // Return ETC2 mode number from 0 to 4. 54 | int block4x4_etc2_rgb8_get_mode(const unsigned char *bitstring); 55 | int block4x4_etc2_eac_get_mode(const unsigned char *bitstring); 56 | int block4x4_etc2_punchthrough_get_mode(const unsigned char *bitstring); 57 | // Modify bitstring to conform to modes. 58 | void block4x4_etc1_set_mode(unsigned char *bitstring, int flags); 59 | void block4x4_etc2_rgb8_set_mode(unsigned char *bitstring, int flags); 60 | void block4x4_etc2_punchthrough_set_mode(unsigned char *bitstring, int flags); 61 | void block4x4_etc2_eac_set_mode(unsigned char *bitstring, int flags); 62 | // "Manual" optimization function. 63 | void optimize_block_alpha_etc2_punchthrough(unsigned char *bitstring, unsigned char *alpha_values); 64 | void optimize_block_alpha_etc2_eac(unsigned char *bitstring, unsigned char *alpha_values, int flags); 65 | 66 | // Functions defined in dxtc.c. 67 | 68 | int draw_block4x4_dxt1(const unsigned char *bitstring, unsigned int *image_buffer, int flags); 69 | int draw_block4x4_dxt1a(const unsigned char *bitstring, unsigned int *image_buffer, int flags); 70 | int draw_block4x4_dxt3(const unsigned char *bitstring, unsigned int *image_buffer, int flags); 71 | int draw_block4x4_dxt5(const unsigned char *bitstring, unsigned int *image_buffer, int flags); 72 | void optimize_block_alpha_dxt3(unsigned char *bitstring, unsigned char *alpha_values); 73 | 74 | // Functions defined in astc.c 75 | 76 | int draw_block_rgba_astc(const unsigned char *bitstring, unsigned int *image_buffer, int flags); 77 | void convert_astc_texture_to_image(Texture *texture, Image *image); 78 | void decompress_astc_file(const char *filename, Image *image); 79 | void compress_image_to_astc_texture(Image *image, int texture_type, Texture *texture); 80 | int match_astc_block_size(int w, int h); 81 | int get_astc_block_size_width(int astc_block_type); 82 | int get_astc_block_size_height(int astc_block_type); 83 | 84 | // Functions defined in bptc.c 85 | 86 | int draw_block4x4_bptc(const unsigned char *bitstring, unsigned int *image_buffer, int flags); 87 | int block4x4_bptc_get_mode(const unsigned char *bitstring); 88 | int draw_block4x4_bptc_float(const unsigned char *bitstring, unsigned int *image_buffer, int flags); 89 | int draw_block4x4_bptc_signed_float(const unsigned char *bitstring, unsigned int *image_buffer, int flags); 90 | int block4x4_bptc_float_get_mode(const unsigned char *bitstring); 91 | void block4x4_bptc_set_mode(unsigned char *bitstring, int flags); 92 | void block4x4_bptc_float_set_mode(unsigned char *bitstring, int flags); 93 | // Try to preinitialize colors for particular modes. 94 | void bptc_set_block_colors(unsigned char *bitstring, int flags, unsigned int *colors); 95 | 96 | // Functions defined in rgtc.c 97 | 98 | int draw_block4x4_rgtc1(const unsigned char *bitstring, unsigned int *image_buffer, int flags); 99 | int draw_block4x4_signed_rgtc1(const unsigned char *bitstring, unsigned int *image_buffer, int flags); 100 | int draw_block4x4_rgtc2(const unsigned char *bitstring, unsigned int *image_buffer, int flags); 101 | int draw_block4x4_signed_rgtc2(const unsigned char *bitstring, unsigned int *image_buffer, int flags); 102 | 103 | // Function defined in texture.c. 104 | 105 | int draw_block4x4_uncompressed(const unsigned char *bitstring, unsigned int *image_buffer, int flags); 106 | int draw_block4x4_argb8(const unsigned char *bitstring, unsigned int *image_buffer, int flags); 107 | int draw_block4x4_uncompressed_rgb_half_float(const unsigned char *bitstring, unsigned int *image_buffer, int flags); 108 | int draw_block4x4_uncompressed_rgba_half_float(const unsigned char *bitstring, unsigned int *image_buffer, int flags); 109 | int draw_block4x4_uncompressed_r_half_float(const unsigned char *bitstring, unsigned int *image_buffer, int flags); 110 | int draw_block4x4_uncompressed_rg_half_float(const unsigned char *bitstring, unsigned int *image_buffer, int flags); 111 | 112 | -------------------------------------------------------------------------------- /dxtc.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015 Harm Hanemaaijer 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include "texgenpack.h" 23 | #include "decode.h" 24 | #include "packing.h" 25 | 26 | // Draw a 4x4 pixel block using the DXT1 texture compression data in bitstring. 27 | 28 | int draw_block4x4_dxt1(const unsigned char *bitstring, unsigned int *image_buffer, int flags) { 29 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ || !defined(__BYTE_ORDER__) 30 | unsigned int colors = *(unsigned int *)&bitstring[0]; 31 | #else 32 | unsigned int colors = ((unsigned int)bitstring[0] << 24) | ((unsigned int)bitstring[1] << 16) | 33 | ((unsigned int)bitstring[2] << 8) | bitstring[3]; 34 | #endif 35 | // Decode the two 5-6-5 RGB colors. 36 | int color_r[4], color_g[4], color_b[4]; 37 | color_b[0] = (colors & 0x0000001F) << 3; 38 | color_g[0] = (colors & 0x000007E0) >> (5 - 2); 39 | color_r[0] = (colors & 0x0000F800) >> (11 - 3); 40 | color_b[1] = (colors & 0x001F0000) >> (16 - 3); 41 | color_g[1] = (colors & 0x07E00000) >> (21 - 2); 42 | color_r[1] = (colors & 0xF8000000) >> (27 - 3); 43 | if ((colors & 0xFFFF) > ((colors & 0xFFFF0000) >> 16)) { 44 | color_r[2] = (2 * color_r[0] + color_r[1]) / 3; 45 | color_g[2] = (2 * color_g[0] + color_g[1]) / 3; 46 | color_b[2] = (2 * color_b[0] + color_b[1]) / 3; 47 | color_r[3] = (color_r[0] + 2 * color_r[1]) / 3; 48 | color_g[3] = (color_g[0] + 2 * color_g[1]) / 3; 49 | color_b[3] = (color_b[0] + 2 * color_b[1]) / 3; 50 | } 51 | else { 52 | color_r[2] = (color_r[0] + color_r[1]) / 2; 53 | color_g[2] = (color_g[0] + color_g[1]) / 2; 54 | color_b[2] = (color_b[0] + color_b[1]) / 2; 55 | color_r[3] = color_g[3] = color_b[3] = 0; 56 | } 57 | unsigned int pixels = *(unsigned int *)&bitstring[4]; 58 | for (int i = 0; i < 16; i++) { 59 | int pixel = (pixels >> (i * 2)) & 0x3; 60 | image_buffer[i] = pack_rgb_alpha_0xff(color_r[pixel], color_g[pixel], color_b[pixel]); 61 | } 62 | return 1; 63 | } 64 | 65 | // Draw a 4x4 pixel block using the DXT1A texture compression data in bitstring. 66 | 67 | int draw_block4x4_dxt1a(const unsigned char *bitstring, unsigned int *image_buffer, int flags) { 68 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ || !defined(__BYTE_ORDER__) 69 | unsigned int colors = *(unsigned int *)&bitstring[0]; 70 | #else 71 | unsigned int colors = ((unsigned int)bitstring[0] << 24) | ((unsigned int)bitstring[1] << 16) | 72 | ((unsigned int)bitstring[2] << 8) | bitstring[3]; 73 | #endif 74 | bool opaque = ((colors & 0xFFFF) > ((colors & 0xFFFF0000) >> 16)); 75 | if (opaque && (flags & MODES_ALLOWED_NON_OPAQUE_ONLY)) 76 | return 0; 77 | if (!opaque && (flags & MODES_ALLOWED_OPAQUE_ONLY)) 78 | return 0; 79 | // Decode the two 5-6-5 RGB colors. 80 | int color_r[4], color_g[4], color_b[4], color_a[4]; 81 | color_b[0] = (colors & 0x0000001F) << 3; 82 | color_g[0] = (colors & 0x000007E0) >> (5 - 2); 83 | color_r[0] = (colors & 0x0000F800) >> (11 - 3); 84 | color_b[1] = (colors & 0x001F0000) >> (16 - 3); 85 | color_g[1] = (colors & 0x07E00000) >> (21 - 2); 86 | color_r[1] = (colors & 0xF8000000) >> (27 - 3); 87 | color_a[0] = color_a[1] = color_a[2] = color_a[3] = 0xFF; 88 | if (opaque) { 89 | color_r[2] = (2 * color_r[0] + color_r[1]) / 3; 90 | color_g[2] = (2 * color_g[0] + color_g[1]) / 3; 91 | color_b[2] = (2 * color_b[0] + color_b[1]) / 3; 92 | color_r[3] = (color_r[0] + 2 * color_r[1]) / 3; 93 | color_g[3] = (color_g[0] + 2 * color_g[1]) / 3; 94 | color_b[3] = (color_b[0] + 2 * color_b[1]) / 3; 95 | } 96 | else { 97 | color_r[2] = (color_r[0] + color_r[1]) / 2; 98 | color_g[2] = (color_g[0] + color_g[1]) / 2; 99 | color_b[2] = (color_b[0] + color_b[1]) / 2; 100 | color_r[3] = color_g[3] = color_b[3] = color_a[3] = 0; 101 | } 102 | unsigned int pixels = *(unsigned int *)&bitstring[4]; 103 | for (int i = 0; i < 16; i++) { 104 | int pixel = (pixels >> (i * 2)) & 0x3; 105 | image_buffer[i] = pack_rgba(color_r[pixel], color_g[pixel], color_b[pixel], color_a[pixel]); 106 | } 107 | return 1; 108 | } 109 | 110 | // Draw a 4x4 pixel block using the DXT3 texture compression data in bitstring. 111 | 112 | int draw_block4x4_dxt3(const unsigned char *bitstring, unsigned int *image_buffer, int flags) { 113 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ || !defined(__BYTE_ORDER__) 114 | unsigned int colors = *(unsigned int *)&bitstring[8]; 115 | #else 116 | unsigned int colors = ((unsigned int)bitstring[8] << 24) | ((unsigned int)bitstring[9] << 16) | 117 | ((unsigned int)bitstring[10] << 8) | bitstring[11]; 118 | #endif 119 | if ((colors & 0xFFFF) <= ((colors & 0xFFFF0000) >> 16) && (flags & ENCODE_BIT)) 120 | // GeForce 6 and 7 series produce wrong result in this case. 121 | return 0; 122 | int color_r[4], color_g[4], color_b[4]; 123 | color_b[0] = (colors & 0x0000001F) << 3; 124 | color_g[0] = (colors & 0x000007E0) >> (5 - 2); 125 | color_r[0] = (colors & 0x0000F800) >> (11 - 3); 126 | color_b[1] = (colors & 0x001F0000) >> (16 - 3); 127 | color_g[1] = (colors & 0x07E00000) >> (21 - 2); 128 | color_r[1] = (colors & 0xF8000000) >> (27 - 3); 129 | color_r[2] = (2 * color_r[0] + color_r[1]) / 3; 130 | color_g[2] = (2 * color_g[0] + color_g[1]) / 3; 131 | color_b[2] = (2 * color_b[0] + color_b[1]) / 3; 132 | color_r[3] = (color_r[0] + 2 * color_r[1]) / 3; 133 | color_g[3] = (color_g[0] + 2 * color_g[1]) / 3; 134 | color_b[3] = (color_b[0] + 2 * color_b[1]) / 3; 135 | unsigned int pixels = *(unsigned int *)&bitstring[12]; 136 | uint64_t alpha_pixels = *(uint64_t *)&bitstring[0]; 137 | for (int i = 0; i < 16; i++) { 138 | int pixel = (pixels >> (i * 2)) & 0x3; 139 | int alpha = ((alpha_pixels >> (i * 4)) & 0xF) * 255 / 15; 140 | image_buffer[i] = pack_rgba(color_r[pixel], color_g[pixel], color_b[pixel], alpha); 141 | } 142 | return 1; 143 | } 144 | 145 | // Draw a 4x4 pixel block using the DXT5 texture compression data in bitstring. 146 | 147 | int draw_block4x4_dxt5(const unsigned char *bitstring, unsigned int *image_buffer, int flags) { 148 | int alpha0 = bitstring[0]; 149 | int alpha1 = bitstring[1]; 150 | if (alpha0 > alpha1 && (flags & MODES_ALLOWED_OPAQUE_ONLY)) 151 | return 0; 152 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ || !defined(__BYTE_ORDER__) 153 | unsigned int colors = *(unsigned int *)&bitstring[8]; 154 | #else 155 | unsigned int colors = ((unsigned int)bitstring[8] << 24) | ((unsigned int)bitstring[9] << 16) | 156 | ((unsigned int)bitstring[10] << 8) | bitstring[11]; 157 | #endif 158 | if ((colors & 0xFFFF) <= ((colors & 0xFFFF0000) >> 16) && (flags & ENCODE_BIT)) 159 | // GeForce 6 and 7 series produce wrong result in this case. 160 | return 0; 161 | int color_r[4], color_g[4], color_b[4]; 162 | color_b[0] = (colors & 0x0000001F) << 3; 163 | color_g[0] = (colors & 0x000007E0) >> (5 - 2); 164 | color_r[0] = (colors & 0x0000F800) >> (11 - 3); 165 | color_b[1] = (colors & 0x001F0000) >> (16 - 3); 166 | color_g[1] = (colors & 0x07E00000) >> (21 - 2); 167 | color_r[1] = (colors & 0xF8000000) >> (27 - 3); 168 | color_r[2] = (2 * color_r[0] + color_r[1]) / 3; 169 | color_g[2] = (2 * color_g[0] + color_g[1]) / 3; 170 | color_b[2] = (2 * color_b[0] + color_b[1]) / 3; 171 | color_r[3] = (color_r[0] + 2 * color_r[1]) / 3; 172 | color_g[3] = (color_g[0] + 2 * color_g[1]) / 3; 173 | color_b[3] = (color_b[0] + 2 * color_b[1]) / 3; 174 | unsigned int pixels = *(unsigned int *)&bitstring[12]; 175 | uint64_t alpha_bits = (unsigned int)bitstring[2] | ((unsigned int)bitstring[3] << 8) | 176 | ((uint64_t)*(unsigned int *)&bitstring[4] << 16); 177 | for (int i = 0; i < 16; i++) { 178 | int pixel = (pixels >> (i * 2)) & 0x3; 179 | int code = (alpha_bits >> (i * 3)) & 0x7; 180 | int alpha; 181 | if (alpha0 > alpha1) 182 | switch (code) { 183 | case 0 : alpha = alpha0; break; 184 | case 1 : alpha = alpha1; break; 185 | case 2 : alpha = (6 * alpha0 + 1 * alpha1) / 7; break; 186 | case 3 : alpha = (5 * alpha0 + 2 * alpha1) / 7; break; 187 | case 4 : alpha = (4 * alpha0 + 3 * alpha1) / 7; break; 188 | case 5 : alpha = (3 * alpha0 + 4 * alpha1) / 7; break; 189 | case 6 : alpha = (2 * alpha0 + 5 * alpha1) / 7; break; 190 | case 7 : alpha = (1 * alpha0 + 6 * alpha1) / 7; break; 191 | } 192 | else 193 | switch (code) { 194 | case 0 : alpha = alpha0; break; 195 | case 1 : alpha = alpha1; break; 196 | case 2 : alpha = (4 * alpha0 + 1 * alpha1) / 5; break; 197 | case 3 : alpha = (3 * alpha0 + 2 * alpha1) / 5; break; 198 | case 4 : alpha = (2 * alpha0 + 3 * alpha1) / 5; break; 199 | case 5 : alpha = (1 * alpha0 + 4 * alpha1) / 5; break; 200 | case 6 : alpha = 0; break; 201 | case 7 : alpha = 0xFF; break; 202 | } 203 | image_buffer[i] = pack_rgba(color_r[pixel], color_g[pixel], color_b[pixel], alpha); 204 | } 205 | return 1; 206 | } 207 | 208 | // Manual optimization function for the alpha components of DXT3. 209 | 210 | void optimize_block_alpha_dxt3(unsigned char *bitstring, unsigned char *alpha_values) { 211 | uint64_t alpha_pixels = 0; 212 | for (int i = 0; i < 16; i++) { 213 | // Scale and round the alpha value to the values allowed by DXT3. 214 | unsigned int alpha_pixel = ((unsigned int)alpha_values[i] * 15 + 8) / 255; 215 | alpha_pixels |= (uint64_t)alpha_pixel << (i * 4); 216 | } 217 | *(uint64_t *)&bitstring[0] = alpha_pixels; 218 | } 219 | 220 | 221 | -------------------------------------------------------------------------------- /filelist.txt: -------------------------------------------------------------------------------- 1 | texgenpack/astc.c 2 | texgenpack/bptc.c 3 | texgenpack/calibrate.c 4 | texgenpack/compare.c 5 | texgenpack/compress.c 6 | texgenpack/CHANGES 7 | texgenpack/COPYING 8 | texgenpack/COPYING.LESSER 9 | texgenpack/decode.h 10 | texgenpack/dxtc.c 11 | texgenpack/etc2.c 12 | texgenpack/file.c 13 | texgenpack/filelist.txt 14 | texgenpack/gtk.c 15 | texgenpack/half_float.c 16 | texgenpack/image.c 17 | texgenpack/Makefile 18 | texgenpack/mipmap.c 19 | texgenpack/packing.h 20 | texgenpack/README 21 | texgenpack/rgtc.c 22 | texgenpack/strcasecmp.h 23 | texgenpack/texgenpack.c 24 | texgenpack/texgenpack.h 25 | texgenpack/texture.c 26 | texgenpack/viewer.c 27 | texgenpack/viewer.h 28 | texgenpack/texgenpack.sln 29 | texgenpack/texgenpack.vcxproj 30 | texgenpack/texgenpack.vcxproj.filters 31 | texgenpack/texview/texview.vcxproj 32 | texgenpack/texview/texview.vcxproj.filters 33 | texgenpack/Release/texgenpack.exe 34 | 35 | -------------------------------------------------------------------------------- /half_float.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * 3 | * Filename: ieeehalfprecision.c 4 | * Programmer: James Tursa 5 | * Version: 1.0 6 | * Date: March 3, 2009 7 | * Copyright: (c) 2009 by James Tursa, All Rights Reserved 8 | * 9 | * This code uses the BSD License: 10 | * 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are 13 | * met: 14 | * 15 | * * Redistributions of source code must retain the above copyright 16 | * notice, this list of conditions and the following disclaimer. 17 | * * Redistributions in binary form must reproduce the above copyright 18 | * notice, this list of conditions and the following disclaimer in 19 | * the documentation and/or other materials provided with the distribution 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | * 33 | * This file contains C code to convert between IEEE double, single, and half 34 | * precision floating point formats. The intended use is for standalone C code 35 | * that does not rely on MATLAB mex.h. The bit pattern for the half precision 36 | * floating point format is stored in a 16-bit unsigned int variable. The half 37 | * precision bit pattern definition is: 38 | * 39 | * 1 bit sign bit 40 | * 5 bits exponent, biased by 15 41 | * 10 bits mantissa, hidden leading bit, normalized to 1.0 42 | * 43 | * Special floating point bit patterns recognized and supported: 44 | * 45 | * All exponent bits zero: 46 | * - If all mantissa bits are zero, then number is zero (possibly signed) 47 | * - Otherwise, number is a denormalized bit pattern 48 | * 49 | * All exponent bits set to 1: 50 | * - If all mantissa bits are zero, then number is +Infinity or -Infinity 51 | * - Otherwise, number is NaN (Not a Number) 52 | * 53 | * For the denormalized cases, note that 2^(-24) is the smallest number that can 54 | * be represented in half precision exactly. 2^(-25) will convert to 2^(-24) 55 | * because of the rounding algorithm used, and 2^(-26) is too small and underflows 56 | * to zero. 57 | * 58 | ********************************************************************************/ 59 | 60 | #include 61 | #include 62 | //----------------------------------------------------------------------------- 63 | // 64 | // Routine: singles2halfp 65 | // 66 | // Input: source = Address of 32-bit floating point data to convert 67 | // numel = Number of values at that address to convert 68 | // 69 | // Output: target = Address of 16-bit data to hold output (numel values) 70 | // return value = 0 if native floating point format is IEEE 71 | // = 1 if native floating point format is not IEEE 72 | // 73 | // Programmer: James Tursa 74 | // 75 | //----------------------------------------------------------------------------- 76 | 77 | int singles2halfp(void *target, void *source, int numel) 78 | { 79 | uint16_t *hp = (uint16_t *) target; // Type pun output as an unsigned 16-bit int 80 | uint32_t *xp = (uint32_t *) source; // Type pun input as an unsigned 32-bit int 81 | uint16_t hs, he, hm; 82 | uint32_t x, xs, xe, xm; 83 | int hes; 84 | static int next; // Little Endian adjustment 85 | static int checkieee = 1; // Flag to check for IEEE754, Endian, and word size 86 | double one = 1.0; // Used for checking IEEE754 floating point format 87 | uint32_t *ip; // Used for checking IEEE754 floating point format 88 | 89 | if( checkieee ) { // 1st call, so check for IEEE754, Endian, and word size 90 | ip = (uint32_t *) &one; 91 | if( *ip ) { // If Big Endian, then no adjustment 92 | next = 0; 93 | } else { // If Little Endian, then adjustment will be necessary 94 | next = 1; 95 | ip++; 96 | } 97 | if( *ip != 0x3FF00000u ) { // Check for exact IEEE 754 bit pattern of 1.0 98 | return 1; // Floating point bit pattern is not IEEE 754 99 | } 100 | if( sizeof(int16_t) != 2 || sizeof(int32_t) != 4 ) { 101 | return 1; // short is not 16-bits, or long is not 32-bits. 102 | } 103 | checkieee = 0; // Everything checks out OK 104 | } 105 | 106 | if( source == NULL || target == NULL ) { // Nothing to convert (e.g., imag part of pure real) 107 | return 0; 108 | } 109 | 110 | while( numel-- ) { 111 | x = *xp++; 112 | if( (x & 0x7FFFFFFFu) == 0 ) { // Signed zero 113 | *hp++ = (uint16_t) (x >> 16); // Return the signed zero 114 | } else { // Not zero 115 | xs = x & 0x80000000u; // Pick off sign bit 116 | xe = x & 0x7F800000u; // Pick off exponent bits 117 | xm = x & 0x007FFFFFu; // Pick off mantissa bits 118 | if( xe == 0 ) { // Denormal will underflow, return a signed zero 119 | *hp++ = (uint16_t) (xs >> 16); 120 | } else if( xe == 0x7F800000u ) { // Inf or NaN (all the exponent bits are set) 121 | if( xm == 0 ) { // If mantissa is zero ... 122 | *hp++ = (uint16_t) ((xs >> 16) | 0x7C00u); // Signed Inf 123 | } else { 124 | *hp++ = (uint16_t) 0xFE00u; // NaN, only 1st mantissa bit set 125 | } 126 | } else { // Normalized number 127 | hs = (uint16_t) (xs >> 16); // Sign bit 128 | hes = ((int)(xe >> 23)) - 127 + 15; // Exponent unbias the single, then bias the halfp 129 | if( hes >= 0x1F ) { // Overflow 130 | *hp++ = (uint16_t) ((xs >> 16) | 0x7C00u); // Signed Inf 131 | } else if( hes <= 0 ) { // Underflow 132 | if( (14 - hes) > 24 ) { // Mantissa shifted all the way off & no rounding possibility 133 | hm = (uint16_t) 0u; // Set mantissa to zero 134 | } else { 135 | xm |= 0x00800000u; // Add the hidden leading bit 136 | hm = (uint16_t) (xm >> (14 - hes)); // Mantissa 137 | if( (xm >> (13 - hes)) & 0x00000001u ) // Check for rounding 138 | hm += (uint16_t) 1u; // Round, might overflow into exp bit, but this is OK 139 | } 140 | *hp++ = (hs | hm); // Combine sign bit and mantissa bits, biased exponent is zero 141 | } else { 142 | he = (uint16_t) (hes << 10); // Exponent 143 | hm = (uint16_t) (xm >> 13); // Mantissa 144 | if( xm & 0x00001000u ) // Check for rounding 145 | *hp++ = (hs | he | hm) + (uint16_t) 1u; // Round, might overflow to inf, this is OK 146 | else 147 | *hp++ = (hs | he | hm); // No rounding 148 | } 149 | } 150 | } 151 | } 152 | return 0; 153 | } 154 | 155 | //----------------------------------------------------------------------------- 156 | // 157 | // Routine: halfp2singles 158 | // 159 | // Input: source = address of 16-bit data to convert 160 | // numel = Number of values at that address to convert 161 | // 162 | // Output: target = Address of 32-bit floating point data to hold output (numel values) 163 | // return value = 0 if native floating point format is IEEE 164 | // = 1 if native floating point format is not IEEE 165 | // 166 | // Programmer: James Tursa 167 | // 168 | //----------------------------------------------------------------------------- 169 | 170 | int halfp2singles(void *target, void *source, int numel) 171 | { 172 | uint16_t *hp = (uint16_t *) source; // Type pun input as an unsigned 16-bit int 173 | uint32_t *xp = (uint32_t *) target; // Type pun output as an unsigned 32-bit int 174 | uint16_t h, hs, he, hm; 175 | uint32_t xs, xe, xm; 176 | int32_t xes; 177 | int e; 178 | #if 0 179 | static int next; // Little Endian adjustment 180 | static int checkieee = 1; // Flag to check for IEEE754, Endian, and word size 181 | double one = 1.0; // Used for checking IEEE754 floating point format 182 | uint32_t *ip; // Used for checking IEEE754 floating point format 183 | 184 | if( checkieee ) { // 1st call, so check for IEEE754, Endian, and word size 185 | ip = (uint32_t *) &one; 186 | if( *ip ) { // If Big Endian, then no adjustment 187 | next = 0; 188 | } else { // If Little Endian, then adjustment will be necessary 189 | next = 1; 190 | ip++; 191 | } 192 | if( *ip != 0x3FF00000u ) { // Check for exact IEEE 754 bit pattern of 1.0 193 | return 1; // Floating point bit pattern is not IEEE 754 194 | } 195 | if( sizeof(int16_t) != 2 || sizeof(int32_t) != 4 ) { 196 | return 1; // short is not 16-bits, or long is not 32-bits. 197 | } 198 | checkieee = 0; // Everything checks out OK 199 | } 200 | #endif 201 | 202 | 203 | if( source == NULL || target == NULL ) // Nothing to convert (e.g., imag part of pure real) 204 | return 0; 205 | 206 | while( numel-- ) { 207 | h = *hp++; 208 | if( (h & 0x7FFFu) == 0 ) { // Signed zero 209 | *xp++ = ((uint32_t) h) << 16; // Return the signed zero 210 | } else { // Not zero 211 | hs = h & 0x8000u; // Pick off sign bit 212 | he = h & 0x7C00u; // Pick off exponent bits 213 | hm = h & 0x03FFu; // Pick off mantissa bits 214 | if( he == 0 ) { // Denormal will convert to normalized 215 | e = -1; // The following loop figures out how much extra to adjust the exponent 216 | do { 217 | e++; 218 | hm <<= 1; 219 | } while( (hm & 0x0400u) == 0 ); // Shift until leading bit overflows into exponent bit 220 | xs = ((uint32_t) hs) << 16; // Sign bit 221 | xes = ((int32_t) (he >> 10)) - 15 + 127 - e; // Exponent unbias the halfp, then bias the single 222 | xe = (uint32_t) (xes << 23); // Exponent 223 | xm = ((uint32_t) (hm & 0x03FFu)) << 13; // Mantissa 224 | *xp++ = (xs | xe | xm); // Combine sign bit, exponent bits, and mantissa bits 225 | } else if( he == 0x7C00u ) { // Inf or NaN (all the exponent bits are set) 226 | if( hm == 0 ) { // If mantissa is zero ... 227 | *xp++ = (((uint32_t) hs) << 16) | ((uint32_t) 0x7F800000u); // Signed Inf 228 | } else { 229 | *xp++ = (uint32_t) 0xFFC00000u; // NaN, only 1st mantissa bit set 230 | } 231 | } else { // Normalized number 232 | xs = ((uint32_t) hs) << 16; // Sign bit 233 | xes = ((int32_t) (he >> 10)) - 15 + 127; // Exponent unbias the halfp, then bias the single 234 | xe = (uint32_t) (xes << 23); // Exponent 235 | xm = ((uint32_t) hm) << 13; // Mantissa 236 | *xp++ = (xs | xe | xm); // Combine sign bit, exponent bits, and mantissa bits 237 | } 238 | } 239 | } 240 | return 0; 241 | } 242 | 243 | -------------------------------------------------------------------------------- /mipmap.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015 Harm Hanemaaijer 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include "texgenpack.h" 23 | #include "packing.h" 24 | 25 | // Use the averaging method to create an RGB(A)8 mipmap level directly from the original size image. source_image 26 | // must have dimensions that are a power of two. divider must be a power of two. 27 | 28 | static void create_mipmap_with_averaging(Image *source_image, int divider, Image *dest_image) { 29 | int n = divider * divider; 30 | for (int y = 0; y < source_image->height; y += divider) { 31 | int dy = y / divider; 32 | for (int x = 0; x < source_image->width; x += divider) { 33 | int dx = x / divider; 34 | // Calculate the average values of the pixel block. 35 | int r = 0; 36 | int g = 0; 37 | int b = 0; 38 | int a = 0; 39 | for (int by = y; by < y + divider; by++) 40 | for (int bx = x; bx < x + divider; bx++) { 41 | unsigned int pixel = source_image->pixels[by * source_image->extended_width + bx]; 42 | r += pixel_get_r(pixel); 43 | g += pixel_get_g(pixel); 44 | b += pixel_get_b(pixel); 45 | a += pixel_get_a(pixel); 46 | } 47 | r /= n; 48 | g /= n; 49 | b /= n; 50 | a /= n; 51 | if (source_image->alpha_bits == 0) 52 | a = 0xFF; 53 | else 54 | if (source_image->alpha_bits == 1) 55 | // Avoid smoothing out 1-bit alpha textures. If there are less than n / 2 56 | // alpha pixels in the source image with alpha 0xFF, then alpha becomes 57 | // zero, otherwise 0xFF. 58 | if (a >= 0x80) 59 | a = 0xFF; 60 | else 61 | a = 0; 62 | unsigned int pixel = pack_rgba(r, g, b, a); 63 | dest_image->pixels[dy * dest_image->extended_width + dx] = pixel; 64 | } 65 | } 66 | } 67 | 68 | // Use the averaging method to create an RGB(A)8 mipmap level from the previous level image (halving the 69 | // dimension) for power-of-two textures. 70 | 71 | static void create_mipmap_with_averaging_divider_2(Image *source_image, Image *dest_image) { 72 | for (int y = 0; y < source_image->height; y += 2) { 73 | int dy = y / 2; 74 | for (int x = 0; x < source_image->width; x += 2) { 75 | int dx = x / 2; 76 | // Calculate the average values of the pixel block. 77 | int r = 0; 78 | int g = 0; 79 | int b = 0; 80 | int a = 0; 81 | unsigned int pixel = source_image->pixels[y * source_image->extended_width + x]; 82 | r += pixel_get_r(pixel); 83 | g += pixel_get_g(pixel); 84 | b += pixel_get_b(pixel); 85 | a += pixel_get_a(pixel); 86 | pixel = source_image->pixels[y * source_image->extended_width + x + 1]; 87 | r += pixel_get_r(pixel); 88 | g += pixel_get_g(pixel); 89 | b += pixel_get_b(pixel); 90 | a += pixel_get_a(pixel); 91 | pixel = source_image->pixels[(y + 1) * source_image->extended_width + x]; 92 | r += pixel_get_r(pixel); 93 | g += pixel_get_g(pixel); 94 | b += pixel_get_b(pixel); 95 | a += pixel_get_a(pixel); 96 | pixel = source_image->pixels[(y + 1) * source_image->extended_width + x + 1]; 97 | r += pixel_get_r(pixel); 98 | g += pixel_get_g(pixel); 99 | b += pixel_get_b(pixel); 100 | a += pixel_get_a(pixel); 101 | r /= 4; 102 | g /= 4; 103 | b /= 4; 104 | a /= 4; 105 | if (source_image->alpha_bits == 0) 106 | a = 0xFF; 107 | else 108 | if (source_image->alpha_bits == 1) 109 | // Avoid smoothing out 1-bit alpha textures. If there are less than two 110 | // alpha pixels in the source image with alpha 0xFF, then alpha becomes 111 | // zero, otherwise 0xFF. 112 | if (a >= 0x80) 113 | a = 0xFF; 114 | else 115 | a = 0; 116 | pixel = pack_rgba(r, g, b, a); 117 | dest_image->pixels[dy * dest_image->extended_width + dx] = pixel; 118 | } 119 | } 120 | } 121 | 122 | // Use the averaging method to create a half-float mipmap level from the previous level image (halving the 123 | // dimension) for power-of-two textures. 124 | 125 | static void create_half_float_mipmap_with_averaging_divider_2(Image *source_image, Image *dest_image) { 126 | for (int y = 0; y < source_image->height; y += 2) { 127 | int dy = y / 2; 128 | for (int x = 0; x < source_image->width; x += 2) { 129 | int dx = x / 2; 130 | // Calculate the average values of the pixel block. 131 | float c[4]; 132 | c[0] = c[1] = c[2] = c[3] = 0; 133 | uint64_t pixel = *(uint64_t *)&source_image->pixels[(y * source_image->extended_width + x) * 2]; 134 | uint16_t h[4]; 135 | h[0] = pixel64_get_r16(pixel); 136 | h[1] = pixel64_get_g16(pixel); 137 | h[2] = pixel64_get_b16(pixel); 138 | h[3] = pixel64_get_a16(pixel); 139 | float f[4]; 140 | halfp2singles(&f[0], &h[0], 4); 141 | c[0] += f[0]; 142 | c[1] += f[1]; 143 | c[2] += f[2]; 144 | c[3] += f[3]; 145 | pixel = *(uint64_t *)&source_image->pixels[(y * source_image->extended_width + x + 1) * 2]; 146 | h[0] = pixel64_get_r16(pixel); 147 | h[1] = pixel64_get_g16(pixel); 148 | h[2] = pixel64_get_b16(pixel); 149 | h[3] = pixel64_get_a16(pixel); 150 | halfp2singles(&f[0], &h[0], 4); 151 | c[0] += f[0]; 152 | c[1] += f[1]; 153 | c[2] += f[2]; 154 | c[3] += f[3]; 155 | pixel = *(uint64_t *)&source_image->pixels[((y + 1) * source_image->extended_width + x) * 2]; 156 | h[0] = pixel64_get_r16(pixel); 157 | h[1] = pixel64_get_g16(pixel); 158 | h[2] = pixel64_get_b16(pixel); 159 | h[3] = pixel64_get_a16(pixel); 160 | halfp2singles(&f[0], &h[0], 4); 161 | c[0] += f[0]; 162 | c[1] += f[1]; 163 | c[2] += f[2]; 164 | c[3] += f[3]; 165 | pixel = *(uint64_t *)&source_image->pixels[((y + 1) * source_image->extended_width + x + 1) * 2]; 166 | h[0] = pixel64_get_r16(pixel); 167 | h[1] = pixel64_get_g16(pixel); 168 | h[2] = pixel64_get_b16(pixel); 169 | h[3] = pixel64_get_a16(pixel); 170 | halfp2singles(&f[0], &h[0], 4); 171 | c[0] += f[0]; 172 | c[1] += f[1]; 173 | c[2] += f[2]; 174 | c[3] += f[3]; 175 | c[0] /= 4.0; 176 | c[1] /= 4.0; 177 | c[2] /= 4.0; 178 | c[3] /= 4.0; 179 | if (source_image->alpha_bits == 0) { 180 | c[3] = 1.0; 181 | } 182 | else 183 | if (source_image->alpha_bits == 1) 184 | // Avoid smoothing out 1-bit alpha textures. If there are less than two 185 | // alpha pixels in the source image with alpha 0xFF, then alpha becomes 186 | // zero, otherwise 0xFF. 187 | if (c[3] >= 0.5) 188 | c[3] = 1.0; 189 | else 190 | c[3] = 0; 191 | singles2halfp(&h[0], &c[0], 4); 192 | pixel = pack_rgba16(h[0], h[1], h[2], h[3]); 193 | *(uint64_t *)&dest_image->pixels[(dy * dest_image->extended_width + dx) * 2] = pixel; 194 | } 195 | } 196 | } 197 | 198 | // Use the averaging method to create a 16-bit mipmap level from the previous level image (halving the 199 | // dimension) for power-of-two textures. 200 | 201 | static void create_16_bit_mipmap_with_averaging_divider_2(Image *source_image, Image *dest_image) { 202 | for (int y = 0; y < source_image->height; y += 2) { 203 | int dy = y / 2; 204 | for (int x = 0; x < source_image->width; x += 2) { 205 | int dx = x / 2; 206 | // Calculate the average values of the pixel block. 207 | int r = 0; 208 | int g = 0; 209 | unsigned int pixel = source_image->pixels[y * source_image->extended_width + x]; 210 | r += pixel_get_r16(pixel); 211 | g += pixel_get_g16(pixel); 212 | pixel = source_image->pixels[y * source_image->extended_width + x + 1]; 213 | r += pixel_get_r16(pixel); 214 | g += pixel_get_g16(pixel); 215 | pixel = source_image->pixels[(y + 1) * source_image->extended_width + x]; 216 | r += pixel_get_r16(pixel); 217 | g += pixel_get_g16(pixel); 218 | pixel = source_image->pixels[(y + 1) * source_image->extended_width + x + 1]; 219 | r += pixel_get_r16(pixel); 220 | g += pixel_get_g16(pixel); 221 | r /= 4; 222 | g /= 4; 223 | pixel = pack_r16(r) | pack_g16(g); 224 | dest_image->pixels[dy * dest_image->extended_width + dx] = pixel; 225 | } 226 | } 227 | } 228 | 229 | // Use the averaging method to create a signed 16-bit mipmap level from the previous level image (halving the 230 | // dimension) for power-of-two textures. 231 | 232 | static void create_signed_16_bit_mipmap_with_averaging_divider_2(Image *source_image, Image *dest_image) { 233 | for (int y = 0; y < source_image->height; y += 2) { 234 | int dy = y / 2; 235 | for (int x = 0; x < source_image->width; x += 2) { 236 | int dx = x / 2; 237 | // Calculate the average values of the pixel block. 238 | int r = 0; 239 | int g = 0; 240 | unsigned int pixel = source_image->pixels[y * source_image->extended_width + x]; 241 | r += pixel_get_signed_r16(pixel); 242 | g += pixel_get_signed_g16(pixel); 243 | pixel = source_image->pixels[y * source_image->extended_width + x + 1]; 244 | r += pixel_get_signed_r16(pixel); 245 | g += pixel_get_signed_g16(pixel); 246 | pixel = source_image->pixels[(y + 1) * source_image->extended_width + x]; 247 | r += pixel_get_signed_r16(pixel); 248 | g += pixel_get_signed_g16(pixel); 249 | pixel = source_image->pixels[(y + 1) * source_image->extended_width + x + 1]; 250 | r += pixel_get_signed_r16(pixel); 251 | g += pixel_get_signed_g16(pixel); 252 | r /= 4; 253 | g /= 4; 254 | pixel = pack_r16((uint16_t)(int16_t)r) | pack_g16((uint16_t)(int16_t)g); 255 | dest_image->pixels[dy * dest_image->extended_width + dx] = pixel; 256 | } 257 | } 258 | } 259 | 260 | // Use the averaging method to create a signed 8-bit mipmap level from the previous level image (halving the 261 | // dimension) for power-of-two textures. 262 | 263 | static void create_signed_8_bit_mipmap_with_averaging_divider_2(Image *source_image, Image *dest_image) { 264 | for (int y = 0; y < source_image->height; y += 2) { 265 | int dy = y / 2; 266 | for (int x = 0; x < source_image->width; x += 2) { 267 | int dx = x / 2; 268 | // Calculate the average values of the pixel block. 269 | int r = 0; 270 | int g = 0; 271 | unsigned int pixel = source_image->pixels[y * source_image->extended_width + x]; 272 | r += pixel_get_signed_r8(pixel); 273 | g += pixel_get_signed_g8(pixel); 274 | pixel = source_image->pixels[y * source_image->extended_width + x + 1]; 275 | r += pixel_get_signed_r8(pixel); 276 | g += pixel_get_signed_g8(pixel); 277 | pixel = source_image->pixels[(y + 1) * source_image->extended_width + x]; 278 | r += pixel_get_signed_r8(pixel); 279 | g += pixel_get_signed_g8(pixel); 280 | pixel = source_image->pixels[(y + 1) * source_image->extended_width + x + 1]; 281 | r += pixel_get_signed_r8(pixel); 282 | g += pixel_get_signed_g8(pixel); 283 | r /= 4; 284 | g /= 4; 285 | pixel = pack_r((uint8_t)(int8_t)r) | pack_g((uint8_t)(int8_t)g); 286 | dest_image->pixels[dy * dest_image->extended_width + dx] = pixel; 287 | } 288 | } 289 | } 290 | 291 | // Helper function to calculate the polyphase weight. 292 | 293 | uint32_t calculate_polyphase_weight(int dx, int dy, int j, int i, int w, int h, int source_width, int source_height) { 294 | uint32_t weighty; 295 | if (source_height & 1) 296 | // Rounding down. 297 | switch (i) { 298 | case 0 : weighty = 65536 * (h - dy) / (2 * h + 1); break; 299 | case 1 : weighty = 65536 * h / (2 * h + 1); break; 300 | case 2 : weighty = 65536 * (1 + dy) / (2 * h + 1); break; 301 | } 302 | else 303 | // No rounding. 304 | if (i == 2) 305 | return 0; 306 | else 307 | weighty = 65536 / 2; 308 | uint32_t weightx; 309 | if (source_width & 1) 310 | // Rounding down. 311 | switch (j) { 312 | case 0 : weightx = 65536 * (w - dx) / (2 * w + 1); break; 313 | case 1 : weightx = 65536 * w / (2 * w + 1); break; 314 | case 2 : weightx = 65536 * (1 + dx) / (2 * w + 1); break; 315 | } 316 | else 317 | // No rounding. 318 | if (j == 2) 319 | return 0; 320 | else 321 | weightx = 65536 / 2; 322 | return ((uint64_t)weightx * (uint64_t)weighty) >> 16; 323 | } 324 | 325 | // Use the polyphase method to create an RGB(A)8 mipmap level from the previous level image (halving the 326 | // dimension, rounding down if necessary). Works for non-power-of-two textures by rounding down. 327 | 328 | static void create_mipmap_polyphase(Image *source_image, Image *dest_image) { 329 | int h = dest_image->height; 330 | int w = dest_image->width; 331 | for (int dy = 0; dy < h; dy++) 332 | for (int dx = 0; dx < w; dx++) { 333 | // Calculate the average values of the pixel block. 334 | uint32_t r = 0; 335 | uint32_t g = 0; 336 | uint32_t b = 0; 337 | uint32_t a = 0; 338 | for (int i = 0; i < 3; i++) 339 | for (int j = 0; j < 3; j++) { 340 | uint32_t weight = calculate_polyphase_weight(dx, dy, j, i, w, h, 341 | source_image->width, source_image->height); 342 | // Add the pixel(2 * dx + j, 2 * dy + i) from the source image with weight. 343 | if (weight > 0) { 344 | uint32_t pixel = source_image->pixels[(2 * dy + i) * 345 | source_image->extended_width + 2 * dx + j]; 346 | r += weight * pixel_get_r(pixel); 347 | g += weight * pixel_get_g(pixel); 348 | b += weight * pixel_get_b(pixel); 349 | a += weight * pixel_get_a(pixel); 350 | } 351 | } 352 | // Bits 16-23 of r, g, b and a hold the integer value, bits 0-15 is the fraction. 353 | // Round up if bit 15 is set. 354 | r = (r >> 16) + ((r & 0x8000) >> 15); 355 | g = (g >> 16) + ((g & 0x8000) >> 15); 356 | b = (b >> 16) + ((b & 0x8000) >> 15); 357 | a = (a >> 16) + ((a & 0x8000) >> 15); 358 | if (source_image->alpha_bits == 0) 359 | a = 0xFF; 360 | else 361 | if (source_image->alpha_bits == 1) 362 | // Avoid smoothing out 1-bit alpha textures. 363 | if (a >= 0x80) 364 | a = 0xFF; 365 | else 366 | a = 0; 367 | unsigned int pixel = pack_rgba(r, g, b, a); 368 | dest_image->pixels[dy * dest_image->extended_width + dx] = pixel; 369 | } 370 | } 371 | 372 | // Use the polyphase method to create a half-float mipmap level from the previous level image (halving the 373 | // dimension, rounding down if necessary). Works for non-power-of-two textures by rounding down. 374 | 375 | static void create_half_float_mipmap_polyphase(Image *source_image, Image *dest_image) { 376 | int h = dest_image->height; 377 | int w = dest_image->width; 378 | for (int dy = 0; dy < h; dy++) 379 | for (int dx = 0; dx < w; dx++) { 380 | // Calculate the average values of the pixel block. 381 | float c[4]; 382 | c[0] = c[1] = c[2] = c[3] = 0; 383 | for (int i = 0; i < 3; i++) 384 | for (int j = 0; j < 3; j++) { 385 | uint32_t weight = calculate_polyphase_weight(dx, dy, j, i, w, h, 386 | source_image->width, source_image->height); 387 | // Add the pixel(2 * dx + j, 2 * dy + i) from the source image with weight. 388 | if (weight > 0) { 389 | uint64_t pixel = *(uint64_t *)&source_image->pixels[((2 * dy + i) * 390 | source_image->extended_width + 2 * dx + j) * 2]; 391 | uint16_t h[4]; 392 | h[0] = pixel64_get_r16(pixel); 393 | h[1] = pixel64_get_g16(pixel); 394 | h[2] = pixel64_get_b16(pixel); 395 | h[3] = pixel64_get_a16(pixel); 396 | float f[4]; 397 | halfp2singles(&f[0], &h[0], 4); 398 | c[0] += (float)weight / 65536.0 * f[0]; 399 | c[1] += (float)weight / 65536.0 * f[1]; 400 | c[2] += (float)weight / 65536.0 * f[2]; 401 | c[3] += (float)weight / 65536.0 * f[3]; 402 | } 403 | } 404 | if (source_image->alpha_bits == 0) { 405 | c[3] = 1.0; 406 | } 407 | else 408 | if (source_image->alpha_bits == 1) 409 | // Avoid smoothing out 1-bit alpha textures. If there are less than two 410 | // alpha pixels in the source image with alpha 0xFF, then alpha becomes 411 | // zero, otherwise 0xFF. 412 | if (c[3] >= 0.5) 413 | c[3] = 1.0; 414 | else 415 | c[3] = 0; 416 | uint16_t h[4]; 417 | singles2halfp(&h[0], &c[0], 4); 418 | uint64_t pixel = pack_rgba16(h[0], h[1], h[2], h[3]); 419 | *(uint64_t *)&dest_image->pixels[(dy * dest_image->extended_width + dx) * 2] = pixel; 420 | } 421 | } 422 | 423 | // Use the polyphase method to create a 16-bit mipmap level from the previous level image (halving the 424 | // dimension, rounding down if necessary). Works for non-power-of-two textures by rounding down. 425 | 426 | static void create_16_bit_mipmap_polyphase(Image *source_image, Image *dest_image) { 427 | int h = dest_image->height; 428 | int w = dest_image->width; 429 | for (int dy = 0; dy < h; dy++) 430 | for (int dx = 0; dx < w; dx++) { 431 | // Calculate the average values of the pixel block. 432 | uint32_t r = 0; 433 | uint32_t g = 0; 434 | for (int i = 0; i < 3; i++) 435 | for (int j = 0; j < 3; j++) { 436 | uint32_t weight = calculate_polyphase_weight(dx, dy, j, i, w, h, 437 | source_image->width, source_image->height); 438 | // Add the pixel(2 * dx + j, 2 * dy + i) from the source image with weight. 439 | if (weight > 0) { 440 | uint32_t pixel = source_image->pixels[(2 * dy + i) * 441 | source_image->extended_width + 2 * dx + j]; 442 | r += weight * pixel_get_r16(pixel); 443 | g += weight * pixel_get_g16(pixel); 444 | } 445 | } 446 | // Bits 16-31 of r, g, b and a hold the integer value, bits 0-15 is the fraction. 447 | // Round up if bit 15 is set. 448 | r = (r >> 16) + ((r & 0x8000) >> 15); 449 | g = (g >> 16) + ((g & 0x8000) >> 15); 450 | uint32_t pixel = pack_r16(r) | pack_g16(g); 451 | dest_image->pixels[dy * dest_image->extended_width + dx] = pixel; 452 | } 453 | } 454 | 455 | // Use the polyphase method to create a signed 16-bit mipmap level from the previous level image (halving the 456 | // dimension, rounding down if necessary). Works for non-power-of-two textures by rounding down. 457 | 458 | static void create_signed_16_bit_mipmap_polyphase(Image *source_image, Image *dest_image) { 459 | int h = dest_image->height; 460 | int w = dest_image->width; 461 | for (int dy = 0; dy < h; dy++) 462 | for (int dx = 0; dx < w; dx++) { 463 | // Calculate the average values of the pixel block. 464 | int32_t r = 0; 465 | int32_t g = 0; 466 | for (int i = 0; i < 3; i++) 467 | for (int j = 0; j < 3; j++) { 468 | uint32_t weight = calculate_polyphase_weight(dx, dy, j, i, w, h, 469 | source_image->width, source_image->height); 470 | // Add the pixel(2 * dx + j, 2 * dy + i) from the source image with weight. 471 | if (weight > 0) { 472 | uint32_t pixel = source_image->pixels[(2 * dy + i) * 473 | source_image->extended_width + 2 * dx + j]; 474 | // weight is in the range [0, 65536]. 475 | // pixel components are the in range [-32768, 32767]. 476 | // So no overflow should occur. 477 | r += (int32_t)weight * pixel_get_signed_r16(pixel); 478 | g += (int32_t)weight * pixel_get_signed_g16(pixel); 479 | } 480 | } 481 | // Bits 16-31 of r, g, b and a hold the integer value, bits 0-15 is the fraction. 482 | // Round up if bit 15 is set. 483 | r = (r >> 16) + ((r & 0x8000) >> 15); 484 | g = (g >> 16) + ((g & 0x8000) >> 15); 485 | uint32_t pixel = pack_r16((uint16_t)(int16_t)r) | pack_g16((uint16_t)(int16_t)g); 486 | dest_image->pixels[dy * dest_image->extended_width + dx] = pixel; 487 | } 488 | } 489 | 490 | // Use the polyphase method to create a signed 8-bit mipmap level from the previous level image (halving the 491 | // dimension, rounding down if necessary). Works for non-power-of-two textures by rounding down. 492 | 493 | static void create_signed_8_bit_mipmap_polyphase(Image *source_image, Image *dest_image) { 494 | int h = dest_image->height; 495 | int w = dest_image->width; 496 | for (int dy = 0; dy < h; dy++) 497 | for (int dx = 0; dx < w; dx++) { 498 | // Calculate the average values of the pixel block. 499 | int32_t r = 0; 500 | int32_t g = 0; 501 | for (int i = 0; i < 3; i++) 502 | for (int j = 0; j < 3; j++) { 503 | uint32_t weight = calculate_polyphase_weight(dx, dy, j, i, w, h, 504 | source_image->width, source_image->height); 505 | // Add the pixel(2 * dx + j, 2 * dy + i) from the source image with weight. 506 | if (weight > 0) { 507 | uint32_t pixel = source_image->pixels[(2 * dy + i) * 508 | source_image->extended_width + 2 * dx + j]; 509 | // weight is in the range [0, 65536]. 510 | // pixel components are the in range [-128, 127]. 511 | // So no overflow should occur. 512 | r += (int32_t)weight * pixel_get_signed_r8(pixel); 513 | g += (int32_t)weight * pixel_get_signed_g8(pixel); 514 | } 515 | } 516 | // Bits 16-23 of r and g hold the integer value, bits 0-15 is the fraction. 517 | // Round up if bit 15 is set. 518 | r = (r >> 16) + ((r & 0x8000) >> 15); 519 | g = (g >> 16) + ((g & 0x8000) >> 15); 520 | uint32_t pixel = pack_r((uint8_t)(int8_t)r) | pack_g((uint8_t)(int8_t)g); 521 | dest_image->pixels[dy * dest_image->extended_width + dx] = pixel; 522 | } 523 | } 524 | 525 | // Generate a scaled down mipmap image according to the given divider. 526 | // divider must be a power of two greater or equal to two. 527 | 528 | static void generate_mipmap_level(Image *source_image, int divider, Image *dest_image) { 529 | // Always round down the new dimensions. 530 | dest_image->width = source_image->width / divider; 531 | dest_image->height = source_image->height / divider; 532 | dest_image->extended_width = dest_image->width; 533 | dest_image->extended_height = dest_image->height; 534 | dest_image->alpha_bits = source_image->alpha_bits; 535 | dest_image->nu_components = source_image->nu_components; 536 | dest_image->bits_per_component = source_image->bits_per_component; 537 | dest_image->srgb = 0; 538 | dest_image->is_signed = source_image->is_signed; 539 | dest_image->is_half_float = source_image->is_half_float; 540 | dest_image->pixels = (unsigned int *)malloc(dest_image->extended_width * dest_image->extended_height * 4); 541 | // Check whether the width and the height are a power of two. 542 | int count = 0; 543 | for (int i = 0; i < 30; i++) { 544 | if (source_image->width == (1 << i)) 545 | count++; 546 | if (source_image->height == (1 << i)) 547 | count++; 548 | } 549 | if (count != 2) { 550 | if (divider != 2) { 551 | printf("Error -- non-power-of-two mipmap must be generated from previous level.\n"); 552 | exit(1); 553 | } 554 | if (source_image->is_half_float) { 555 | dest_image->pixels = (unsigned int *)realloc(dest_image->pixels, 556 | dest_image->extended_width * dest_image->extended_height * 8); 557 | create_half_float_mipmap_polyphase(source_image, dest_image); 558 | } 559 | else 560 | if (source_image->bits_per_component == 16 && !source_image->is_signed) { 561 | create_16_bit_mipmap_polyphase(source_image, dest_image); 562 | } 563 | else 564 | if (source_image->bits_per_component == 16 && source_image->is_signed) { 565 | create_signed_16_bit_mipmap_polyphase(source_image, dest_image); 566 | } 567 | else 568 | if (source_image->bits_per_component == 8 && source_image->is_signed) { 569 | create_signed_8_bit_mipmap_polyphase(source_image, dest_image); 570 | } 571 | else 572 | create_mipmap_polyphase(source_image, dest_image); 573 | } 574 | else 575 | if (divider == 2) 576 | if (source_image->is_half_float) { 577 | dest_image->pixels = (unsigned int *)realloc(dest_image->pixels, 578 | dest_image->extended_width * dest_image->extended_height * 8); 579 | create_half_float_mipmap_with_averaging_divider_2(source_image, dest_image); 580 | } 581 | else 582 | if (source_image->bits_per_component == 16 && !source_image->is_signed) { 583 | create_16_bit_mipmap_with_averaging_divider_2(source_image, dest_image); 584 | } 585 | else 586 | if (source_image->bits_per_component == 16 && source_image->is_signed) { 587 | create_signed_16_bit_mipmap_with_averaging_divider_2(source_image, dest_image); 588 | } 589 | else 590 | if (source_image->bits_per_component == 8 && source_image->is_signed) { 591 | create_signed_8_bit_mipmap_with_averaging_divider_2(source_image, dest_image); 592 | } 593 | else 594 | create_mipmap_with_averaging_divider_2(source_image, dest_image); 595 | else { 596 | if (source_image->is_half_float) { 597 | printf("Error -- cannot generate mipmaps for image with half-float components."); 598 | exit(1); 599 | } 600 | if (source_image->bits_per_component == 16 || source_image->is_signed) { 601 | printf("Error -- cannot generate mipmaps for image with 16-bit or signed components.\n"); 602 | exit(1); 603 | } 604 | create_mipmap_with_averaging(source_image, divider, dest_image); 605 | } 606 | } 607 | 608 | void generate_mipmap_level_from_original(Image *source_image, int level, Image *dest_image) { 609 | int divider = 1 << level; 610 | generate_mipmap_level(source_image, divider, dest_image); 611 | } 612 | 613 | void generate_mipmap_level_from_previous_level(Image *source_image, Image *dest_image) { 614 | generate_mipmap_level(source_image, 2, dest_image); 615 | } 616 | 617 | int count_mipmap_levels(Image *image) { 618 | int i = 1; 619 | int divider = 2; 620 | for (;;) { 621 | int width = image->width / divider; 622 | int height = image->height / divider; 623 | if (width < 1 || height < 1) 624 | return i; 625 | i++; 626 | divider *= 2; 627 | } 628 | } 629 | 630 | -------------------------------------------------------------------------------- /packing.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015 Harm Hanemaaijer 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | */ 18 | 19 | #include 20 | 21 | // Define some short functions for pixel packing/unpacking. The compiler will take care of optimization by 22 | // inlining and removing unused functions. 23 | 24 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ || !defined(__BYTE_ORDER__) 25 | 26 | static unsigned int pack_rgba(int r, int g, int b, int a) { 27 | return (unsigned int)r | ((unsigned int)g << 8) | ((unsigned int)b << 16) | 28 | ((unsigned int)a << 24); 29 | } 30 | 31 | static unsigned int pack_rgb_alpha_0xff(int r, int g, int b) { 32 | return pack_rgba(r, g, b, 0xFF); 33 | } 34 | 35 | static unsigned int pack_r(int r) { 36 | return (unsigned int)r; 37 | } 38 | 39 | static unsigned int pack_g(int g) { 40 | return (unsigned int)g << 8; 41 | } 42 | 43 | static unsigned int pack_b(int b) { 44 | return (unsigned int)b << 16; 45 | } 46 | 47 | static unsigned int pack_a(int a) { 48 | return (unsigned int)a << 24; 49 | } 50 | 51 | static unsigned int pack_half_float(int r16, int g16) { 52 | return (unsigned int)r16 | ((unsigned int)g16 << 16); 53 | } 54 | 55 | static uint64_t pack_r16(unsigned int r16) { 56 | return r16; 57 | } 58 | 59 | static uint64_t pack_g16(unsigned int g16) { 60 | return g16 << 16; 61 | } 62 | 63 | static uint64_t pack_b16(unsigned int b16) { 64 | return (uint64_t)b16 << 32; 65 | } 66 | 67 | static uint64_t pack_a16(unsigned int a16) { 68 | return (uint64_t)a16 << 48; 69 | } 70 | 71 | static uint64_t pack_rgb16(uint16_t r16, uint16_t g16, uint16_t b16) { 72 | return (uint64_t)r16 | ((uint64_t)g16 << 16) | ((uint64_t)b16 << 32); 73 | } 74 | 75 | static uint64_t pack_rgba16(uint16_t r16, uint16_t g16, uint16_t b16, uint16_t a16) { 76 | return (uint64_t)r16 | ((uint64_t)g16 << 16) | ((uint64_t)b16 << 32) | ((uint64_t)a16 << 48); 77 | } 78 | 79 | static int pixel_get_r(unsigned int pixel) { 80 | return pixel & 0xFF; 81 | } 82 | 83 | static int pixel_get_g(unsigned int pixel) { 84 | return (pixel & 0xFF00) >> 8; 85 | } 86 | 87 | static int pixel_get_b(unsigned int pixel) { 88 | return (pixel & 0xFF0000) >> 16; 89 | } 90 | 91 | static int pixel_get_a(unsigned int pixel) { 92 | return (pixel & 0xFF000000) >> 24; 93 | } 94 | 95 | static int pixel_get_signed_r8(unsigned int pixel) { 96 | return (char)(pixel & 0xFF); 97 | } 98 | 99 | static int pixel_get_signed_g8(unsigned int pixel) { 100 | return (char)((pixel & 0xFF00) >> 8); 101 | } 102 | 103 | static unsigned int pixel_get_r16(unsigned int pixel) { 104 | return pixel & 0x0000FFFF; 105 | } 106 | 107 | static unsigned int pixel_get_g16(unsigned int pixel) { 108 | return (pixel & 0xFFFF0000) >> 16; 109 | } 110 | 111 | static int pixel_get_signed_r16(unsigned int pixel) { 112 | return (short)(pixel & 0xFFFF); 113 | } 114 | 115 | static int pixel_get_signed_g16(unsigned int pixel) { 116 | return (short)((pixel & 0xFFFF0000) >> 16); 117 | } 118 | 119 | static uint64_t pixel64_get_r16(uint64_t pixel) { 120 | return pixel & 0xFFFF; 121 | } 122 | 123 | static uint64_t pixel64_get_g16(uint64_t pixel) { 124 | return (pixel & 0xFFFF0000) >> 16; 125 | } 126 | 127 | static uint64_t pixel64_get_b16(uint64_t pixel) { 128 | return (pixel & 0xFFFF00000000) >> 32; 129 | } 130 | 131 | static uint64_t pixel64_get_a16(uint64_t pixel) { 132 | return (pixel & 0xFFFF000000000000) >> 48; 133 | } 134 | 135 | 136 | #define alpha_byte_offset 3 137 | 138 | #else 139 | 140 | static unsigned int pack_rgba(int r, int g, int b, int a) { 141 | return a | ((unsigned int)b << 8) | ((unsigned int)g << 16) | ((unsigned int )r << 24); 142 | } 143 | 144 | static unsigned int pack_rgb_alpha_0xff(int r, int g, int b) { 145 | return pack_rgba(r, g, b, 0xFF); 146 | } 147 | 148 | static unsigned int pack_r(int r) { 149 | return (unsigned int)r << 24; 150 | } 151 | 152 | static unsigned int pack_g(int g) { 153 | return (unsigned int)g << 16; 154 | } 155 | 156 | static unsigned int pack_b(int b) { 157 | return (unsigned int)b << 8; 158 | } 159 | 160 | static unsigned int pack_a(int a) { 161 | return a; 162 | } 163 | 164 | static unsigned int pack_half_float(int r16, int g16) { 165 | return (unsigned int)g16 | ((unsigned int)r16 << 16); 166 | } 167 | 168 | static int pixel_get_r(unsigned int pixel) { 169 | return (pixel & 0xFF000000) >> 24; 170 | } 171 | 172 | static int pixel_get_g(unsigned int pixel) { 173 | return (pixel & 0xFF0000) >> 16; 174 | } 175 | 176 | static int pixel_get_b(unsigned int pixel) { 177 | return (pixel & 0xFF00) >> 8; 178 | } 179 | 180 | static int pixel_get_a(unsigned int pixel) { 181 | return pixel & 0xFF; 182 | } 183 | 184 | static unsigned int pixel_get_r16(unsigned int pixel) { 185 | return ((pixel & 0xFF000000) >> 24) | ((pixel & 0x00FF0000) >> 8); 186 | } 187 | 188 | static unsigned int pixel_get_g16(unsigned int pixel) { 189 | return ((pixel & 0x0000FF00) >> 8) | ((pixel & 0x000000FF) << 8); 190 | } 191 | 192 | static int pixel_get_signed_r16(unsigned int pixel) { 193 | return (short)(((pixel & 0xFF000000) >> 24) | ((pixel & 0x00FF0000) >> 8)); 194 | } 195 | 196 | static int pixel_get_signed_g16(unsigned int pixel) { 197 | return (short)(((pixel & 0x0000FF00) >> 8) | ((pixel & 0x000000FF) << 8)); 198 | } 199 | 200 | #define alpha_byte_offset 0 201 | 202 | #endif 203 | -------------------------------------------------------------------------------- /rgtc.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015 Harm Hanemaaijer 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | */ 18 | 19 | // A note about texgenpack's implementation of RGTC1 and RGTC2: 20 | // Unsigned RGTC's internal format is 8 bits per component [0, 255]. 21 | // Signed RGTC's internal format is 16 bits per component [-32768, 32768]. This is because signed RGTC 22 | // maps [-127, 127] to [0, 1], leaving -128 unused. 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "texgenpack.h" 29 | #include "decode.h" 30 | #include "packing.h" 31 | 32 | static int draw_block4x4_rgtc1_shared(const unsigned char *bitstring, unsigned int *image_buffer, int offset, int flags) { 33 | // LSBFirst byte order only. 34 | uint64_t bits = (*(uint64_t *)&bitstring[0]) >> 16; 35 | int lum0 = bitstring[0]; 36 | int lum1 = bitstring[1]; 37 | for (int i = 0; i < 16; i++) { 38 | int control_code = bits & 0x7; 39 | uint8_t output; 40 | if (lum0 > lum1) 41 | switch (control_code) { 42 | case 0 : output = lum0; break; 43 | case 1 : output = lum1; break; 44 | case 2 : output = (6 * lum0 + lum1) / 7; break; 45 | case 3 : output = (5 * lum0 + 2 * lum1) / 7; break; 46 | case 4 : output = (4 * lum0 + 3 * lum1) / 7; break; 47 | case 5 : output = (3 * lum0 + 4 * lum1) / 7; break; 48 | case 6 : output = (2 * lum0 + 5 * lum1) / 7; break; 49 | case 7 : output = (lum0 + 6 * lum1) / 7; break; 50 | } 51 | else 52 | switch (control_code) { 53 | case 0 : output = lum0; break; 54 | case 1 : output = lum1; break; 55 | case 2 : output = (4 * lum0 + lum1) / 5; break; 56 | case 3 : output = (3 * lum0 + 2 * lum1) / 5; break; 57 | case 4 : output = (2 * lum0 + 3 * lum1) / 5; break; 58 | case 5 : output = (lum0 + 4 * lum1) / 5; break; 59 | case 6 : output = 0; break; 60 | case 7 : output = 0xFF; break; 61 | } 62 | *((uint8_t *)&image_buffer[i] + offset) = output; 63 | bits >>= 3; 64 | } 65 | return 1; 66 | } 67 | 68 | int draw_block4x4_rgtc1(const unsigned char *bitstring, unsigned int *image_buffer, int flags) { 69 | memset(image_buffer, 0, 64); 70 | return draw_block4x4_rgtc1_shared(bitstring, image_buffer, 0, flags); 71 | } 72 | 73 | int draw_block4x4_signed_rgtc1_shared(const unsigned char *bitstring, unsigned int *image_buffer, 74 | int offset, int flags) { 75 | // LSBFirst byte order only. 76 | uint64_t bits = (*(uint64_t *)&bitstring[0]) >> 16; 77 | int lum0 = (char)bitstring[0]; 78 | int lum1 = (char)bitstring[1]; 79 | if (lum0 == - 127 && lum1 == - 128) 80 | // Not allowed. 81 | return 0; 82 | if (lum0 == - 128) 83 | lum0 = - 127; 84 | if (lum1 == - 128) 85 | lum1 = - 127; 86 | // Note: values are mapped to a red value of -127 to 127. 87 | for (int i = 0; i < 16; i++) { 88 | int control_code = bits & 0x7; 89 | int32_t result; 90 | if (lum0 > lum1) 91 | switch (control_code) { 92 | case 0 : result = lum0; break; 93 | case 1 : result = lum1; break; 94 | case 2 : result = (6 * lum0 + lum1) / 7; break; 95 | case 3 : result = (5 * lum0 + 2 * lum1) / 7; break; 96 | case 4 : result = (4 * lum0 + 3 * lum1) / 7; break; 97 | case 5 : result = (3 * lum0 + 4 * lum1) / 7; break; 98 | case 6 : result = (2 * lum0 + 5 * lum1) / 7; break; 99 | case 7 : result = (lum0 + 6 * lum1) / 7; break; 100 | } 101 | else 102 | switch (control_code) { 103 | case 0 : result = lum0; break; 104 | case 1 : result = lum1; break; 105 | case 2 : result = (4 * lum0 + lum1) / 5; break; 106 | case 3 : result = (3 * lum0 + 2 * lum1) / 5; break; 107 | case 4 : result = (2 * lum0 + 3 * lum1) / 5; break; 108 | case 5 : result = (lum0 + 4 * lum1) / 5; break; 109 | case 6 : result = - 127; break; 110 | case 7 : result = 127; break; 111 | } 112 | // Map from [-127, 127] to [-32768, 32767]. 113 | *((int16_t *)&image_buffer[i] + offset) = (int16_t)((result + 127) * 65535 / 254 - 32768); 114 | bits >>= 3; 115 | } 116 | return 1; 117 | } 118 | 119 | int draw_block4x4_signed_rgtc1(const unsigned char *bitstring, unsigned int *image_buffer, int flags) { 120 | memset(image_buffer, 0, 64); 121 | return draw_block4x4_signed_rgtc1_shared(bitstring, image_buffer, 0, flags); 122 | } 123 | 124 | // Note: the second component in stored in the green byte, not the alpha byte. 125 | 126 | int draw_block4x4_rgtc2(const unsigned char *bitstring, unsigned int *image_buffer, int flags) { 127 | memset(image_buffer, 0, 64); 128 | int r = draw_block4x4_rgtc1_shared(bitstring, image_buffer, 0, flags); 129 | if (r == 0) 130 | return 0; 131 | return draw_block4x4_rgtc1_shared(&bitstring[8], image_buffer, 1, flags); 132 | } 133 | 134 | int draw_block4x4_signed_rgtc2(const unsigned char *bitstring, unsigned int *image_buffer, int flags) { 135 | memset(image_buffer, 0, 64); 136 | int r = draw_block4x4_signed_rgtc1_shared(bitstring, image_buffer, 0, flags); 137 | if (r == 0) 138 | return 0; 139 | return draw_block4x4_signed_rgtc1_shared(&bitstring[8], image_buffer, 1, flags); 140 | } 141 | 142 | -------------------------------------------------------------------------------- /strcasecmp.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015 Harm Hanemaaijer 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | */ 18 | 19 | 20 | static void strtoupper(char *str) { 21 | int n = strlen(str); 22 | for (int i = 0; i < n; i++) 23 | str[i] = toupper(str[i]); 24 | } 25 | 26 | static int strcasecmp(const char *str1, const char *str2) { 27 | char *str1_upper = (char *)alloca(strlen(str1) + 1); 28 | strcpy(str1_upper, str1); 29 | strtoupper(str1_upper); 30 | char *str2_upper = (char *)alloca(strlen(str2) + 1); 31 | strcpy(str2_upper, str2); 32 | strtoupper(str2_upper); 33 | int r = strcmp(str1_upper, str2_upper); 34 | return r; 35 | } 36 | 37 | -------------------------------------------------------------------------------- /texgenpack.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015 Harm Hanemaaijer 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "texgenpack.h" 26 | #include "decode.h" 27 | #ifndef __GNUC__ 28 | #include "strcasecmp.h" 29 | #endif 30 | 31 | 32 | // Local functions in this file. 33 | 34 | static int file_exists(const char *filename); 35 | static int determine_filename_type(const char *filename); 36 | static void compare_files(); 37 | static void decompress(); 38 | static void compress(); 39 | static void calibrate(); 40 | 41 | // Variables reflecting command-line options. 42 | 43 | int command; 44 | int option_verbose = 0; 45 | int option_max_threads = - 1; 46 | char *source_filename; 47 | char *dest_filename; 48 | int source_filetype; 49 | int dest_filetype; 50 | int option_orientation = 0; 51 | int option_texture_format = - 1; 52 | int option_compression_level = SPEED_FAST; 53 | int option_progress = 0; 54 | int option_modal_etc2 = 1; 55 | int option_allowed_modes_etc2 = - 1; 56 | int option_mipmaps = 0; 57 | int option_generations = - 1; 58 | int option_islands = - 1; 59 | int option_generations_second_pass = - 1; 60 | int option_islands_second_pass = - 1; 61 | int option_flip_vertical = 0; 62 | int option_quiet = 0; 63 | int option_block_width = 4; 64 | int option_block_height = 4; 65 | int option_half_float = 0; 66 | int option_hdr = 0; 67 | int option_perceptive = 1; 68 | 69 | // Other option variables that are not actually set by command-line options. 70 | 71 | int option_deterministic = 0; 72 | 73 | static char *instructions1 = 74 | "texgenpack v0.9.6 -- Texture conversion and compression using a genetic algorithm.\n" 75 | "Usage: texgenpack .\n" 76 | "\n" 77 | "Commands:\n"; 78 | 79 | #define NU_COMMANDS 4 80 | 81 | static const char *commands[NU_COMMANDS] = { 82 | "--compress", "--decompress", "--compare", "--calibrate" }; 83 | 84 | #define NU_OPTIONS 24 85 | 86 | enum { 87 | OPTION_COMPRESSION_LEVEL = 0, 88 | OPTION_ULTRA, 89 | OPTION_FAST, 90 | OPTION_MEDIUM, 91 | OPTION_SLOW, 92 | OPTION_NON_PERCEPTIVE, 93 | OPTION_TEXTURE_FORMAT, 94 | OPTION_HALF_FLOAT, 95 | OPTION_HDR, 96 | OPTION_MIPMAPS, 97 | OPTION_ORIENTATION, 98 | OPTION_FLIP_VERTICAL, 99 | OPTION_MODAL_ETC2, 100 | OPTION_ALLOWED_MODES, 101 | OPTION_MAXTHREADS, 102 | OPTION_GENERATIONS, 103 | OPTION_ISLANDS, 104 | OPTION_GENERATIONS_SECOND_PASS, 105 | OPTION_ISLANDS_SECOND_PASS, 106 | OPTION_PROGRESS, 107 | OPTION_VERBOSE, 108 | OPTION_VERY_VERBOSE, 109 | OPTION_QUIET, 110 | OPTION_VERBOSITY, 111 | }; 112 | 113 | static const char *options[NU_OPTIONS] = { 114 | "--level", "--ultra", "--fast", "--medium", "--slow", 115 | "--non-perceptive", 116 | "--format", 117 | "--half-float", "--hdr", 118 | "--mipmaps", 119 | "--orientation", "--flip-vertical", 120 | "--modal", "--allowed-modes", 121 | "--maxthreads", "--generations", "--islands", "--generations-second-pass", "--islands-second-pass", 122 | "--progress", "--verbose", "--very-verbose", "--quiet", "--verbosity" 123 | }; 124 | 125 | static const char *option_argument[NU_OPTIONS] = { 126 | "", "", "", "", "", 127 | "", 128 | "", 129 | "", "", 130 | "", 131 | "", "", 132 | "", "", 133 | "", "", "", "", "", 134 | "", "", "", "", "", 135 | }; 136 | 137 | static const char *option_description[NU_OPTIONS] = { 138 | "Compression level (0 to 50, 0 = fastest, 50 = slowest/best compression).", 139 | "Ultra fast compression optimizing different blocks concurrently.", 140 | "Fast compression method (level = 8) (default).", 141 | "Medium compression method (level = 16).", 142 | "Slow compression method (level = 32).", 143 | "Use non-perceptive quality strategy (defaalt for --ultra setting).", 144 | "Texture format. One of the following: ", 145 | "Convert regular images to half-float format before compression.", 146 | "The half-float format contains a HDR texture that is not normalized. This affects compression.", 147 | "Generate mipmaps when compressing an image into a texture. When decompressing to an image file, generate a " 148 | "sequence of image files named filename-mipmap*.png holding the mipmap levels.", 149 | "Write orientation key when writing .ktx file. Direction must be up or down.", 150 | "Flip the texture vertically during the conversion process.", 151 | "Use a different technique for ETC2 compression with islands tied to specific ETC2 modes.", 152 | "Specify the ETC2 modes to use. Argument is a string containing a subset of the letters IDTHP.", 153 | "Specify the maximum number of threads to use (not recommended; uses --islands option to control threading level).", 154 | "Set the number of generations for the genetic algorithm per block (adjusted from compression level).", 155 | "Set the number of concurrent islands for the genetic algorithm (adjusted from compression level).", 156 | "Set the number of generations for the second pass of the genetic algorithm per block (adjusted from " 157 | "compression level).", 158 | "Set the number of concurrent islands for the second pass of the genetic algorithm (adjusted from " 159 | "compression level).", 160 | "Display a percentage progress indicator.", 161 | "Be verbose (information for each block).", 162 | "Be very verbose (more information for each block).", 163 | "Don't print anything.", 164 | "Set verbosity level (0 to 3), 0 = quiet, 1 = verbose, 2 = very verbose.", 165 | }; 166 | 167 | int main(int argc, char **argv) { 168 | if (argc <= 2) { 169 | printf("%s", instructions1); 170 | for (int i = 0; i < NU_COMMANDS; i++) 171 | printf("%s\n", commands[i]); 172 | printf("\nOptions:\n"); 173 | for (int i = 0; i < NU_OPTIONS; i++) { 174 | printf("%s %s\n %s\n", options[i], option_argument[i], option_description[i]); 175 | if (i == OPTION_TEXTURE_FORMAT) { 176 | int n = get_number_of_texture_formats(); 177 | for (int j = 0; j < n - 1; j++) { 178 | printf("%s, ", get_texture_format_index_text(j, 0)); 179 | const char *text2 = get_texture_format_index_text(j, 1); 180 | if (strlen(text2) > 0) 181 | printf("%s, ", text2); 182 | } 183 | printf("%s\n", get_texture_format_index_text(n - 1, 0)); 184 | } 185 | } 186 | exit(0); 187 | } 188 | 189 | command = - 1; 190 | for (int i = 0; i < NU_COMMANDS; i++) 191 | if (strcmp(argv[1], commands[i]) == 0) { 192 | command = i; 193 | break; 194 | } 195 | if (command == -1) { 196 | printf("Error -- no valid command found as first argument. Valid command are --compress,\n" 197 | "--decompress and --compare. Run with no arguments for help.\n"); 198 | exit(1); 199 | } 200 | 201 | int i = 2; 202 | for (; i < argc;) { 203 | int option = - 1; 204 | for (int j = 0; j < NU_OPTIONS; j++) 205 | if (strcmp(argv[i], options[j]) == 0) { 206 | option = j; 207 | break; 208 | } 209 | if (option == - 1) 210 | break; 211 | // Single argument options. 212 | switch (option) { 213 | case OPTION_VERBOSE : 214 | option_verbose = 1; 215 | i++; 216 | continue; 217 | case OPTION_VERY_VERBOSE : 218 | option_verbose = 2; 219 | i++; 220 | continue; 221 | case OPTION_FAST : 222 | option_compression_level = SPEED_FAST; 223 | i++; 224 | continue; 225 | case OPTION_MEDIUM : 226 | option_compression_level = SPEED_MEDIUM; 227 | i++; 228 | continue; 229 | case OPTION_SLOW : 230 | option_compression_level = SPEED_SLOW; 231 | i++; 232 | continue; 233 | case OPTION_NON_PERCEPTIVE : 234 | option_perceptive = 0; 235 | i++; 236 | continue; 237 | case OPTION_PROGRESS : 238 | option_progress = 1; 239 | i++; 240 | continue; 241 | case OPTION_MODAL_ETC2 : 242 | option_modal_etc2 = 1; 243 | i++; 244 | continue; 245 | case OPTION_ULTRA : 246 | option_compression_level = SPEED_ULTRA; 247 | i++; 248 | continue; 249 | case OPTION_MIPMAPS : 250 | option_mipmaps = 1; 251 | i++; 252 | continue; 253 | case OPTION_FLIP_VERTICAL : 254 | option_flip_vertical = 1; 255 | i++; 256 | continue; 257 | case OPTION_QUIET : 258 | option_quiet = 1; 259 | i++; 260 | continue; 261 | case OPTION_HALF_FLOAT : 262 | option_half_float = 1; 263 | i++; 264 | continue; 265 | case OPTION_HDR : 266 | option_hdr = 1; 267 | i++; 268 | continue; 269 | } 270 | // Two argument options. 271 | if (i + 1 >= argc) { 272 | printf("Error -- no filenames specified.\n"); 273 | exit(1); 274 | } 275 | int value; 276 | switch (option) { 277 | case OPTION_MAXTHREADS : 278 | value = atoi(argv[i + 1]); 279 | if (value < 1 || value > 256) { 280 | printf("Error -- invalid number of max threads specified.\n"); 281 | exit(1); 282 | } 283 | option_max_threads = value; 284 | i += 2; 285 | break; 286 | case OPTION_ORIENTATION : 287 | if (strcasecmp(argv[i + 1], "down") == 0) 288 | option_orientation = ORIENTATION_DOWN; 289 | else 290 | if (strcasecmp(argv[i + 1], "up") == 0) 291 | option_orientation = ORIENTATION_UP; 292 | else { 293 | printf("Error -- orientation should be down or up.\n"); 294 | exit(1); 295 | } 296 | i += 2; 297 | break; 298 | case OPTION_TEXTURE_FORMAT : 299 | { 300 | TextureInfo *info; 301 | info = match_texture_description(argv[i + 1]); 302 | if (info != NULL) 303 | option_texture_format = info->type; 304 | else { 305 | printf("Error -- unknown texture format specified.\n"); 306 | exit(1); 307 | } 308 | i += 2; 309 | break; 310 | } 311 | case OPTION_ALLOWED_MODES : 312 | option_allowed_modes_etc2 = 0; 313 | for (int j = 0; j < strlen(argv[i + 1]); j++) 314 | switch (argv[i + 1][j]) { 315 | case 'I' : 316 | option_allowed_modes_etc2 |= ETC_MODE_ALLOWED_INDIVIDUAL; 317 | break; 318 | case 'D' : 319 | option_allowed_modes_etc2 |= ETC_MODE_ALLOWED_DIFFERENTIAL; 320 | break; 321 | case 'T' : 322 | option_allowed_modes_etc2 |= ETC2_MODE_ALLOWED_T; 323 | break; 324 | case 'H' : 325 | option_allowed_modes_etc2 |= ETC2_MODE_ALLOWED_H; 326 | break; 327 | case 'P' : 328 | option_allowed_modes_etc2 |= ETC2_MODE_ALLOWED_PLANAR; 329 | break; 330 | default : 331 | printf("Error -- invalid character in modes arguments for --allowed-modes.\n"); 332 | exit(1); 333 | } 334 | i += 2; 335 | break; 336 | case OPTION_GENERATIONS : 337 | value = atoi(argv[i + 1]); 338 | if (value < 1 || value > 10000) { 339 | printf("Error -- invalid number of generations specified (range 1-10000).\n"); 340 | exit(1); 341 | } 342 | option_generations = value; 343 | i += 2; 344 | break; 345 | case OPTION_ISLANDS : 346 | value = atoi(argv[i + 1]); 347 | if (value < 1 || value > 64) { 348 | printf("Error -- invalid number of islands specified (range 1-64).\n"); 349 | exit(1); 350 | } 351 | option_islands = value; 352 | i += 2; 353 | break; 354 | case OPTION_GENERATIONS_SECOND_PASS : 355 | value = atoi(argv[i + 1]); 356 | if (value < 1 || value > 100000) { 357 | printf("Error -- invalid number of generations specified (range 1-100000).\n"); 358 | exit(1); 359 | } 360 | option_generations_second_pass = value; 361 | i += 2; 362 | break; 363 | case OPTION_ISLANDS_SECOND_PASS : 364 | value = atoi(argv[i + 1]); 365 | if (value < 1 || value > 64) { 366 | printf("Error -- invalid number of islands specified (range 1-64).\n"); 367 | exit(1); 368 | } 369 | option_islands_second_pass = value; 370 | i += 2; 371 | break; 372 | case OPTION_COMPRESSION_LEVEL : 373 | value = atoi(argv[i + 1]); 374 | if (value < 0 || value > 50) { 375 | printf("Error -- invalid compression level (range 0-50).\n"); 376 | exit(1); 377 | } 378 | option_compression_level = value; 379 | i += 2; 380 | break; 381 | case OPTION_VERBOSITY : 382 | value = atoi(argv[i + 1]); 383 | if (value < 0 || value > 3) { 384 | printf("Error -- invalid verbosity (range 0-3).\n"); 385 | exit(1); 386 | } 387 | option_verbose = value; 388 | i += 2; 389 | break; 390 | #if 0 391 | case OPTION_BLOCK_SIZE : 392 | { 393 | int w, h; 394 | int r = sscanf(argv[i + 1], "%dx%d", &w, &h); 395 | if (r < 2) { 396 | printf("Error -- expected valid block size, such as 4x4, 5x4 etc.\n"); 397 | exit(1); 398 | } 399 | int j = match_astc_block_size(w, h); 400 | if (j == - 1) { 401 | printf("Error -- ASTC block size not supported.\n"); 402 | exit(1); 403 | } 404 | option_block_width = w; 405 | option_block_height = h; 406 | i += 2; 407 | break; 408 | } 409 | #endif 410 | } 411 | } 412 | 413 | if (i >= argc - 1) { 414 | printf("Error -- expected two filenames at the end of the command line.\n"); 415 | exit(1); 416 | } 417 | if ((option_block_width != 4 || option_block_height != 4) && !(option_texture_format & TEXTURE_TYPE_ASTC_BIT)) { 418 | printf("Error -- block size option only allowed when compressing to ASTC format.\n"); 419 | exit(1); 420 | } 421 | 422 | source_filename = argv[i]; 423 | dest_filename = argv[i + 1]; 424 | source_filetype = determine_filename_type(source_filename); 425 | if (source_filetype == FILE_TYPE_UNDEFINED) { 426 | printf("Error -- unknown file %s (no known extension found).\n", source_filename); 427 | exit(1); 428 | } 429 | dest_filetype = determine_filename_type(dest_filename); 430 | if (dest_filetype == FILE_TYPE_UNDEFINED) { 431 | printf("Error -- unknown file %s (no known extension found).\n", dest_filename); 432 | exit(1); 433 | } 434 | if (!file_exists(source_filename)) { 435 | printf("Error -- source file doesn't exist or is unreadable.\n"); 436 | exit(1); 437 | } 438 | if (strcasecmp(source_filename, dest_filename) == 0) { 439 | printf("Error -- source filename and destination filename are identical.\n"); 440 | exit(1); 441 | } 442 | if (command == COMMAND_COMPARE) { 443 | if (!file_exists(dest_filename)) { 444 | printf("Error -- second filename to compare to doesn't exist or is unreadable.\n"); 445 | exit(1); 446 | } 447 | compare_files(); 448 | exit(0); 449 | } 450 | if (command == COMMAND_DECOMPRESS) { 451 | if ((source_filetype & FILE_TYPE_TEXTURE_BIT) == 0) { 452 | printf("Error -- expected texture file as first argument.\n"); 453 | exit(1); 454 | } 455 | // if ((dest_filetype & FILE_TYPE_IMAGE_BIT) == 0) { 456 | // printf("Error -- expected image file as second argument.\n"); 457 | // exit(1); 458 | // } 459 | decompress(); 460 | exit(0); 461 | } 462 | if (command == COMMAND_COMPRESS) { 463 | // if ((source_filetype & FILE_TYPE_IMAGE_BIT) == 0) { 464 | // printf("Error -- expected image file as first argument.\n"); 465 | // exit(1); 466 | // } 467 | if ((dest_filetype & FILE_TYPE_TEXTURE_BIT) == 0) { 468 | printf("Error -- expected texture file as second argument.\n"); 469 | exit(1); 470 | } 471 | if (option_mipmaps && (dest_filetype & FILE_TYPE_MIPMAPS_BIT) == 0) { 472 | printf("Error -- destination file type cannot hold multiple mipmap levels.\n"); 473 | exit(1); 474 | } 475 | compress(); 476 | } 477 | if (command == COMMAND_CALIBRATE) { 478 | calibrate(); 479 | } 480 | } 481 | 482 | static int file_exists(const char *filename) { 483 | FILE *f = fopen(filename, "rb"); 484 | if (f == NULL) 485 | return 0; 486 | fclose(f); 487 | return 1; 488 | } 489 | 490 | static const char *extension[NU_FILE_TYPES] = { 491 | ".png", ".ppm", ".ktx", ".pkm", ".dds", ".astc" }; 492 | 493 | static int file_type_id[NU_FILE_TYPES] = { 494 | FILE_TYPE_PNG, FILE_TYPE_PPM, FILE_TYPE_KTX, FILE_TYPE_PKM, FILE_TYPE_DDS, FILE_TYPE_ASTC }; 495 | 496 | static int determine_filename_type(const char *filename) { 497 | if (strlen(filename) < 5) { 498 | printf("Error -- filename %s too short to be a valid file.\n", filename); 499 | exit(1); 500 | } 501 | for (int i = 0; i < NU_FILE_TYPES; i++) 502 | if (strcasecmp(&filename[strlen(filename) - strlen(extension[i])], extension[i]) == 0) 503 | return file_type_id[i]; 504 | return FILE_TYPE_UNDEFINED; 505 | } 506 | 507 | static const char *get_extension(int filetype) { 508 | for (int i = 0; i < NU_FILE_TYPES; i++) { 509 | if (filetype == file_type_id[i]) 510 | return extension[i]; 511 | } 512 | return NULL; 513 | } 514 | 515 | static void compare_files() { 516 | Image source_image, dest_image; 517 | if (!option_quiet) 518 | printf("Comparing %s to %s.\n", source_filename, dest_filename); 519 | load_image(source_filename, source_filetype, &source_image); 520 | if (option_flip_vertical) 521 | flip_image_vertical(&source_image); 522 | load_image(dest_filename, dest_filetype, &dest_image); 523 | if (source_image.width != dest_image.width || source_image.height != dest_image.height) { 524 | printf("Error -- image files to compare must be have same dimensions.\n"); 525 | exit(1); 526 | } 527 | compare_images(&source_image, &dest_image); 528 | } 529 | 530 | static void decompress() { 531 | if (dest_filetype & FILE_TYPE_TEXTURE_BIT) { 532 | if (option_texture_format != -1) 533 | if (!(option_texture_format & TEXTURE_TYPE_UNCOMPRESSED_BIT)) { 534 | printf("Error -- expected uncompressed texture type for destination.\n"); 535 | exit(1); 536 | } 537 | if (option_mipmaps && !(source_filetype & FILE_TYPE_MIPMAPS_BIT)) { 538 | printf("Error -- destination texture file type cannot hold mipmaps.\n"); 539 | exit(1); 540 | } 541 | } 542 | if (option_mipmaps && (source_filetype & FILE_TYPE_MIPMAPS_BIT)) { 543 | Image image[32]; 544 | int n = load_mipmap_images(source_filename, source_filetype, 32, &image[0]); 545 | if (dest_filetype & FILE_TYPE_IMAGE_BIT) { 546 | // Saving multiple mipmaps to multiple image files. 547 | int j = strlen(dest_filename); 548 | char *basefilename = (char *)alloca(j - 4 + 1); 549 | strncpy(basefilename, dest_filename, j - 4); 550 | basefilename[j - 4] = '\0'; 551 | char *filename = (char *)alloca(j + 12); 552 | for (int i = 0; i < n; i++) { 553 | sprintf(filename, "%s-mipmap%d%s", basefilename, i, get_extension(dest_filetype)); 554 | if (option_flip_vertical) 555 | flip_image_vertical(&image[i]); 556 | save_image(&image[i], filename, dest_filetype); 557 | } 558 | } 559 | else { 560 | // Saving multiple mipmaps to uncompressed texture file. 561 | Texture texture[32]; 562 | int texture_type; 563 | if (option_texture_format != - 1) 564 | texture_type = option_texture_format; 565 | else 566 | if (image[0].alpha_bits > 0) 567 | texture_type = TEXTURE_TYPE_UNCOMPRESSED_RGBA8; 568 | else 569 | texture_type = TEXTURE_TYPE_UNCOMPRESSED_RGB8; 570 | for (int i = 0; i < n; i++) 571 | copy_image_to_uncompressed_texture(&image[i], texture_type, &texture[i]); 572 | save_texture(&texture[0], n, dest_filename, dest_filetype); 573 | } 574 | return; 575 | } 576 | Image image; 577 | if (!option_quiet) 578 | printf("Decompressing %s to %s.\n", source_filename, dest_filename); 579 | // Load image, decompressing texture if necessary. 580 | load_image(source_filename, source_filetype, &image); 581 | if (option_flip_vertical) 582 | flip_image_vertical(&image); 583 | // Save to an image or uncompressed texture file. 584 | if (dest_filetype & FILE_TYPE_TEXTURE_BIT) { 585 | int texture_type; 586 | if (option_texture_format != - 1) 587 | texture_type = option_texture_format; 588 | else 589 | if (image.alpha_bits > 0) 590 | texture_type = TEXTURE_TYPE_UNCOMPRESSED_RGBA8; 591 | else 592 | texture_type = TEXTURE_TYPE_UNCOMPRESSED_RGB8; 593 | Texture texture; 594 | copy_image_to_uncompressed_texture(&image, texture_type, &texture); 595 | save_texture(&texture, 1, dest_filename, dest_filetype); 596 | } 597 | else 598 | save_image(&image, dest_filename, dest_filetype); 599 | } 600 | 601 | static void compress_callback(BlockUserData *user_data) { 602 | // Do nothing. 603 | } 604 | 605 | static void compress() { 606 | Image image[32]; 607 | if (!option_quiet) 608 | printf("Compressing %s to %s.\n", source_filename, dest_filename); 609 | int nu_mipmaps; 610 | if (source_filetype & FILE_TYPE_MIPMAPS_BIT) 611 | nu_mipmaps = load_mipmap_images(source_filename, source_filetype, 32, &image[0]); 612 | else { 613 | load_image(source_filename, source_filetype, &image[0]); 614 | nu_mipmaps = 1; 615 | } 616 | if (option_flip_vertical) 617 | for (int i = 0; i < nu_mipmaps; i++) 618 | flip_image_vertical(&image[i]); 619 | int texture_type; 620 | if (option_texture_format != - 1) 621 | texture_type = option_texture_format; 622 | else 623 | // Set default compression format for the given the file type. 624 | if (dest_filetype == FILE_TYPE_KTX || dest_filetype == FILE_TYPE_PKM) 625 | texture_type = TEXTURE_TYPE_ETC1; 626 | else 627 | if (dest_filetype == FILE_TYPE_DDS) 628 | texture_type = TEXTURE_TYPE_DXT1; 629 | const char *texture_type_str = texture_type_text(texture_type); 630 | if (!option_quiet) { 631 | if (nu_mipmaps > 1) 632 | printf("Number of mipmaps in source file: %d\n", nu_mipmaps); 633 | printf("Target texture format: %s\n", texture_type_str); 634 | } 635 | // If there is only one mipmap in the source, and the --mipmaps option was given, generate mipmaps. 636 | int generate_mipmaps = 0; 637 | if (option_mipmaps && nu_mipmaps == 1) { 638 | nu_mipmaps = count_mipmap_levels(&image[0]); 639 | generate_mipmaps = 1; 640 | if (!option_quiet) 641 | printf("Generating %d mipmaps.\n", nu_mipmaps); 642 | } 643 | Texture *texture = (Texture *)alloca(sizeof(Texture) * nu_mipmaps); 644 | Image *mipmap_image = (Image *)alloca(sizeof(Image) * nu_mipmaps); 645 | if (generate_mipmaps) { 646 | mipmap_image[0] = image[0]; 647 | if ((texture_type & TEXTURE_TYPE_SRGB_BIT) && nu_mipmaps > 1) { 648 | // For sRGB textures, perform mipmapping after first converting to RGB and then convert the 649 | // RGB mipmaps back to sRGB. 650 | Image *rgb_mipmap_image = (Image *)alloca(sizeof(Image) * nu_mipmaps); 651 | if (!option_quiet) 652 | printf("Converting image from sRGB to RGB for mipmap generation.\n"); 653 | convert_image_from_srgb_to_rgb(&image[0], &rgb_mipmap_image[0]); 654 | // Generate the mipmaps in RGB space. 655 | for (int i = 1; i < nu_mipmaps; i++) { 656 | generate_mipmap_level_from_previous_level(&rgb_mipmap_image[i - 1], &rgb_mipmap_image[i]); 657 | } 658 | // Convert them to sRGB 659 | for (int i = 1; i < nu_mipmaps; i++) { 660 | convert_image_from_rgb_to_srgb(&rgb_mipmap_image[i], &mipmap_image[i]); 661 | } 662 | } 663 | else 664 | for (int i = 1; i < nu_mipmaps; i++) { 665 | generate_mipmap_level_from_previous_level(&mipmap_image[i - 1], &mipmap_image[i]); 666 | } 667 | } 668 | else { 669 | // The mipmaps are present in the source file. 670 | for (int i = 0; i < nu_mipmaps; i++) { 671 | mipmap_image[i] = image[i]; 672 | printf("Source mipmap %d: %d x %d\n", i, mipmap_image[i].width, mipmap_image[i].height); 673 | } 674 | } 675 | for (int i = 0; i < nu_mipmaps; i++) { 676 | // Compress the image into a texture. 677 | if (!option_quiet) 678 | printf("Mipmap level: %d (%d x %d)\n", i, mipmap_image[i].width, mipmap_image[i].height); 679 | compress_image(&mipmap_image[i], texture_type, compress_callback, &texture[i], 0, 0, 0); 680 | // Decompress the compressed texture and calculate the difference with the original. 681 | Image compressed_image; 682 | convert_texture_to_image(&texture[i], &compressed_image); 683 | compare_images(&mipmap_image[i], &compressed_image); 684 | destroy_image(&compressed_image); 685 | } 686 | // Save texture. 687 | save_texture(&texture[0], nu_mipmaps, dest_filename, dest_filetype); 688 | } 689 | 690 | static void calibrate() { 691 | Image image; 692 | if (!option_quiet) 693 | printf("Calibrating genetic parameters for compression of source file %s.\n", source_filename); 694 | load_image(source_filename, source_filetype, &image); 695 | int texture_type; 696 | if (option_texture_format != - 1) 697 | texture_type = option_texture_format; 698 | else 699 | // Set default compression format for the given the file type. 700 | if (dest_filetype == FILE_TYPE_KTX || dest_filetype == FILE_TYPE_PKM) 701 | texture_type = TEXTURE_TYPE_ETC1; 702 | else 703 | if (dest_filetype == FILE_TYPE_DDS) 704 | texture_type = TEXTURE_TYPE_DXT1; 705 | calibrate_genetic_parameters(&image, texture_type); 706 | } 707 | 708 | -------------------------------------------------------------------------------- /texgenpack.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015 Harm Hanemaaijer 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | */ 18 | 19 | #define NU_FILE_TYPES 6 20 | 21 | #define FILE_TYPE_PNG 0x101 22 | #define FILE_TYPE_PPM 0x102 23 | #define FILE_TYPE_KTX 0x210 24 | #define FILE_TYPE_PKM 0x201 25 | #define FILE_TYPE_DDS 0x212 26 | #define FILE_TYPE_ASTC 0x203 27 | #define FILE_TYPE_IMAGE_BIT 0x100 28 | #define FILE_TYPE_TEXTURE_BIT 0x200 29 | #define FILE_TYPE_MIPMAPS_BIT 0x010 30 | #define FILE_TYPE_UNDEFINED 0x000 31 | #define FILE_TYPE_IMAGE_UNKNOWN 0x100 32 | 33 | // Structures. 34 | 35 | typedef struct { 36 | unsigned int *pixels; 37 | int width; 38 | int height; 39 | int extended_width; 40 | int extended_height; 41 | int alpha_bits; // 0 for no alpha, 1 if alpha is limited to 0 and 0xFF, 8 otherwise. 42 | int nu_components; // Indicates the number of components. 43 | int bits_per_component; // 8 or 16. 44 | int is_signed; // 1 if the components are signed, 0 if unsigned. 45 | int srgb; // Whether the image is stored in sRGB format. 46 | int is_half_float; // The image pixels are combinations of half-floats. The pixel size is 64-bit. 47 | } Image; 48 | 49 | 50 | #define TEXTURE_TYPE_UNCOMPRESSED_RGB8 0x2000 51 | #define TEXTURE_TYPE_UNCOMPRESSED_RGBA8 0x2021 52 | #define TEXTURE_TYPE_UNCOMPRESSED_ARGB8 0x2022 53 | #define TEXTURE_TYPE_UNCOMPRESSED_RGB_HALF_FLOAT 0x6402 54 | #define TEXTURE_TYPE_UNCOMPRESSED_RGBA_HALF_FLOAT 0x6422 55 | #define TEXTURE_TYPE_UNCOMPRESSED_RG16 0x2400 56 | #define TEXTURE_TYPE_UNCOMPRESSED_RG_HALF_FLOAT 0x6800 57 | #define TEXTURE_TYPE_UNCOMPRESSED_R16 0x2401 58 | #define TEXTURE_TYPE_UNCOMPRESSED_R_HALF_FLOAT 0x6400 59 | #define TEXTURE_TYPE_UNCOMPRESSED_RG8 0x2001 60 | #define TEXTURE_TYPE_UNCOMPRESSED_R8 0x2002 61 | #define TEXTURE_TYPE_UNCOMPRESSED_SIGNED_RG16 0x2C00 62 | #define TEXTURE_TYPE_UNCOMPRESSED_SIGNED_R16 0x2C01 63 | #define TEXTURE_TYPE_UNCOMPRESSED_SIGNED_RG8 0x2801 64 | #define TEXTURE_TYPE_UNCOMPRESSED_SIGNED_R8 0x2802 65 | #define TEXTURE_TYPE_ETC1 0x0100 66 | #define TEXTURE_TYPE_ETC2_RGB8 0x0101 67 | #define TEXTURE_TYPE_ETC2_EAC 0x0162 68 | #define TEXTURE_TYPE_ETC2_PUNCHTHROUGH 0x0123 69 | #define TEXTURE_TYPE_R11_EAC 0x0400 70 | #define TEXTURE_TYPE_RG11_EAC 0x0440 71 | #define TEXTURE_TYPE_SIGNED_R11_EAC 0x0C00 72 | #define TEXTURE_TYPE_SIGNED_RG11_EAC 0x0C40 73 | #define TEXTURE_TYPE_ETC2_SRGB8 0x1104 74 | #define TEXTURE_TYPE_ETC2_SRGB_PUNCHTHROUGH 0x1125 75 | #define TEXTURE_TYPE_ETC2_SRGB_EAC 0x1166 76 | #define TEXTURE_TYPE_DXT1 0x0200 77 | #define TEXTURE_TYPE_DXT3 0x0261 78 | #define TEXTURE_TYPE_DXT5 0x0262 79 | #define TEXTURE_TYPE_DXT1A 0x0223 80 | #define TEXTURE_TYPE_BPTC 0x0064 81 | #define TEXTURE_TYPE_BPTC_FLOAT 0x4445 82 | #define TEXTURE_TYPE_BPTC_SIGNED_FLOAT 0x4C46 83 | #define TEXTURE_TYPE_RGTC1 0x0001 84 | #define TEXTURE_TYPE_SIGNED_RGTC1 0x0C01 85 | #define TEXTURE_TYPE_RGTC2 0x0041 86 | #define TEXTURE_TYPE_SIGNED_RGTC2 0x0C41 87 | #define TEXTURE_TYPE_RGBA_ASTC_4X4 0x8000 88 | #define TEXTURE_TYPE_RGBA_ASTC_5X4 0x8001 89 | #define TEXTURE_TYPE_RGBA_ASTC_5X5 0x8002 90 | #define TEXTURE_TYPE_RGBA_ASTC_6X5 0x8003 91 | #define TEXTURE_TYPE_RGBA_ASTC_6X6 0x8004 92 | #define TEXTURE_TYPE_RGBA_ASTC_8X5 0x8005 93 | #define TEXTURE_TYPE_RGBA_ASTC_8X6 0x8006 94 | #define TEXTURE_TYPE_RGBA_ASTC_8X8 0x8007 95 | #define TEXTURE_TYPE_RGBA_ASTC_10X5 0x8008 96 | #define TEXTURE_TYPE_RGBA_ASTC_10X6 0x8009 97 | #define TEXTURE_TYPE_RGBA_ASTC_10X8 0x800A 98 | #define TEXTURE_TYPE_RGBA_ASTC_10X10 0x800B 99 | #define TEXTURE_TYPE_RGBA_ASTC_12X10 0x800C 100 | #define TEXTURE_TYPE_RGBA_ASTC_12X12 0x800D 101 | #define TEXTURE_TYPE_SRGB8_ALPHA8_ASTC_4X4 0x9000 102 | 103 | #define TEXTURE_TYPE_ALPHA_BIT 0x0020 104 | #define TEXTURE_TYPE_ETC_BIT 0x0100 105 | #define TEXTURE_TYPE_DXTC_BIT 0x0200 106 | #define TEXTURE_TYPE_128BIT_BIT 0x0040 107 | #define TEXTURE_TYPE_16_BIT_COMPONENTS_BIT 0x0400 108 | #define TEXTURE_TYPE_SIGNED_BIT 0x0800 109 | #define TEXTURE_TYPE_SRGB_BIT 0x1000 110 | #define TEXTURE_TYPE_UNCOMPRESSED_BIT 0x2000 111 | #define TEXTURE_TYPE_HALF_FLOAT_BIT 0x4000 112 | #define TEXTURE_TYPE_ASTC_BIT 0x8000 113 | 114 | typedef struct { 115 | int type; 116 | int ktx_support; 117 | int dds_support; 118 | const char *text1; 119 | const char *text2; 120 | int block_width; // The block width (1 for uncompressed textures). 121 | int block_height; // The block height (1 for uncompressed textures). 122 | int bits_per_block; // The number of bits per block (per pixel for uncompressed textures). 123 | int internal_bits_per_block; // The number of bits per block as stored internally (per pixel for uncompressed). 124 | int alpha_bits; 125 | int nu_components; 126 | int gl_internal_format; 127 | int gl_format; 128 | int gl_type; 129 | const char *dx_four_cc; 130 | int dx10_format; 131 | uint64_t red_mask, green_mask, blue_mask, alpha_mask; 132 | } TextureInfo; 133 | 134 | typedef struct BlockUserData_t BlockUserData; 135 | 136 | typedef int (*TextureDecodingFunction)(const unsigned char *bitstring, unsigned int *image_buffer, int flags); 137 | typedef double (*TextureComparisonFunction)(unsigned int *image_buffer, BlockUserData *user_data); 138 | typedef int (*TextureGetModeFunction)(const unsigned char *bitstring); 139 | typedef void (*TextureSetModeFunction)(unsigned char *bitstring, int flags); 140 | 141 | typedef struct { 142 | unsigned int *pixels; 143 | int width; 144 | int height; 145 | int extended_width; 146 | int extended_height; 147 | int type; 148 | int bits_per_block; // The bits per block of the real format. The internally used bits per block is 149 | // given by info->internal_bits_per_block. 150 | int block_width; 151 | int block_height; 152 | TextureDecodingFunction decoding_function; 153 | TextureComparisonFunction comparison_function; 154 | TextureComparisonFunction perceptive_comparison_function; 155 | TextureGetModeFunction get_mode_function; 156 | TextureSetModeFunction set_mode_function; 157 | TextureInfo *info; 158 | } Texture; 159 | 160 | struct BlockUserData_t { 161 | unsigned int *image_pixels; 162 | int image_rowstride; 163 | int x_offset; 164 | int y_offset; 165 | int flags; 166 | Texture *texture; 167 | unsigned char *alpha_pixels; 168 | unsigned int *colors; 169 | int stop_signalled; 170 | int pass; 171 | unsigned int *texture_pixels; 172 | unsigned int *texture_pixels_above; 173 | unsigned int *texture_pixels_left; 174 | }; 175 | 176 | typedef void (*CompressCallbackFunction)(BlockUserData *user_data); 177 | 178 | // Command line options defined in texgenpack.c 179 | 180 | #define COMMAND_COMPRESS 0 181 | #define COMMAND_DECOMPRESS 1 182 | #define COMMAND_COMPARE 2 183 | #define COMMAND_CALIBRATE 3 184 | 185 | #define ORIENTATION_DOWN 1 186 | #define ORIENTATION_UP 2 187 | 188 | // Compression levels (0 to 50). 189 | 190 | enum { 191 | // Compression level class 0 (levels 0 to 7). 192 | // Compress different blocks concurrently, populations_size = 256, 193 | // nu_generations = 100 + level * 25. 194 | COMPRESSION_LEVEL_CLASS_0 = 0, 195 | // Compression level class 1 (levels 8 to 32) 196 | // Compress the same block concurrently, number of tries is equal to 197 | // level value (8 to 32), nu_generations = 100. 198 | COMPRESSION_LEVEL_CLASS_1 = 8, 199 | // Compression level class 2 (levels 33 to 50) 200 | // Compress the same block concurrently, number of tries is 32, 201 | // nu_generations = 100 + 25 * (level - 32) 202 | COMPRESSION_LEVEL_CLASS_2 = 33, 203 | }; 204 | 205 | // Ultra preset: Compress different blocks concurrenty, nu_generations = 100. 206 | #define SPEED_ULTRA (COMPRESSION_LEVEL_CLASS_0) 207 | // Fast preset: Eight tries per block, nu_generations = 100. 208 | #define SPEED_FAST (COMPRESSION_LEVEL_CLASS_1) 209 | // Medium preset: 16 tries per block, nu_generations = 100. 210 | #define SPEED_MEDIUM (COMPRESSION_LEVEL_CLASS_1 + 8) 211 | // Slow preset 32 tries per block, nu_generations = 100. 212 | #define SPEED_SLOW (COMPRESSION_LEVEL_CLASS_1 + 24) 213 | 214 | extern int command; 215 | extern int option_verbose; 216 | extern int option_max_threads; 217 | extern int option_orientation; 218 | extern int option_compression_level; 219 | extern int option_progress; 220 | extern int option_modal_etc2; 221 | extern int option_allowed_modes_etc2; 222 | extern int option_generations; 223 | extern int option_islands; 224 | extern int option_generations_second_pass; 225 | extern int option_islands_second_pass; 226 | extern int option_texture_format; 227 | extern int option_flip_vertical; 228 | extern int option_quiet; 229 | extern int option_block_width; 230 | extern int option_block_height; 231 | extern int option_half_float; 232 | extern int option_deterministic; 233 | extern int option_hdr; 234 | extern int option_perceptive; 235 | 236 | // Defined in image.c 237 | 238 | void load_image(const char *filename, int filetype, Image *image); 239 | int load_mipmap_images(const char *filename, int filetype, int max_images, Image *image); 240 | void save_image(Image *image, const char *filename, int filetype); 241 | double compare_images(Image *image1, Image *image2); 242 | int load_texture(const char *filename, int filetype, int max_mipmaps, Texture *texture); 243 | void save_texture(Texture *texture, int nu_mipmaps, const char *filename, int filetype); 244 | void convert_texture_to_image(Texture *texture, Image *image); 245 | void destroy_texture(Texture *texture); 246 | void destroy_image(Image *image); 247 | void clone_image(Image *image1, Image *image2); 248 | void clear_image(Image *image); 249 | void pad_image_borders(Image *image); 250 | void check_1bit_alpha(Image *image); 251 | void convert_image_from_srgb_to_rgb(Image *source_image, Image *dest_image); 252 | void convert_image_from_rgb_to_srgb(Image *source_image, Image *dest_image); 253 | void copy_image_to_uncompressed_texture(Image *image, int texture_type, Texture *texture); 254 | void flip_image_vertical(Image *image); 255 | void print_image_info(Image *image); 256 | void calculate_image_dynamic_range(Image *image, float *range_min_out, float *range_max_out); 257 | void convert_image_from_half_float(Image *image, float range_min, float range_max, float gamma); 258 | void convert_image_to_half_float(Image *image); 259 | void extend_half_float_image_to_rgb(Image *image); 260 | void remove_alpha_from_image(Image *image); 261 | void add_alpha_to_image(Image *image); 262 | void convert_image_from_16_bit_format(Image *image); 263 | void convert_image_to_16_bit_format(Image *image, int nu_components, int signed_format); 264 | void convert_image_from_8_bit_format(Image *image); 265 | void convert_image_to_8_bit_format(Image *image, int nu_components, int signed_format); 266 | 267 | // Defined in compress.c 268 | 269 | void compress_image(Image *image, int texture_type, CompressCallbackFunction func, Texture *texture, 270 | int genetic_parameters, float mutation_prob, float crossover_prob); 271 | 272 | // Defined in mipmap.c 273 | 274 | void generate_mipmap_level_from_original(Image *source_image, int level, Image *dest_image); 275 | void generate_mipmap_level_from_previous_level(Image *source_image, Image *dest_image); 276 | int count_mipmap_levels(Image *image); 277 | 278 | // Defined in file.c 279 | 280 | void load_pkm_file(const char *filename, Texture *texture); 281 | void save_pkm_file(Texture *texture, const char *fikename); 282 | int load_ktx_file(const char *filename, int max_mipmaps, Texture *texture); 283 | void save_ktx_file(Texture *texture, int nu_mipmaps, const char *filename); 284 | int load_dds_file(const char *filename, int max_mipmaps, Texture *texture); 285 | void save_dds_file(Texture *texture, int nu_mipmaps, const char *filename); 286 | void load_astc_file(const char *filename, Texture *texture); 287 | void save_astc_file(Texture *texture, const char *filename); 288 | void load_ppm_file(const char *filename, Image *image); 289 | void load_png_file(const char *filename, Image *image); 290 | void save_png_file(Image *image, const char *filename); 291 | 292 | // Defined in texture.c 293 | 294 | TextureInfo *match_texture_type(int type); 295 | TextureInfo *match_texture_description(const char *s); 296 | TextureInfo *match_ktx_id(int gl_internal_format, int gl_format, int gl_type); 297 | TextureInfo *match_dds_id(const char *four_cc, int dx10_format, uint32_t pixel_format_flags, int bitcount, 298 | uint32_t red_mask, uint32_t green_mask, uint32_t blue_mask, uint32_t alpha_mask); 299 | const char *texture_type_text(int texture_type); 300 | int get_number_of_texture_formats(); 301 | const char *get_texture_format_index_text(int i, int j); 302 | void set_texture_decoding_function(Texture *texture, Image *image); 303 | 304 | // Defined in compare.c 305 | 306 | extern float *half_float_table; 307 | extern float *gamma_corrected_half_float_table; 308 | extern float *normalized_float_table; 309 | 310 | double compare_block_any_size_rgba(unsigned int *image_buffer, BlockUserData *user_data); 311 | double compare_block_4x4_rgb(unsigned int *image_buffer, BlockUserData *user_data); 312 | double compare_block_perceptive_4x4_rgb(unsigned int *image_buffer, BlockUserData *user_data); 313 | double compare_block_4x4_rgba(unsigned int *image_buffer, BlockUserData *user_data); 314 | double compare_block_perceptive_4x4_rgba(unsigned int *image_buffer, BlockUserData *user_data); 315 | void calculate_normalized_float_table(); 316 | double compare_block_4x4_rgb8_with_half_float(unsigned int *image_buffer, BlockUserData *user_data); 317 | double compare_block_4x4_rgba8_with_half_float(unsigned int *image_buffer, BlockUserData *user_data); 318 | double compare_block_4x4_8_bit_components(unsigned int *image_buffer, BlockUserData *user_data); 319 | double compare_block_4x4_signed_8_bit_components(unsigned int *image_buffer, BlockUserData *user_data); 320 | double compare_block_4x4_8_bit_components_with_16_bit(unsigned int *image_buffer, BlockUserData *user_data); 321 | double compare_block_4x4_signed_8_bit_components_with_16_bit(unsigned int *image_buffer, BlockUserData *user_data); 322 | double compare_block_4x4_r16(unsigned int *image_buffer, BlockUserData *user_data); 323 | double compare_block_4x4_rg16(unsigned int *image_buffer, BlockUserData *user_data); 324 | double compare_block_4x4_r16_signed(unsigned int *image_buffer, BlockUserData *user_data); 325 | double compare_block_4x4_rg16_signed(unsigned int *image_buffer, BlockUserData *user_data); 326 | void calculate_half_float_table(); 327 | double compare_block_4x4_rgb_half_float(unsigned int *image_buffer, BlockUserData *user_data); 328 | double compare_block_4x4_rgba_half_float(unsigned int *image_buffer, BlockUserData *user_data); 329 | double compare_block_4x4_r_half_float(unsigned int *image_buffer, BlockUserData *user_data); 330 | double compare_block_4x4_rg_half_float(unsigned int *image_buffer, BlockUserData *user_data); 331 | void calculate_gamma_corrected_half_float_table(); 332 | double compare_block_4x4_rgb_half_float_hdr(unsigned int *image_buffer, BlockUserData *user_data); 333 | double compare_block_4x4_rgba_half_float_hdr(unsigned int *image_buffer, BlockUserData *user_data); 334 | 335 | // Defined in half_float.c 336 | 337 | int halfp2singles(void *target, void *source, int numel); 338 | int singles2halfp(void *target, void *source, int numel); 339 | 340 | // Defined in calibrate.c 341 | 342 | void calibrate_genetic_parameters(Image *image, int texture_type); 343 | 344 | -------------------------------------------------------------------------------- /texgenpack.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual C++ Express 2010 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "texgenpack", "texgenpack.vcxproj", "{9497C7AC-6366-4FA5-8AA2-143C99F1CBA5}" 5 | EndProject 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "texview", "texview\texview.vcxproj", "{66020A2C-3CDA-4EA8-9D0C-3C1F91253227}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Release|Win32 = Release|Win32 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {9497C7AC-6366-4FA5-8AA2-143C99F1CBA5}.Debug|Win32.ActiveCfg = Debug|Win32 15 | {9497C7AC-6366-4FA5-8AA2-143C99F1CBA5}.Debug|Win32.Build.0 = Debug|Win32 16 | {9497C7AC-6366-4FA5-8AA2-143C99F1CBA5}.Release|Win32.ActiveCfg = Release|Win32 17 | {9497C7AC-6366-4FA5-8AA2-143C99F1CBA5}.Release|Win32.Build.0 = Release|Win32 18 | {66020A2C-3CDA-4EA8-9D0C-3C1F91253227}.Debug|Win32.ActiveCfg = Debug|Win32 19 | {66020A2C-3CDA-4EA8-9D0C-3C1F91253227}.Debug|Win32.Build.0 = Debug|Win32 20 | {66020A2C-3CDA-4EA8-9D0C-3C1F91253227}.Release|Win32.ActiveCfg = Release|Win32 21 | {66020A2C-3CDA-4EA8-9D0C-3C1F91253227}.Release|Win32.Build.0 = Release|Win32 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | EndGlobal 27 | -------------------------------------------------------------------------------- /texgenpack.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {9497C7AC-6366-4FA5-8AA2-143C99F1CBA5} 15 | Win32Proj 16 | texgenpack 17 | 18 | 19 | 20 | Application 21 | true 22 | Unicode 23 | 24 | 25 | Application 26 | false 27 | true 28 | Unicode 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | true 42 | 43 | 44 | false 45 | 46 | 47 | 48 | 49 | 50 | Level3 51 | Disabled 52 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 53 | false 54 | StreamingSIMDExtensions2 55 | CompileAsCpp 56 | $(SolutionDir)\..\libfgen;$(SolutionDir)\..\lpng160 57 | 58 | 59 | Console 60 | true 61 | 62 | 63 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);libfgen.lib;libpng16.lib;pthreadVC2.lib 64 | 65 | 66 | 67 | 68 | Level3 69 | 70 | 71 | MaxSpeed 72 | true 73 | true 74 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 75 | $(SolutionDir)\..\libfgen;$(SolutionDir)\..\lpng160 76 | false 77 | StreamingSIMDExtensions2 78 | CompileAsCpp 79 | MultiThreadedDLL 80 | 81 | 82 | Console 83 | true 84 | true 85 | true 86 | NotSet 87 | LIBCMT;%(IgnoreSpecificDefaultLibraries) 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 | -------------------------------------------------------------------------------- /texgenpack.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files 41 | 42 | 43 | Source Files 44 | 45 | 46 | Source Files 47 | 48 | 49 | Source Files 50 | 51 | 52 | Source Files 53 | 54 | 55 | Source Files 56 | 57 | 58 | Source Files 59 | 60 | 61 | 62 | 63 | Header Files 64 | 65 | 66 | Header Files 67 | 68 | 69 | Header Files 70 | 71 | 72 | Header Files 73 | 74 | 75 | 76 | 77 | Resource Files 78 | 79 | 80 | Resource Files 81 | 82 | 83 | Resource Files 84 | 85 | 86 | Resource Files 87 | 88 | 89 | -------------------------------------------------------------------------------- /texture.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015 Harm Hanemaaijer 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "texgenpack.h" 26 | #include "decode.h" 27 | #include "packing.h" 28 | #ifndef __GNUC__ 29 | #include "strcasecmp.h" 30 | #endif 31 | 32 | /* 33 | 34 | The TextureInfo structure has the following fields: 35 | 36 | int type; // The texture type. Various properties of the texture type can be derived from bits in the texture type. 37 | int ktx_support; // Whether the program supports loading/saving this format in ktx files. 38 | int dds_support; // Whether the program supports loading/saving this format in dds files. 39 | const char *text1; // A short text identifier, also used with the --format option. 40 | const char *text2; // An alternative identifier. 41 | int block_width; // The block width (1 for uncompressed textures). 42 | int block_height; // The block height (1 for uncompressed textures). 43 | int bits_per_block; // The number of bits per block for compressed textures, the number of bits per pixel for uncompressed textures. 44 | int internal_bits_per_block; // The number of bits per block as stored internally, the number of internal bits per pixel for uncompressed textures. 45 | int alpha_bits; // The number of alpha bits per pixel in the format. 46 | int nu_components; // The number of pixel components including alpha. 47 | int gl_internal_format; // The OpenGL glInternalFormat identifier for this texture type. 48 | int gl_format; // The matching glFormat. 49 | int gl_type; // The matching glType. 50 | const char *dx_four_cc; // The DDS file four character code matching this texture type. If "DX10", dx10_format is valid. 51 | int dx10_format; // The DX10 format identifier matching this texture type. 52 | uint64_t red_mask, green_mask, blue_mask, alpha_mask; // The bitmasks of the pixel components. 53 | 54 | There is also a synonym table for KTX and DDS file formats with alternative IDs. When loading a texture file 55 | the synonyms will be recognized and treated as the corresponding texture type in the primary table. When 56 | saving a file the format used will be that of the primary table. 57 | 58 | For uncompressed textures with 8-bit components, the internal texture storage is 32-bit. For uncompressed 59 | textures with half-float components, the internal texture storage is 64-bit, irrespective of the number of components. 60 | For other uncompressed textures with 16-bit components, the internal texture storage is 32-bit. 61 | */ 62 | 63 | 64 | #define NU_ITEMS 51 65 | 66 | static TextureInfo texture_info[NU_ITEMS] = { 67 | // Texture type Support text1, text2 Block size Comp. OpenGL ID in KTX files DDS file IDs Masks 68 | // internalFormat, format, type FourCC, DX10 format red, green, blue, alpha 69 | { TEXTURE_TYPE_UNCOMPRESSED_RGB8, 1, 1, "rgb8", "", 1, 1, 24, 32, 0, 3, 0x1907, 0x1907, 0x1401, "", 0, 0xFF, 0xFF00, 0xFF0000, 0 }, 70 | { TEXTURE_TYPE_UNCOMPRESSED_RGBA8, 1, 1, "rgba8", "", 1, 1, 32, 32, 8, 4, 0x1908, 0x1908, 0x1401, "DX10", 28, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 }, 71 | { TEXTURE_TYPE_UNCOMPRESSED_ARGB8, 0, 1, "argb8", "", 1, 1, 32, 32, 8, 4, 0, 0, 0, "", 0, 0xFF00, 0xFF0000, 0xFF000000, 0xFF }, 72 | { TEXTURE_TYPE_UNCOMPRESSED_RGB_HALF_FLOAT, 1, 0, "rgb_half_float", "", 1, 1, 48, 64, 0, 3, 0x1907, 0x1907, 0x140B, "", 0, 0xFFFF, 0xFFFF0000, 0xFFFF00000000, 0 }, 73 | { TEXTURE_TYPE_UNCOMPRESSED_RGBA_HALF_FLOAT, 1, 1, "rgba_half_float", "", 1, 1, 64, 64, 16, 4, 0x1908, 0x1908, 0x140B, "DX10", 10, 0xFFFF, 0xFFFF0000, 0xFFFF00000000, 0xFFFF000000000000 }, 74 | { TEXTURE_TYPE_UNCOMPRESSED_RG16, 1, 1, "rg16", "", 1, 1, 32, 32, 0, 2, 0x8226, 0x8227, 0x1403, "DX10", 35, 0xFFFF, 0xFFFF0000, 0, 0 }, 75 | { TEXTURE_TYPE_UNCOMPRESSED_SIGNED_RG16, 1, 1, "signed_rg16", "", 1, 1, 32, 32, 0, 2, 0x8F99, 0x8227, 0x1402, "DX10", 37, 0xFFFF, 0xFFFF0000, 0, 0 }, 76 | { TEXTURE_TYPE_UNCOMPRESSED_RG_HALF_FLOAT, 1, 1, "rg_half_float", "", 1, 1, 32, 64, 0, 2, 0x822F, 0x8227, 0x140B, "DX10", 34, 0xFFFF, 0xFFFF0000, 0, 0 }, 77 | { TEXTURE_TYPE_UNCOMPRESSED_RG8, 1, 1, "rg8", "", 1, 1, 16, 32, 0, 2, 0x822B, 0x8227, 0x1401, "DX10", 49, 0xFF, 0xFF00, 0, 0 }, 78 | { TEXTURE_TYPE_UNCOMPRESSED_SIGNED_RG8, 1, 1, "signed_rg8", "", 1, 1, 16, 32, 0, 2, 0x8F95, 0x8227, 0x1400, "DX10", 51, 0xFF, 0xFF00, 0, 0 }, 79 | { TEXTURE_TYPE_UNCOMPRESSED_R16, 1, 1, "r16", "", 1, 1, 16, 32, 0, 1, 0x822A, 0x1903, 0x1403, "DX10", 56, 0xFFFF, 0, 0, 0 }, 80 | { TEXTURE_TYPE_UNCOMPRESSED_SIGNED_R16, 1, 1, "signed_r16", "", 1, 1, 16, 32, 0, 1, 0x8F98, 0x1903, 0x1402, "DX10", 58, 0xFFFF, 0, 0, 0 }, 81 | { TEXTURE_TYPE_UNCOMPRESSED_R_HALF_FLOAT, 1, 1, "r_half_float", "", 1, 1, 16, 64, 0, 1, 0x822D, 0x1903, 0x140B, "DX10", 54, 0xFFFF, 0, 0, 0 }, 82 | { TEXTURE_TYPE_UNCOMPRESSED_R8, 1, 1, "r8", "", 1, 1, 8, 32, 0, 1, 0x8229, 0x1903, 0x1401, "DX10", 61, 0xFF, 0, 0, 0 }, 83 | { TEXTURE_TYPE_UNCOMPRESSED_SIGNED_R8, 1, 1, "signed_r8", "", 1, 1, 8, 32, 0, 1, 0x8F49, 0x1903, 0x1400, "DX10", 63, 0xFF, 0, 0, 0 }, 84 | { TEXTURE_TYPE_ETC1, 1, 0, "etc1", "", 4, 4, 64, 64, 0, 3, 0x8D64, 0, 0, "", 0, 0xFF, 0xFF00, 0xFF0000, 0}, 85 | { TEXTURE_TYPE_ETC2_RGB8, 1, 0, "etc2_rgb8", "etc2", 4, 4, 64, 64, 0, 3, 0x9274, 0, 0, "", 0, 0xFF, 0xFF00, 0xFF0000, 0}, 86 | { TEXTURE_TYPE_ETC2_EAC, 1, 0, "etc2_eac", "eac", 4, 4, 128, 128, 8, 4, 0x9278, 0, 0, "", 0, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 }, 87 | { TEXTURE_TYPE_ETC2_PUNCHTHROUGH, 1, 0, "etc2_punchthrough", "", 4, 4, 64, 64, 1, 4, 0x9275, 0, 0, "", 0, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 }, 88 | { TEXTURE_TYPE_R11_EAC, 1, 0, "r11_eac", "", 4, 4, 64, 64, 0, 1, 0x9270, 0, 0, "", 0, 0xFFFF, 0, 0, 0 }, 89 | { TEXTURE_TYPE_RG11_EAC, 1, 0, "rg11_eac", "", 4, 4, 128, 128, 0, 2, 0x9272, 0, 0, "", 0, 0xFFFF, 0xFFFF0000, 0, 0 }, 90 | { TEXTURE_TYPE_SIGNED_R11_EAC, 1, 0, "signed_r11_eac", "", 4, 4, 64, 64, 0, 1, 0x9271, 0, 0, "", 0, 0xFFFF, 0, 0, 0 }, 91 | { TEXTURE_TYPE_SIGNED_RG11_EAC, 1, 0, "signed_rg11_eac", "", 4, 4, 128, 128, 0, 2, 0x9273, 0, 0, "", 0, 0xFFFF, 0xFFFF0000, 0, 0 }, 92 | { TEXTURE_TYPE_ETC2_SRGB8, 1, 0, "etc2_srgb8", "", 4, 4, 64, 64, 0, 3, 0x9275, 0, 0, "", 0, 0xFF, 0xFF00, 0xFF0000, 0}, 93 | { TEXTURE_TYPE_ETC2_SRGB_EAC, 1, 0, "etc2_sgrb_eac", "", 4, 4, 128, 128, 8, 3, 0x9279, 0, 0, "", 0, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 }, 94 | { TEXTURE_TYPE_ETC2_SRGB_PUNCHTHROUGH, 1, 0, "etc2_sgrb_punchthrough", "", 4, 4, 64, 64, 1, 3, 0x9277, 0, 0, "", 0, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 }, 95 | { TEXTURE_TYPE_DXT1, 1, 1, "dxt1", "bc1", 4, 4, 64, 64, 0, 3, 0x83F0, 0, 0, "DXT1", 0, 0xFF, 0xFF00, 0xFF0000, 0}, 96 | { TEXTURE_TYPE_DXT1A, 1, 1, "dxt1a", "bc1a", 4, 4, 64, 64, 1, 4, 0x83F1, 0, 0, "", 0, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 }, 97 | { TEXTURE_TYPE_DXT3, 1, 1, "dxt3", "bc2", 4, 4, 128, 128, 8, 4, 0x83F2, 0, 0, "DXT3", 0, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 }, 98 | { TEXTURE_TYPE_DXT5, 1, 1, "dxt5", "bc3", 4, 4, 128, 128, 8, 4, 0x83F3, 0, 0, "DXT5", 0, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 }, 99 | { TEXTURE_TYPE_BPTC, 1, 1, "bptc", "bc7", 4, 4, 128, 128, 8, 4, 0x8E8C, 0, 0, "DX10", 98, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 }, 100 | { TEXTURE_TYPE_BPTC_FLOAT, 1, 1, "bptc_float", "bc6h_uf16", 4, 4, 128, 128, 0, 3, 0x8E8F, 0, 0, "DX10", 95, 0xFFFF, 0xFFFF0000, 0xFFFF00000000, 0 }, 101 | { TEXTURE_TYPE_BPTC_SIGNED_FLOAT, 1, 1, "bptc_signed_float", "bc6h_sf16", 4, 4, 128, 128, 0, 3, 0x8E8E, 0, 0, "DX10", 96, 0xFFFF, 0xFFFF0000, 0xFFFF00000000, 0 }, 102 | { TEXTURE_TYPE_RGTC1, 1, 1, "rgtc1", "bc4_unorm", 4, 4, 64, 64, 0, 1, 0x8DBB, 0, 0, "DX10", 80, 0xFFFF, 0, 0, 0 }, 103 | { TEXTURE_TYPE_SIGNED_RGTC1, 1, 1, "signed_rgtc1", "bc4_snorm", 4, 4, 64, 64, 0, 1, 0x8DBC, 0, 0, "DX10", 81, 0xFFFF, 0, 0, 0 }, 104 | { TEXTURE_TYPE_RGTC2, 1, 1, "rgtc2", "bc5_unorm", 4, 4, 128, 128, 0, 2, 0x8DBD, 0, 0, "DX10", 83, 0xFFFF, 0xFFFF0000, 0, 0 }, 105 | { TEXTURE_TYPE_SIGNED_RGTC2, 1, 1, "signed_rgtc2", "bc5_snorm", 4, 4, 128, 128, 0, 2, 0x8DBE, 0, 0, "DX10", 84, 0xFFFF, 0xFFFF0000, 0, 0 }, 106 | { TEXTURE_TYPE_RGBA_ASTC_4X4, 1, 0, "astc_4x4", "", 4, 4, 128, 128, 8, 4, 0x93B0, 0, 0, "", 0, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 }, 107 | { TEXTURE_TYPE_RGBA_ASTC_5X4, 1, 0, "astc_5x4", "", 5, 4, 128, 128, 8, 4, 0x93B1, 0, 0, "", 0, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 }, 108 | { TEXTURE_TYPE_RGBA_ASTC_5X5, 1, 0, "astc_5x5", "", 5, 5, 128, 128, 8, 4, 0x93B2, 0, 0, "", 0, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 }, 109 | { TEXTURE_TYPE_RGBA_ASTC_6X5, 1, 0, "astc_6x4", "", 6, 5, 128, 128, 8, 4, 0x93B3, 0, 0, "", 0, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 }, 110 | { TEXTURE_TYPE_RGBA_ASTC_6X6, 1, 0, "astc_6x6", "", 6, 6, 128, 128, 8, 4, 0x93B4, 0, 0, "", 0, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 }, 111 | { TEXTURE_TYPE_RGBA_ASTC_8X5, 1, 0, "astc_8x5", "", 8, 5, 128, 128, 8, 4, 0x93B5, 0, 0, "", 0, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 }, 112 | { TEXTURE_TYPE_RGBA_ASTC_8X6, 1, 0, "astc_8x6", "", 8, 6, 128, 128, 8, 4, 0x93B6, 0, 0, "", 0, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 }, 113 | { TEXTURE_TYPE_RGBA_ASTC_8X8, 1, 0, "astc_8x8", "", 8, 8, 128, 128, 8, 4, 0x93B7, 0, 0, "", 0, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 }, 114 | { TEXTURE_TYPE_RGBA_ASTC_10X5, 1, 0, "astc_10x5", "", 10, 5, 128, 128, 8, 4, 0x93B8, 0, 0, "", 0, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 }, 115 | { TEXTURE_TYPE_RGBA_ASTC_10X6, 1, 0, "astc_10x6", "", 10, 6, 128, 128, 8, 4, 0x93B9, 0, 0, "", 0, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 }, 116 | { TEXTURE_TYPE_RGBA_ASTC_10X8, 1, 0, "astc_10x8", "", 10, 8, 128, 128, 8, 4, 0x93BA, 0, 0, "", 0, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 }, 117 | { TEXTURE_TYPE_RGBA_ASTC_10X10, 1, 0, "astc_10x10", "", 10, 10, 128, 128, 8, 4, 0x93BB, 0, 0, "", 0, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 }, 118 | { TEXTURE_TYPE_RGBA_ASTC_12X10, 1, 0, "astc_12x10", "", 12, 10, 128, 128, 8, 4, 0x93BC, 0, 0, "", 0, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 }, 119 | { TEXTURE_TYPE_RGBA_ASTC_12X12, 1, 0, "astc_12x12", "", 12, 12, 128, 128, 8, 4, 0x93BD, 0, 0, "", 0, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 }, 120 | }; 121 | 122 | typedef struct { 123 | int type; 124 | int gl_internal_format; 125 | int gl_format; 126 | int gl_type; 127 | } OpenGLTextureFormatSynonym; 128 | 129 | #define NU_OPEN_GL_SYNONYMS 6 130 | 131 | static OpenGLTextureFormatSynonym open_gl_synonym[NU_OPEN_GL_SYNONYMS] = { 132 | { TEXTURE_TYPE_UNCOMPRESSED_RGB8, 0x8051, 0x1907, 0x1401 }, // GL_RGB8 133 | { TEXTURE_TYPE_UNCOMPRESSED_RGBA8, 0x8058, 0x1908, 0x1401 }, // GL_RGBA8 134 | { TEXTURE_TYPE_RGTC1, 0x8C70, 0, 0 }, // LATC1 135 | { TEXTURE_TYPE_SIGNED_RGTC1, 0x8C71, 0, 0 }, // SIGNED_LATC1 136 | { TEXTURE_TYPE_RGTC2, 0x8C72, 0, 0 }, // LATC1 137 | { TEXTURE_TYPE_SIGNED_RGTC2, 0x8C73, 0, 0 }, // SIGNED_LATC1 138 | }; 139 | 140 | typedef struct { 141 | int type; 142 | const char *dx10_four_cc; 143 | int dx10_format; 144 | } DDSTextureFormatSynonym; 145 | 146 | #define NU_DDS_SYNONYMS 22 147 | 148 | static DDSTextureFormatSynonym dds_synonym[NU_DDS_SYNONYMS] = { 149 | { TEXTURE_TYPE_UNCOMPRESSED_RGBA8, "DX10", 27 }, 150 | { TEXTURE_TYPE_UNCOMPRESSED_RGBA8, "DX10", 30 }, 151 | { TEXTURE_TYPE_UNCOMPRESSED_RG16, "DX10", 36 }, 152 | { TEXTURE_TYPE_UNCOMPRESSED_R16, "DX10", 57 }, 153 | { TEXTURE_TYPE_UNCOMPRESSED_SIGNED_RG16, "DX10", 38 }, 154 | { TEXTURE_TYPE_UNCOMPRESSED_SIGNED_R16, "DX10", 59 }, 155 | { TEXTURE_TYPE_UNCOMPRESSED_RG8, "DX10", 50 }, 156 | { TEXTURE_TYPE_UNCOMPRESSED_R8, "DX10", 62 }, 157 | { TEXTURE_TYPE_UNCOMPRESSED_SIGNED_RG8, "DX10", 52 }, 158 | { TEXTURE_TYPE_UNCOMPRESSED_SIGNED_R8, "DX10", 64 }, 159 | { TEXTURE_TYPE_DXT1, "DX10", 70 }, 160 | { TEXTURE_TYPE_DXT1, "DX10", 71 }, 161 | { TEXTURE_TYPE_DXT3, "DX10", 73 }, 162 | { TEXTURE_TYPE_DXT3, "DX10", 74 }, 163 | { TEXTURE_TYPE_DXT5, "DX10", 76 }, 164 | { TEXTURE_TYPE_DXT5, "DX10", 77 }, 165 | { TEXTURE_TYPE_RGTC1, "DX10", 79 }, 166 | { TEXTURE_TYPE_RGTC2, "DX10", 82 }, 167 | { TEXTURE_TYPE_BPTC, "DX10", 97 }, 168 | { TEXTURE_TYPE_BPTC_FLOAT, "DX10", 94 }, 169 | { TEXTURE_TYPE_RGTC1, "ATI1", 0 }, 170 | { TEXTURE_TYPE_RGTC2, "ATI2", 0 } 171 | }; 172 | 173 | TextureInfo *match_texture_type(int type) { 174 | for (int i = 0; i < NU_ITEMS; i++) 175 | if (texture_info[i].type == type) 176 | return &texture_info[i]; 177 | return NULL; 178 | } 179 | 180 | TextureInfo *match_texture_description(const char *s) { 181 | for (int i = 0; i < NU_ITEMS; i++) 182 | if (strcasecmp(texture_info[i].text1, s) == 0 || strcasecmp(texture_info[i].text2, s) == 0) 183 | return &texture_info[i]; 184 | return NULL; 185 | } 186 | 187 | TextureInfo *match_ktx_id(int gl_internal_format, int gl_format, int gl_type) { 188 | for (int i = 0; i < NU_ITEMS; i++) 189 | if (texture_info[i].gl_internal_format != 0 && texture_info[i].gl_internal_format == gl_internal_format) { 190 | if (texture_info[i].gl_format == 0) 191 | return &texture_info[i]; 192 | if (texture_info[i].gl_format == gl_format && texture_info[i].gl_type == gl_type) 193 | return &texture_info[i]; 194 | } 195 | for (int i = 0; i < NU_OPEN_GL_SYNONYMS; i++) 196 | if (open_gl_synonym[i].gl_internal_format == gl_internal_format) { 197 | if (open_gl_synonym[i].gl_format == 0) 198 | return match_texture_type(open_gl_synonym[i].type); 199 | if (open_gl_synonym[i].gl_format == gl_format && open_gl_synonym[i].gl_type == gl_type) 200 | return match_texture_type(open_gl_synonym[i].type); 201 | } 202 | return NULL; 203 | } 204 | 205 | TextureInfo *match_dds_id(const char *four_cc, int dx10_format, uint32_t pixel_format_flags, int bitcount, uint32_t red_mask, uint32_t green_mask, uint32_t blue_mask, uint32_t alpha_mask) { 206 | for (int i = 0; i < NU_ITEMS; i++) 207 | if (strncmp(four_cc, "DX10", 4) == 0) { 208 | if (texture_info[i].dx10_format == dx10_format) 209 | return &texture_info[i]; 210 | } 211 | else 212 | if (texture_info[i].dx_four_cc[0] != '\0' && 213 | strncmp(texture_info[i].dx_four_cc, four_cc, 4) == 0) 214 | return &texture_info[i]; 215 | else { 216 | if (pixel_format_flags & 0x40) { // Uncompressed data 217 | if ((texture_info[i].type & TEXTURE_TYPE_UNCOMPRESSED_BIT) && 218 | texture_info[i].bits_per_block == bitcount && texture_info[i].red_mask == red_mask && 219 | texture_info[i].green_mask == green_mask && ((pixel_format_flags & 0x1) == 0 || 220 | texture_info[i].alpha_mask == alpha_mask)) { 221 | return &texture_info[i]; 222 | } 223 | } 224 | } 225 | for (int i = 0; i < NU_DDS_SYNONYMS; i++) 226 | if (strncmp(four_cc, "DX10", 4) == 0) { 227 | if (dds_synonym[i].dx10_format == dx10_format) 228 | return match_texture_type(dds_synonym[i].type); 229 | } 230 | else if (dds_synonym[i].dx10_four_cc[0] != '\0' && 231 | strncmp(dds_synonym[i].dx10_four_cc, four_cc, 4) == 0) 232 | return match_texture_type(dds_synonym[i].type); 233 | return NULL; 234 | } 235 | 236 | // Return a description of the texture type. 237 | 238 | const char *texture_type_text(int texture_type) { 239 | TextureInfo *info; 240 | info = match_texture_type(texture_type); 241 | if (info == NULL) { 242 | printf("Error -- invalid texture type.\n"); 243 | exit(1); 244 | } 245 | return info->text1; 246 | } 247 | 248 | // Set the texture decoding function and comparison function based on the texture type. 249 | // If image is NULL, use the texture's native format in the comparison function. 250 | // If image if not NULL, and for example in half-float format, use a higher-precision comparison function 251 | // if possible. 252 | 253 | void set_texture_decoding_function(Texture *texture, Image *image) { 254 | TextureDecodingFunction decoding_func; 255 | TextureComparisonFunction comparison_func; 256 | TextureComparisonFunction perceptive_comparison_func = NULL; 257 | TextureGetModeFunction get_mode_func = NULL; 258 | TextureSetModeFunction set_mode_func = NULL; 259 | if (texture->type >= TEXTURE_TYPE_RGBA_ASTC_4X4 && texture->type <= TEXTURE_TYPE_RGBA_ASTC_12X12) { 260 | decoding_func = draw_block_rgba_astc; 261 | comparison_func = compare_block_any_size_rgba; 262 | goto end; 263 | } 264 | else 265 | switch (texture->type) { 266 | case TEXTURE_TYPE_ETC1 : 267 | decoding_func = draw_block4x4_etc1; 268 | get_mode_func = block4x4_etc1_get_mode; 269 | set_mode_func = block4x4_etc1_set_mode; 270 | break; 271 | case TEXTURE_TYPE_ETC2_RGB8 : 272 | case TEXTURE_TYPE_ETC2_SRGB8 : 273 | decoding_func = draw_block4x4_etc2_rgb8; 274 | get_mode_func = block4x4_etc2_rgb8_get_mode; 275 | set_mode_func = block4x4_etc2_rgb8_set_mode; 276 | break; 277 | case TEXTURE_TYPE_ETC2_EAC : 278 | case TEXTURE_TYPE_ETC2_SRGB_EAC : 279 | decoding_func = draw_block4x4_etc2_eac; 280 | get_mode_func = block4x4_etc2_eac_get_mode; 281 | set_mode_func = block4x4_etc2_eac_set_mode; 282 | break; 283 | case TEXTURE_TYPE_ETC2_PUNCHTHROUGH : 284 | case TEXTURE_TYPE_ETC2_SRGB_PUNCHTHROUGH : 285 | decoding_func = draw_block4x4_etc2_punchthrough; 286 | get_mode_func = block4x4_etc2_punchthrough_get_mode; 287 | set_mode_func = block4x4_etc2_punchthrough_set_mode; 288 | break; 289 | case TEXTURE_TYPE_R11_EAC : 290 | decoding_func = draw_block4x4_r11_eac; 291 | break; 292 | case TEXTURE_TYPE_RG11_EAC : 293 | decoding_func = draw_block4x4_rg11_eac; 294 | break; 295 | case TEXTURE_TYPE_SIGNED_R11_EAC : 296 | decoding_func = draw_block4x4_signed_r11_eac; 297 | break; 298 | case TEXTURE_TYPE_SIGNED_RG11_EAC : 299 | decoding_func = draw_block4x4_signed_rg11_eac; 300 | break; 301 | case TEXTURE_TYPE_DXT1 : 302 | decoding_func = draw_block4x4_dxt1; 303 | break; 304 | case TEXTURE_TYPE_DXT3 : 305 | decoding_func = draw_block4x4_dxt3; 306 | break; 307 | case TEXTURE_TYPE_DXT5 : 308 | decoding_func = draw_block4x4_dxt5; 309 | break; 310 | case TEXTURE_TYPE_DXT1A : 311 | decoding_func = draw_block4x4_dxt1a; 312 | break; 313 | case TEXTURE_TYPE_UNCOMPRESSED_RGB8 : 314 | case TEXTURE_TYPE_UNCOMPRESSED_RGBA8 : 315 | case TEXTURE_TYPE_UNCOMPRESSED_RG8 : 316 | case TEXTURE_TYPE_UNCOMPRESSED_R8 : 317 | case TEXTURE_TYPE_UNCOMPRESSED_RG16 : 318 | case TEXTURE_TYPE_UNCOMPRESSED_R16 : 319 | case TEXTURE_TYPE_UNCOMPRESSED_SIGNED_RG8 : 320 | case TEXTURE_TYPE_UNCOMPRESSED_SIGNED_R8 : 321 | case TEXTURE_TYPE_UNCOMPRESSED_SIGNED_RG16 : 322 | case TEXTURE_TYPE_UNCOMPRESSED_SIGNED_R16 : 323 | decoding_func = draw_block4x4_uncompressed; 324 | break; 325 | case TEXTURE_TYPE_UNCOMPRESSED_ARGB8 : 326 | decoding_func = draw_block4x4_argb8; 327 | break; 328 | case TEXTURE_TYPE_UNCOMPRESSED_RGB_HALF_FLOAT : 329 | decoding_func = draw_block4x4_uncompressed_rgb_half_float; 330 | break; 331 | case TEXTURE_TYPE_UNCOMPRESSED_RGBA_HALF_FLOAT : 332 | decoding_func = draw_block4x4_uncompressed_rgba_half_float; 333 | break; 334 | case TEXTURE_TYPE_UNCOMPRESSED_R_HALF_FLOAT : 335 | decoding_func = draw_block4x4_uncompressed_r_half_float; 336 | break; 337 | case TEXTURE_TYPE_UNCOMPRESSED_RG_HALF_FLOAT : 338 | decoding_func = draw_block4x4_uncompressed_rg_half_float; 339 | break; 340 | case TEXTURE_TYPE_BPTC : 341 | decoding_func = draw_block4x4_bptc; 342 | get_mode_func = block4x4_bptc_get_mode; 343 | set_mode_func = block4x4_bptc_set_mode; 344 | break; 345 | case TEXTURE_TYPE_BPTC_FLOAT : 346 | decoding_func = draw_block4x4_bptc_float; 347 | get_mode_func = block4x4_bptc_float_get_mode; 348 | set_mode_func = block4x4_bptc_float_set_mode; 349 | break; 350 | case TEXTURE_TYPE_BPTC_SIGNED_FLOAT : 351 | decoding_func = draw_block4x4_bptc_signed_float; 352 | get_mode_func = block4x4_bptc_float_get_mode; 353 | set_mode_func = block4x4_bptc_float_set_mode; 354 | break; 355 | case TEXTURE_TYPE_RGTC1 : 356 | decoding_func = draw_block4x4_rgtc1; 357 | break; 358 | case TEXTURE_TYPE_SIGNED_RGTC1 : 359 | decoding_func = draw_block4x4_signed_rgtc1; 360 | break; 361 | case TEXTURE_TYPE_RGTC2 : 362 | decoding_func = draw_block4x4_rgtc2; 363 | break; 364 | case TEXTURE_TYPE_SIGNED_RGTC2 : 365 | decoding_func = draw_block4x4_signed_rgtc2; 366 | break; 367 | default : 368 | printf("Error -- no decoding function defined for texture type.\n"); 369 | exit(1); 370 | } 371 | switch (texture->type) { 372 | case TEXTURE_TYPE_UNCOMPRESSED_RGB8 : 373 | case TEXTURE_TYPE_ETC1 : 374 | case TEXTURE_TYPE_ETC2_RGB8 : 375 | case TEXTURE_TYPE_ETC2_SRGB8 : 376 | case TEXTURE_TYPE_DXT1 : 377 | comparison_func = compare_block_4x4_rgb; 378 | perceptive_comparison_func = compare_block_perceptive_4x4_rgb; 379 | break; 380 | case TEXTURE_TYPE_UNCOMPRESSED_RGBA8 : 381 | case TEXTURE_TYPE_UNCOMPRESSED_ARGB8 : 382 | case TEXTURE_TYPE_ETC2_PUNCHTHROUGH : 383 | case TEXTURE_TYPE_ETC2_SRGB_PUNCHTHROUGH : 384 | case TEXTURE_TYPE_ETC2_EAC : 385 | case TEXTURE_TYPE_ETC2_SRGB_EAC : 386 | case TEXTURE_TYPE_DXT3 : 387 | case TEXTURE_TYPE_DXT5 : 388 | case TEXTURE_TYPE_DXT1A : 389 | case TEXTURE_TYPE_BPTC : 390 | comparison_func = compare_block_4x4_rgba; 391 | perceptive_comparison_func = compare_block_perceptive_4x4_rgba; 392 | break; 393 | case TEXTURE_TYPE_UNCOMPRESSED_RG8 : 394 | case TEXTURE_TYPE_UNCOMPRESSED_R8 : 395 | case TEXTURE_TYPE_RGTC1 : 396 | case TEXTURE_TYPE_RGTC2 : 397 | comparison_func = compare_block_4x4_8_bit_components; 398 | break; 399 | case TEXTURE_TYPE_UNCOMPRESSED_SIGNED_RG8 : 400 | case TEXTURE_TYPE_UNCOMPRESSED_SIGNED_R8 : 401 | comparison_func = compare_block_4x4_signed_8_bit_components; 402 | break; 403 | case TEXTURE_TYPE_UNCOMPRESSED_R16 : 404 | case TEXTURE_TYPE_R11_EAC : 405 | comparison_func = compare_block_4x4_r16; 406 | break; 407 | case TEXTURE_TYPE_UNCOMPRESSED_RG16 : 408 | case TEXTURE_TYPE_RG11_EAC : 409 | comparison_func = compare_block_4x4_rg16; 410 | break; 411 | case TEXTURE_TYPE_UNCOMPRESSED_SIGNED_R16 : 412 | case TEXTURE_TYPE_SIGNED_R11_EAC : 413 | case TEXTURE_TYPE_SIGNED_RGTC1 : 414 | comparison_func = compare_block_4x4_r16_signed; 415 | break; 416 | case TEXTURE_TYPE_UNCOMPRESSED_SIGNED_RG16 : 417 | case TEXTURE_TYPE_SIGNED_RG11_EAC : 418 | case TEXTURE_TYPE_SIGNED_RGTC2 : 419 | comparison_func = compare_block_4x4_rg16_signed; 420 | break; 421 | case TEXTURE_TYPE_UNCOMPRESSED_RGB_HALF_FLOAT : 422 | case TEXTURE_TYPE_BPTC_FLOAT : 423 | case TEXTURE_TYPE_BPTC_SIGNED_FLOAT : 424 | if (option_hdr) 425 | comparison_func = compare_block_4x4_rgb_half_float_hdr; 426 | else 427 | comparison_func = compare_block_4x4_rgb_half_float; 428 | break; 429 | case TEXTURE_TYPE_UNCOMPRESSED_RGBA_HALF_FLOAT : 430 | if (option_hdr) 431 | comparison_func = compare_block_4x4_rgba_half_float_hdr; 432 | else 433 | comparison_func = compare_block_4x4_rgba_half_float; 434 | break; 435 | case TEXTURE_TYPE_UNCOMPRESSED_R_HALF_FLOAT : 436 | comparison_func = compare_block_4x4_r_half_float; 437 | break; 438 | case TEXTURE_TYPE_UNCOMPRESSED_RG_HALF_FLOAT : 439 | comparison_func = compare_block_4x4_rg_half_float; 440 | break; 441 | default : 442 | printf("Error -- no block comparison function defined for texture type.\n"); 443 | exit(1); 444 | } 445 | end : 446 | if (image != NULL) { 447 | if (comparison_func == compare_block_4x4_rgb && image->is_half_float) 448 | comparison_func = compare_block_4x4_rgb8_with_half_float; 449 | if (comparison_func == compare_block_4x4_rgba && image->is_half_float) 450 | comparison_func = compare_block_4x4_rgba8_with_half_float; 451 | if (comparison_func == compare_block_4x4_8_bit_components && image->bits_per_component == 16) 452 | comparison_func = compare_block_4x4_8_bit_components_with_16_bit; 453 | if (comparison_func == compare_block_4x4_signed_8_bit_components && image->bits_per_component == 16) 454 | comparison_func = compare_block_4x4_signed_8_bit_components_with_16_bit; 455 | } 456 | texture->decoding_function = decoding_func; 457 | texture->comparison_function = comparison_func; 458 | texture->perceptive_comparison_function = perceptive_comparison_func; 459 | texture->get_mode_function = get_mode_func; 460 | texture->set_mode_function = set_mode_func; 461 | } 462 | 463 | int get_number_of_texture_formats() { 464 | return NU_ITEMS; 465 | } 466 | 467 | const char *get_texture_format_index_text(int i, int j) { 468 | if (j == 0) 469 | return texture_info[i].text1; 470 | else 471 | return texture_info[i].text2; 472 | } 473 | 474 | int draw_block4x4_uncompressed(const unsigned char *bitstring, unsigned int *image_buffer, int flags) { 475 | // Block is one pixel. 476 | image_buffer[0] = *(unsigned int *)&bitstring[0]; 477 | return 1; 478 | } 479 | 480 | int draw_block4x4_argb8(const unsigned char *bitstring, unsigned int *image_buffer, int flags) { 481 | uint32_t pixel = *(uint32_t *)&bitstring[0]; 482 | image_buffer[0] = pack_rgba(pixel_get_a(pixel), pixel_get_r(pixel), pixel_get_g(pixel), pixel_get_b(pixel)); 483 | return 1; 484 | } 485 | 486 | int draw_block4x4_uncompressed_rgb_half_float(const unsigned char *bitstring, unsigned int *image_buffer, int flags) { 487 | // Block is one pixel. 488 | // The image is also in half-float format. 489 | *(uint64_t *)&image_buffer[0] = *(uint64_t *)&bitstring[0]; 490 | return 1; 491 | } 492 | 493 | int draw_block4x4_uncompressed_rgba_half_float(const unsigned char *bitstring, unsigned int *image_buffer, int flags) { 494 | // Block is one pixel. 495 | // The image is also in half-float format. 496 | *(uint64_t *)&image_buffer[0] = *(uint64_t *)&bitstring[0]; 497 | return 1; 498 | } 499 | 500 | int draw_block4x4_uncompressed_r_half_float(const unsigned char *bitstring, unsigned int *image_buffer, int flags) { 501 | *(uint64_t *)&image_buffer[0] = *(uint64_t *)&bitstring[0]; 502 | return 1; 503 | } 504 | 505 | int draw_block4x4_uncompressed_rg_half_float(const unsigned char *bitstring, unsigned int *image_buffer, int flags) { 506 | *(uint64_t *)&image_buffer[0] = *(uint64_t *)&bitstring[0]; 507 | return 1; 508 | } 509 | 510 | -------------------------------------------------------------------------------- /texview/texview.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {66020A2C-3CDA-4EA8-9D0C-3C1F91253227} 15 | Win32Proj 16 | texview 17 | 18 | 19 | 20 | Application 21 | true 22 | Unicode 23 | 24 | 25 | Application 26 | false 27 | true 28 | Unicode 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | true 42 | 43 | 44 | false 45 | 46 | 47 | 48 | 49 | 50 | Level3 51 | Disabled 52 | WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) 53 | 54 | 55 | Windows 56 | true 57 | 58 | 59 | 60 | 61 | Level3 62 | 63 | 64 | MaxSpeed 65 | true 66 | true 67 | WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) 68 | $(SolutionDir)\..\libfgen 69 | false 70 | StreamingSIMDExtensions2 71 | CompileAsCpp 72 | MultiThreadedDLL 73 | 74 | 75 | Console 76 | true 77 | true 78 | true 79 | false 80 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 81 | LIBCMT;%(IgnoreSpecificDefaultLibraries) 82 | NotSet 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | $(SolutionDir)\..\libfgen;$(SolutionDir)\..\lpng160\ 101 | 102 | 103 | $(SolutionDir)\..\libfgen;"c:\opt\GTK+-Bundle-3.6.1\include\gtk-3.0" ;"c:\opt\GTK+-Bundle-3.6.1\include\atk-1.0" ;"c:\opt\GTK+-Bundle-3.6.1\include\cairo" ;"c:\opt\GTK+-Bundle-3.6.1\include\gdk-pixbuf-2.0" ;"c:\opt\GTK+-Bundle-3.6.1\include\glib-2.0" ;"c:\opt\GTK+-Bundle-3.6.1\lib\glib-2.0\include" ;"c:\opt\GTK+-Bundle-3.6.1\include\pango-1.0" ;"c:\opt\GTK+-Bundle-3.6.1\include\pixman-1" ;"c:\opt\GTK+-Bundle-3.6.1\include" ;"c:\opt\GTK+-Bundle-3.6.1\include\freetype2" ;"c:\opt\GTK+-Bundle-3.6.1\include\libpng15" 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 | -------------------------------------------------------------------------------- /texview/texview.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | 35 | 36 | Source Files 37 | 38 | 39 | Source Files 40 | 41 | 42 | Source Files 43 | 44 | 45 | Source Files 46 | 47 | 48 | Source Files 49 | 50 | 51 | Source Files 52 | 53 | 54 | Source Files 55 | 56 | 57 | Source Files 58 | 59 | 60 | Source Files 61 | 62 | 63 | Source Files 64 | 65 | 66 | Source Files 67 | 68 | 69 | Source Files 70 | 71 | 72 | Source Files 73 | 74 | 75 | 76 | 77 | Resource Files 78 | 79 | 80 | Resource Files 81 | 82 | 83 | Resource Files 84 | 85 | 86 | Resource Files 87 | 88 | 89 | Resource Files 90 | 91 | 92 | Resource Files 93 | 94 | 95 | Resource Files 96 | 97 | 98 | Resource Files 99 | 100 | 101 | Resource Files 102 | 103 | 104 | Resource Files 105 | 106 | 107 | Resource Files 108 | 109 | 110 | Resource Files 111 | 112 | 113 | Resource Files 114 | 115 | 116 | Resource Files 117 | 118 | 119 | Resource Files 120 | 121 | 122 | Resource Files 123 | 124 | 125 | Resource Files 126 | 127 | 128 | Resource Files 129 | 130 | 131 | Resource Files 132 | 133 | 134 | Resource Files 135 | 136 | 137 | -------------------------------------------------------------------------------- /viewer.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015 Harm Hanemaaijer 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "texgenpack.h" 28 | #include "packing.h" 29 | #include "viewer.h" 30 | #ifndef __GNUC__ 31 | #include "strcasecmp.h" 32 | #endif 33 | 34 | 35 | // Define the option variables used by the texgenpack modules. 36 | 37 | int command; 38 | int option_verbose = 0; 39 | int option_max_threads = - 1; 40 | int option_orientation = 0; 41 | int option_texture_format = - 1; 42 | int option_compression_level = SPEED_FAST; 43 | int option_progress = 0; 44 | int option_modal_etc2 = 1; 45 | int option_allowed_modes_etc2 = - 1; 46 | int option_mipmaps = 0; 47 | int option_generations = - 1; 48 | int option_islands = - 1; 49 | int option_generations_second_pass = - 1; 50 | int option_islands_second_pass = - 1; 51 | int option_flip_vertical = 0; 52 | int option_quiet = 0; 53 | int option_block_width = 4; 54 | int option_block_height = 4; 55 | int option_half_float = 0; 56 | int option_deterministic = 0; 57 | int option_half_float_fit_to_range = 0; 58 | int option_hdr = 0; 59 | int option_perceptive = 1; 60 | 61 | // Global variables 62 | 63 | int current_nu_image_sets; 64 | Image current_image[2][32]; 65 | Texture current_texture[2][32]; 66 | int current_nu_mipmaps[2]; 67 | int current_file_type[2]; 68 | double current_rmse[32]; 69 | float dynamic_range_min = 0; 70 | float dynamic_range_max = 1.0; 71 | float dynamic_range_gamma = 1.0; 72 | 73 | // Variables used in this module only. 74 | 75 | static char *source_filename[2]; 76 | static int source_filetype[2]; 77 | 78 | static int file_exists(const char *filename) { 79 | FILE *f = fopen(filename, "rb"); 80 | if (f == NULL) 81 | return 0; 82 | fclose(f); 83 | return 1; 84 | } 85 | 86 | static const char *extension[NU_FILE_TYPES] = { 87 | ".png", ".ppm", ".ktx", ".pkm", ".dds", ".astc" }; 88 | 89 | static int file_type_id[NU_FILE_TYPES] = { 90 | FILE_TYPE_PNG, FILE_TYPE_PPM, FILE_TYPE_KTX, FILE_TYPE_PKM, FILE_TYPE_DDS, FILE_TYPE_ASTC }; 91 | 92 | int determine_filename_type(const char *filename) { 93 | if (strlen(filename) < 5) { 94 | printf("Error -- filename %s too short to be a valid file.\n", filename); 95 | exit(1); 96 | } 97 | for (int i = 0; i < NU_FILE_TYPES; i++) 98 | if (strcasecmp(&filename[strlen(filename) - strlen(extension[i])], extension[i]) == 0) 99 | return file_type_id[i]; 100 | return FILE_TYPE_UNDEFINED; 101 | } 102 | 103 | static const char *get_extension(int filetype) { 104 | for (int i = 0; i < NU_FILE_TYPES; i++) { 105 | if (filetype == file_type_id[i]) 106 | return extension[i]; 107 | } 108 | return NULL; 109 | } 110 | 111 | 112 | // Convert the loaded images to Cairo's internal pixel format ARGB8. 113 | // Swap red and blue components. 114 | 115 | static void convert_image_set(int j) { 116 | for (int i = 0; i < current_nu_mipmaps[j]; i++) { 117 | for (int y = 0; y < current_image[j][i].height; y++) 118 | for (int x = 0; x < current_image[j][i].width; x++) { 119 | if (current_image[j][i].is_half_float) { 120 | uint64_t pixel64 = *(uint64_t *)¤t_image[j][i].pixels[(y * 121 | current_image[j][i].extended_width + x) * 2]; 122 | *(uint64_t *)¤t_image[j][i].pixels[(y * current_image[j][i].extended_width 123 | + x) * 2] = pack_rgba16(pixel64_get_b16(pixel64), pixel64_get_g16(pixel64), 124 | pixel64_get_r16(pixel64), pixel64_get_a16(pixel64)); 125 | } 126 | else { 127 | uint32_t pixel = current_image[j][i].pixels[y * 128 | current_image[j][i].extended_width + x]; 129 | current_image[j][i].pixels[y * current_image[j][i].extended_width + x] = 130 | pack_rgba(pixel_get_b(pixel), pixel_get_g(pixel), pixel_get_r(pixel), 131 | pixel_get_a(pixel)); 132 | } 133 | } 134 | } 135 | } 136 | 137 | void destroy_image_set(int j) { 138 | if (j >= current_nu_image_sets) 139 | return; 140 | // Free pixel buffers associated with current_texture[j][i]. 141 | if (current_file_type[j] & FILE_TYPE_TEXTURE_BIT) 142 | for (int i = 0; i < current_nu_mipmaps[j]; i++) 143 | free(current_texture[j][i].pixels); 144 | // Free pixel buffers associated with current_image[j][i]. 145 | if (current_nu_image_sets > j) 146 | for (int i = 0; i < current_nu_mipmaps[j]; i++) 147 | free(current_image[j][i].pixels); 148 | } 149 | 150 | void convert_image_to_or_from_cairo_format(Image *image) { 151 | for (int y = 0; y < image->height; y++) 152 | for (int x = 0; x < image->width; x++) { 153 | if (image->is_half_float) { 154 | uint64_t pixel64 = *(uint64_t *)&image->pixels[(y * 155 | image->extended_width + x) * 2]; 156 | *(uint64_t *)&image->pixels[(y * image->extended_width 157 | + x) * 2] = pack_rgba16(pixel64_get_b16(pixel64), pixel64_get_g16(pixel64), 158 | pixel64_get_r16(pixel64), pixel64_get_a16(pixel64)); 159 | } 160 | else { 161 | uint32_t pixel = image->pixels[y * image->extended_width + x]; 162 | image->pixels[y * image->extended_width + x] = 163 | pack_rgba(pixel_get_b(pixel), pixel_get_g(pixel), pixel_get_r(pixel), 164 | pixel_get_a(pixel)); 165 | } 166 | } 167 | } 168 | 169 | 170 | void calculate_difference_images(Image *source_image1, Image *source_image2, int n, Image *dest_image) { 171 | for (int i = 0; i < n; i++) { 172 | int h = source_image1[i].height; 173 | if (source_image2[i].height < h) 174 | h = source_image2[i].height; 175 | int w = source_image1[i].width; 176 | if (source_image2[i].width < w) 177 | w = source_image2[i].width; 178 | dest_image[i] = source_image1[i]; // Copy fields. 179 | dest_image[i].pixels = (unsigned int *)malloc(h * source_image1[i].extended_width * 4); 180 | Image *source1; 181 | int source1_is_cloned = 0; 182 | Image cloned_image1; 183 | if (source_image1[i].is_half_float) { 184 | dest_image[i].is_half_float = 0; 185 | dest_image[i].bits_per_component = 8; 186 | clone_image(&source_image1[i], &cloned_image1); 187 | convert_image_from_half_float(&cloned_image1, dynamic_range_min, dynamic_range_max, 188 | dynamic_range_gamma); 189 | convert_image_to_or_from_cairo_format(&cloned_image1); 190 | source1 = &cloned_image1; 191 | source1_is_cloned = 1; 192 | } 193 | else 194 | source1 = &source_image1[i]; 195 | Image *source2; 196 | int source2_is_cloned = 0; 197 | Image cloned_image2; 198 | if (source_image2[i].is_half_float) { 199 | clone_image(&source_image2[i], &cloned_image2); 200 | convert_image_from_half_float(&cloned_image2, dynamic_range_min, dynamic_range_max, 201 | dynamic_range_gamma); 202 | convert_image_to_or_from_cairo_format(&cloned_image2); 203 | source2 = &cloned_image2; 204 | source2_is_cloned = 1; 205 | } 206 | else 207 | source2 = &source_image2[i]; 208 | bool have_alpha = source1->alpha_bits > 0 && source2->alpha_bits > 0; 209 | for (int y = 0; y < h; y++) 210 | for (int x = 0; x < w; x++) { 211 | uint32_t pixel1 = source1->pixels[y * source1->extended_width + x]; 212 | uint32_t pixel2 = source2->pixels[y * source2->extended_width + x]; 213 | int difference = 0; 214 | bool add_rgb_diff = true; 215 | if (have_alpha) { 216 | int a1 = pixel_get_a(pixel1); 217 | int a2 = pixel_get_a(pixel2); 218 | if ((a1 | a2) == 0) 219 | add_rgb_diff = false; 220 | else 221 | difference += abs(a1 - a2); 222 | } 223 | else 224 | add_rgb_diff = true; 225 | if (add_rgb_diff) 226 | difference += abs(pixel_get_r(pixel1) - pixel_get_r(pixel2)) + 227 | abs(pixel_get_g(pixel1) - pixel_get_g(pixel2)) + 228 | abs(pixel_get_b(pixel1) - pixel_get_b(pixel2)); 229 | if (difference > 255) 230 | difference = 255; 231 | dest_image[i].pixels[y * dest_image[i].extended_width + x] = 232 | pack_rgb_alpha_0xff(difference, difference, difference); 233 | } 234 | if (source1_is_cloned) 235 | destroy_image(source1); 236 | if (source2_is_cloned) 237 | destroy_image(source2); 238 | } 239 | } 240 | 241 | int main(int argc, char **argv) { 242 | gui_initialize(&argc, &argv); 243 | gui_create_window_layout(); 244 | gui_draw_window(); 245 | 246 | if (argc == 2 && strcmp(argv[1], "--help") == 0) { 247 | printf("texview -- a texture and image file viewer.\n" 248 | "Usage: texview \n" 249 | " is optional.\n"); 250 | exit(1); 251 | } 252 | 253 | option_quiet = 1; 254 | current_nu_image_sets = 0; 255 | 256 | if (argc >= 2) { 257 | source_filename[0] = argv[1]; 258 | source_filetype[0] = determine_filename_type(source_filename[0]); 259 | 260 | if (!(source_filetype[0] & FILE_TYPE_TEXTURE_BIT) && !(source_filetype[0] & FILE_TYPE_IMAGE_BIT)) { 261 | printf("Error -- expected image or texture filename as argument.\n"); 262 | exit(1); 263 | } 264 | if (!file_exists(source_filename[0])) { 265 | printf("Error -- source file %s doesn't exist or is unreadable.\n", source_filename[0]); 266 | exit(1); 267 | } 268 | current_nu_image_sets = 1; 269 | } 270 | 271 | if (argc >= 3) { 272 | source_filename[1] = argv[2]; 273 | source_filetype[1] = determine_filename_type(source_filename[1]); 274 | 275 | if (!(source_filetype[1] & FILE_TYPE_TEXTURE_BIT) && !(source_filetype[1] & FILE_TYPE_IMAGE_BIT)) { 276 | printf("Error -- expected image or texture filename as second argument.\n"); 277 | exit(1); 278 | } 279 | 280 | if (!file_exists(source_filename[1])) { 281 | printf("Error -- source file %s doesn't exist or is unreadable.\n", source_filename[1]); 282 | exit(1); 283 | } 284 | 285 | current_nu_image_sets = 2; 286 | } 287 | 288 | for (int j = 0; j < current_nu_image_sets; j++) { 289 | current_file_type[j] = source_filetype[j]; 290 | if (source_filetype[j] & FILE_TYPE_TEXTURE_BIT) { 291 | current_nu_mipmaps[j] = load_texture(source_filename[j], source_filetype[j], 32, 292 | ¤t_texture[j][0]); 293 | for (int i = 0 ; i < current_nu_mipmaps[j]; i++) 294 | convert_texture_to_image(¤t_texture[j][i], ¤t_image[j][i]); 295 | } 296 | else { 297 | load_image(source_filename[j], source_filetype[j], ¤t_image[j][0]); 298 | current_nu_mipmaps[j] = 1; 299 | } 300 | if (current_nu_mipmaps[j] > 1) 301 | printf("Found %d mipmap levels.\n", current_nu_mipmaps[j]); 302 | } 303 | 304 | current_rmse[0] = -1.0; 305 | if (current_nu_image_sets > 1) 306 | current_rmse[0] = compare_images(¤t_image[0][0], ¤t_image[1][0]); 307 | 308 | for (int i = 0; i < current_nu_image_sets; i++) { 309 | if (!current_image[i][0].is_half_float && 310 | current_image[i][0].bits_per_component == 8 && 311 | current_image[i][0].nu_components > 2) 312 | convert_image_set(i); 313 | } 314 | gui_zoom_fit_to_window(); 315 | for (int i = 0; i < current_nu_image_sets; i++) 316 | gui_create_base_surface(i); 317 | gui_draw_and_show_window(); 318 | gui_run(); 319 | } 320 | 321 | -------------------------------------------------------------------------------- /viewer.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2015 Harm Hanemaaijer 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | */ 18 | 19 | // Defined in viewer.c 20 | 21 | extern int current_nu_image_sets; 22 | extern Image current_image[2][32]; 23 | extern Texture current_texture[2][32]; 24 | extern int current_nu_mipmaps[2]; 25 | extern int current_file_type[2]; 26 | extern double current_rmse[32]; 27 | extern float dynamic_range_min, dynamic_range_max, dynamic_range_gamma; 28 | 29 | int determine_filename_type(const char *filename); 30 | void calculate_difference_images(Image *source_image1, Image *source_image2, int n, Image *dest_image); 31 | void convert_image_to_or_from_cairo_format(Image *image); 32 | void destroy_image_set(int j); 33 | 34 | // Defined in gtk.c. 35 | 36 | void gui_initialize(int *argc, char ***argv); 37 | void gui_create_window_layout(); 38 | void gui_run(); 39 | void gui_handle_events(); 40 | void gui_draw_window(); 41 | void gui_draw_and_show_window(); 42 | void gui_create_base_surface(int i); 43 | void gui_zoom_fit_to_window(); 44 | 45 | --------------------------------------------------------------------------------