--- /dev/null
+// nbvtf.h - v1.03 - Writer for Valve Texture Format - public domain
+// Written by Harry 'hgn' Godden
+//
+// Credits:
+// Rich Geldreich - bc7enc (BC1/BC3 High Quality texture compression)
+// Fabian "ryg" Giesen, stb - stb_dxt (BC1/BC3 realtime compressors)
+// Sean Barrett - stb_image.h, stb_image_write.h (Image I/O)
+//
+// Note:
+// This library assumes normal maps are input in OpenGL(correct) format, and will be converted into DirectX(incorrect) at
+// compile time automatically. Do not submit DirectX normals into this software.
+//
+// Since this project uses stb_image, use '#define STB_IMAGE_IMPLEMENTATION' before including
+// Additionally, to use high quality DXT, '#define USE_LIBRGBCX'
+//
+// USAGE:
+// Call these functions:
+// int nbvtf_convert( const char *src, int w, int h, int mipmap, EImageFormat_t format, uint32_t usr_flags, const char *dest );
+// int nbvtf_write( uint8_t *src, int w, int h, int mipmap, EImageFormat_t format, uint32_t usr_flags, const char *dest );
+//
+// Param definitions:
+// src - RGBA byte data of image
+// w - width of image
+// h - height of image
+// mipmap - enable mipmap generation (box filter), 1/0
+// format - Choose from: k_EImageFormat_DXT1, compressedk_EImageFormat_DXT5, k_EImageFormat_BGR888, k_EImageFormat_ABGR8888
+// usr_flags - You can append any flags but only really need TEXTUREFLAGS_NORMAL if texture is normal map
+// dest - file path to write vtf to
+// qual - Image quality 0-18 (rgbcx, stb always highqual)
+//
+// Convert specific:
+// src - file path of source image to convert
+// w, h - MAXIMUM image dimentions of final product. Set as 0 to be automatic
+//
+// version history:
+// v1.03 - Added quality switch
+// v1.02 - Improved box filtering, small bug fixes
+// v1.01 - switch to OpenGL normal format for input
+// v1.00 - (hgn) first release
+//
+// LICENSE
+// See end of file for license information.
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NBVTF_MAX_MIPLEVELS 9
+#define nbvtf__min(a,b) (((a)<(b))?(a):(b))
+#define nbvtf__max(a,b) (((a)>(b))?(a):(b))
+
+#ifdef NBVTF_AS_SO
+ #include <stdio.h>
+ #include <string.h>
+ #include <stdint.h>
+ #include <math.h>
+
+ #define NBVTF_SHOW_STDERR
+ #define STB_IMAGE_IMPLEMENTATION
+#endif
+
+#include "stb/stb_image.h"
+
+#ifdef USE_LIBRGBCX
+ #include "librgbcx.h"
+#else
+ #define STB_DXT_IMPLEMENTATION
+ #include "stb/stb_dxt.h"
+#endif
+
+#ifdef NBVTF_SHOW_STDERR
+ #define NBVTF_ERR(...)
+#else
+ #define NBVTF_ERR(...) fprintf( stderr, __VA_ARGS__ )
+#endif
+
+#pragma pack(push, 1)
+
+typedef enum EImageFormat
+{
+ // Name // Supported?
+ k_EImageFormat_NONE = -1,
+ k_EImageFormat_RGBA8888 = 0, // YES
+ k_EImageFormat_ABGR8888,
+ k_EImageFormat_RGB888, // YES
+ k_EImageFormat_BGR888,
+ k_EImageFormat_RGB565,
+ k_EImageFormat_I8,
+ k_EImageFormat_IA88,
+ k_EImageFormat_P8,
+ k_EImageFormat_A8,
+ k_EImageFormat_RGB888_BLUESCREEN,
+ k_EImageFormat_BGR888_BLUESCREEN,
+ k_EImageFormat_ARGB8888,
+ k_EImageFormat_BGRA8888,
+ k_EImageFormat_DXT1, // YES
+ k_EImageFormat_DXT3,
+ k_EImageFormat_DXT5, // YES
+ k_EImageFormat_BGRX8888,
+ k_EImageFormat_BGR565,
+ k_EImageFormat_BGRX5551,
+ k_EImageFormat_BGRA4444,
+ k_EImageFormat_DXT1_ONEBITALPHA,
+ k_EImageFormat_BGRA5551,
+ k_EImageFormat_UV88,
+ k_EImageFormat_UVWQ8888,
+ k_EImageFormat_RGBA16161616F,
+ k_EImageFormat_RGBA16161616,
+ k_EImageFormat_UVLX8888
+} EImageFormat_t;
+
+const char *vtf_format_strings[] =
+{
+ // Name // Supported?
+ "RGBA8888",
+ "ABGR8888",
+ "RGB888",
+ "BGR888",
+ "RGB565",
+ "I8",
+ "IA88",
+ "P8",
+ "A8",
+ "RGB888_BLUESCREEN",
+ "BGR888_BLUESCREEN",
+ "ARGB8888",
+ "BGRA8888",
+#ifdef USE_LIBRGBCX
+ "DXT1 (rgbcx.h)",
+#else
+ "DXT1 (stb_dxt.h)",
+#endif
+ "DXT3",
+#ifdef USE_LIBRGBCX
+ "DXT5 (rgbcx.h)",
+#else
+ "DXT5 (stb_dxt.h)",
+#endif
+ "BGRX8888",
+ "BGR565",
+ "BGRX5551",
+ "BGRA4444",
+ "DXT1_ONEBITALPHA",
+ "BGRA5551",
+ "UV88",
+ "UVWQ8888",
+ "RGBA16161616F",
+ "RGBA16161616",
+ "UVLX8888"
+};
+
+enum flag
+{
+ // Flags from the *.txt config file
+ TEXTUREFLAGS_POINTSAMPLE = 0x00000001,
+ TEXTUREFLAGS_TRILINEAR = 0x00000002,
+ TEXTUREFLAGS_CLAMPS = 0x00000004,
+ TEXTUREFLAGS_CLAMPT = 0x00000008,
+ TEXTUREFLAGS_ANISOTROPIC = 0x00000010,
+ TEXTUREFLAGS_HINT_DXT5 = 0x00000020,
+ TEXTUREFLAGS_PWL_CORRECTED = 0x00000040,
+ TEXTUREFLAGS_NORMAL = 0x00000080,
+ TEXTUREFLAGS_NOMIP = 0x00000100,
+ TEXTUREFLAGS_NOLOD = 0x00000200,
+ TEXTUREFLAGS_ALL_MIPS = 0x00000400,
+ TEXTUREFLAGS_PROCEDURAL = 0x00000800,
+
+ // These are automatically generated by vtex from the texture data.
+ TEXTUREFLAGS_ONEBITALPHA = 0x00001000,
+ TEXTUREFLAGS_EIGHTBITALPHA = 0x00002000,
+
+ // Newer flags from the *.txt config file
+ TEXTUREFLAGS_ENVMAP = 0x00004000,
+ TEXTUREFLAGS_RENDERTARGET = 0x00008000,
+ TEXTUREFLAGS_DEPTHRENDERTARGET = 0x00010000,
+ TEXTUREFLAGS_NODEBUGOVERRIDE = 0x00020000,
+ TEXTUREFLAGS_SINGLECOPY = 0x00040000,
+ TEXTUREFLAGS_PRE_SRGB = 0x00080000,
+
+ TEXTUREFLAGS_UNUSED_00100000 = 0x00100000,
+ TEXTUREFLAGS_UNUSED_00200000 = 0x00200000,
+ TEXTUREFLAGS_UNUSED_00400000 = 0x00400000,
+
+ TEXTUREFLAGS_NODEPTHBUFFER = 0x00800000,
+
+ TEXTUREFLAGS_UNUSED_01000000 = 0x01000000,
+
+ TEXTUREFLAGS_CLAMPU = 0x02000000,
+ TEXTUREFLAGS_VERTEXTEXTURE = 0x04000000,
+ TEXTUREFLAGS_SSBUMP = 0x08000000,
+
+ TEXTUREFLAGS_UNUSED_10000000 = 0x10000000,
+
+ TEXTUREFLAGS_BORDER = 0x20000000,
+
+ TEXTUREFLAGS_UNUSED_40000000 = 0x40000000,
+ TEXTUREFLAGS_UNUSED_80000000 = 0x80000000,
+};
+
+typedef struct vtfheader
+{
+ union
+ {
+ char signature[4]; // File signature ("VTF\0"). (or as little-endian integer, 0x00465456)
+ uint32_t usig;
+ };
+
+ unsigned int version[2]; // version[0].version[1] (currently 7.2).
+ unsigned int headerSize; // Size of the header struct (16 byte aligned; currently 80 bytes) + size of the resources dictionary (7.3+).
+ unsigned short width; // Width of the largest mipmap in pixels. Must be a power of 2.
+ unsigned short height; // Height of the largest mipmap in pixels. Must be a power of 2.
+ unsigned int flags; // VTF flags.
+ unsigned short frames; // Number of frames, if animated (1 for no animation).
+ unsigned short firstFrame; // First frame in animation (0 based).
+ unsigned char padding0[4]; // reflectivity padding (16 byte alignment).
+ float reflectivity[3]; // reflectivity vector.
+ unsigned char padding1[4]; // reflectivity padding (8 byte packing).
+ float bumpmapScale; // Bumpmap scale.
+ unsigned int highResImageFormat; // High resolution image format.
+ unsigned char mipmapCount; // Number of mipmaps.
+ unsigned int lowResImageFormat; // Low resolution image format (always DXT1).
+ unsigned char lowResImageWidth; // Low resolution image width.
+ unsigned char lowResImageHeight; // Low resolution image height.
+
+ // 7.2+
+ unsigned short depth; // Depth of the largest mipmap in pixels.
+ // Must be a power of 2. Can be 0 or 1 for a 2D texture (v7.2 only).
+
+ // 7.3+
+ unsigned char padding2[3]; // depth padding (4 byte alignment).
+ unsigned int numResources; // Number of resources this vtf has
+
+ unsigned char padding3[8]; // Necessary on certain compilers
+} vtfheader_t;
+
+#pragma pack(pop)
+
+typedef struct mipimg
+{
+ uint32_t w;
+ uint32_t h;
+ uint32_t src_offset;
+} mipimg_t;
+
+int nbvtf_power2( uint32_t x )
+{
+ return (x != 0) && ((x & (x - 1)) == 0);
+}
+
+int nbvtf_power2x( uint32_t y, uint32_t x )
+{
+ return nbvtf_power2( y ) && nbvtf_power2( x );
+}
+
+int nbvtf_lower( int *x, int *y )
+{
+ if( *x == 1 && *y == 1 )
+ {
+ return 0;
+ }
+
+ *x = nbvtf__max( 1, (*x)/2 );
+ *y = nbvtf__max( 1, (*y)/2 );
+
+ return 1;
+}
+
+int nbvtf_lowres_index( int w, int h )
+{
+ int x, y;
+ int i = 0;
+
+ x = w;
+ y = h;
+
+ while(1)
+ {
+ if( (x <= 16) && ( y <= 16 ) )
+ {
+ return i;
+ }
+
+ i ++;
+
+ nbvtf_lower( &x, &y );
+ }
+}
+
+// Simple box filter downscale
+void nbvtf_downscale( uint8_t *src, int w, int h, int dw, int dh, uint8_t *dest )
+{
+ int bx = w/dw;
+ int by = h/dh;
+ int div = bx*by;
+
+ for( int y = 0; y < dh; y ++ )
+ for( int x = 0; x < dw; x ++ )
+ {
+ // Average block colours
+ uint32_t tr = 0, tg = 0, tb = 0, ta = 0;
+
+ for( int yy = 0; yy < by; yy ++ )
+ for( int xx = 0; xx < bx; xx ++ )
+ {
+ uint8_t *psrc = &src[ (x*bx+xx + (y*by+yy)*w)*4 ];
+ tr+=psrc[0];
+ tg+=psrc[1];
+ tb+=psrc[2];
+ ta+=psrc[3];
+ }
+
+ uint8_t *pdst = &dest[ (y*dw + x)*4 ];
+ pdst[0] = tr / div;
+ pdst[1] = tg / div;
+ pdst[2] = tb / div;
+ pdst[3] = ta / div;
+ }
+}
+
+uint8_t *nbvtf_create_mipmaps( uint8_t *src, int w, int h, mipimg_t *offsets, int *num )
+{
+ int memory = 0;
+ int x, y, i;
+ uint32_t offset;
+
+ x = w;
+ y = h;
+ while( nbvtf_lower( &x, &y ) )
+ memory += x*y*4;
+
+ uint8_t *mipmem = (uint8_t *)malloc( memory );
+
+ if( mipmem )
+ {
+ x = w;
+ y = h;
+ i = 0;
+ offset = 0;
+
+ uint8_t *dest = mipmem;
+
+ while(1)
+ {
+ if( !nbvtf_lower( &x, &y ) )
+ break;
+
+ nbvtf_downscale( src, w, h, x, y, dest );
+
+ offsets[ i ].src_offset = offset;
+ offsets[ i ].w = x;
+ offsets[ i ].h = y;
+ i ++;
+
+ offset += x*y*4;
+ dest = mipmem + offset;
+ }
+
+ *num = i;
+ return mipmem;
+ }
+ else
+ {
+ NBVTF_ERR( "nbvtf_write:err out of memory allocating mipmap buffer!\n" );
+ return NULL;
+ }
+}
+
+void nbvtf_reflectivity( uint8_t *src, int w, int h, float *dest )
+{
+ uint32_t totals[3] = {0,0,0};
+
+ for( int i = 0; i < w*h; i ++ )
+ {
+ totals[0] += src[i*4+0];
+ totals[1] += src[i*4+1];
+ totals[2] += src[i*4+2];
+ }
+
+ dest[0] = (float)( totals[0] / (w*h) ) / 255.0f;
+ dest[1] = (float)( totals[1] / (w*h) ) / 255.0f;
+ dest[2] = (float)( totals[2] / (w*h) ) / 255.0f;
+}
+
+#ifdef NBVTF_ALLOW_EXPORT
+void nbvtf_debug_view_mips( uint8_t *src, int w, int h )
+{
+ int x, y, i;
+ char fnbuf[512];
+
+ x = w;
+ y = h;
+ i = 1;
+
+ uint8_t *dest = src;
+
+ while( nbvtf_lower( &x, &y ) )
+ {
+ sprintf( fnbuf, "mip_%d.png", i ++ );
+
+ stbi_write_png( fnbuf, x,y, 4, dest, x*4 );
+ dest += x*y*4;
+ }
+}
+#endif
+
+void nbvtf_dxt_pad( uint8_t *src, int bx, int by, int w, int h, uint8_t *dest )
+{
+ int px = bx*4;
+ int py = by*4;
+
+ uint32_t *stream = (uint32_t *)src;
+ uint32_t *stream_out = (uint32_t *)dest;
+
+ for( int y = 0; y < 4; y ++ )
+ {
+ for( int x = 0; x < 4; x ++ )
+ {
+ stream_out[ y*4+x ] = stream[ nbvtf__min( py+y, h-1 )*w + nbvtf__min( px+x, w-1 ) ];
+ }
+ }
+}
+
+uint32_t nbvtf_dxt_sizeimg( int w, int h, int alpha )
+{
+ uint32_t blocks_x, blocks_y;
+ int block_size = alpha? 16: 8;
+
+ blocks_x = ((uint32_t)w) >> 2;
+ blocks_y = ((uint32_t)h) >> 2;
+
+ int padx = w % 4 != 0? 1: 0;
+ int pady = h % 4 != 0? 1: 0;
+
+ return (blocks_x+padx)*(blocks_y+pady)*block_size;
+}
+
+uint32_t nbvtf_sizeimg( int w, int h, EImageFormat_t format )
+{
+ if( format == k_EImageFormat_DXT5 || format == k_EImageFormat_DXT1 )
+ {
+ return nbvtf_dxt_sizeimg( w, h, format == k_EImageFormat_DXT1? 0: 1 );
+ }
+
+ if( format == k_EImageFormat_BGR888 )
+ return w*h*3;
+
+ if( format == k_EImageFormat_ABGR8888 )
+ return w*h*4;
+
+ return 0;
+}
+
+void nbvtf_dxt_block( uint8_t *dest, uint8_t *src, int alpha, int qual )
+{
+#ifdef USE_LIBRGBCX
+ // TODO: move this somewehre else
+ static int init = 0;
+ if( !init )
+ {
+ rgbcx__init();
+ init = 1;
+ }
+
+ if( alpha )
+ {
+ rgbcx__encode_bc3( qual, dest, src );
+ }
+ else
+ {
+ rgbcx__encode_bc1( qual, dest, src, 0, 0 );
+ }
+#else
+ stb_compress_dxt_block( dest, src, alpha, STB_DXT_HIGHQUAL );
+#endif
+}
+
+void nbvtf_compress_dxt( uint8_t *src, int w, int h, int alpha, int qual,
+ uint8_t *dest )
+{
+ uint32_t blocks_x, blocks_y;
+
+ blocks_x = ((uint32_t)w) >> 2;
+ blocks_y = ((uint32_t)h) >> 2;
+
+ int padx = w % 4 != 0? 1: 0;
+ int pady = h % 4 != 0? 1: 0;
+
+ int block_size = alpha? 16: 8;
+
+ uint8_t *dest_block = dest;
+
+ uint8_t working_block[ 4*4*4 ];
+
+ // Compress rows
+ for( int y = 0; y < blocks_y; y ++ )
+ {
+ for( int x = 0; x < blocks_x; x ++ )
+ {
+ uint8_t *src_begin = src + (y*w*4 + x*4)*4;
+ for( int i = 0; i < 4; i ++ )
+ {
+ memcpy( working_block + i*4*4, src_begin + w*4*i, 4*4 );
+ }
+
+ nbvtf_dxt_block( dest_block, working_block, alpha, qual );
+ dest_block += block_size;
+ }
+
+ if( padx )
+ {
+ nbvtf_dxt_pad( src, blocks_x, y, w, h, working_block );
+ nbvtf_dxt_block( dest_block, working_block, alpha, qual );
+ dest_block += block_size;
+ }
+ }
+
+ // Compress remainder row
+ if( pady )
+ {
+ for( int x = 0; x < blocks_x; x ++ )
+ {
+ nbvtf_dxt_pad( src, x, blocks_y, w, h, working_block );
+ nbvtf_dxt_block( dest_block, working_block, alpha, qual );
+ dest_block += block_size;
+ }
+ }
+
+ // Compress last little corner
+ if( padx && pady )
+ {
+ nbvtf_dxt_pad( src, blocks_x, blocks_y, w, h, working_block );
+ nbvtf_dxt_block( dest_block, working_block, alpha, qual );
+ }
+}
+
+void nbvtf_swizzle_to( uint8_t *src, int n, int nc, uint8_t *dest )
+{
+ for( int i = 0; i < n; i ++ )
+ {
+ for( int j = 0; j < nc; j ++ )
+ {
+ dest[ i*nc+nc-j-1 ] = src[ i*4+j ];
+ }
+ }
+}
+
+void nbvtf_write_img_data( uint8_t *src, int w, int h,
+ EImageFormat_t format, int qual, uint8_t *wb, FILE *file )
+{
+ switch( format )
+ {
+ case k_EImageFormat_DXT1:
+ nbvtf_compress_dxt( src, w, h, 0, qual, wb );
+ fwrite( wb, nbvtf_dxt_sizeimg( w, h, 0 ), 1, file );
+ break;
+ case k_EImageFormat_DXT5:
+ nbvtf_compress_dxt( src, w, h, 1, qual, wb );
+ fwrite( wb, nbvtf_dxt_sizeimg( w, h, 1 ), 1, file );
+ break;
+ case k_EImageFormat_ABGR8888:
+ nbvtf_swizzle_to( src, w*h, 4, wb );
+ fwrite( wb, w*h*4, 1, file );
+ break;
+ case k_EImageFormat_BGR888:
+ nbvtf_swizzle_to( src, w*h, 3, wb );
+ fwrite( wb, w*h*3, 1, file );
+ break;
+
+ default:
+ break;
+ }
+}
+
+#ifdef NBVTF_AS_SO
+__attribute__((visibility("default")))
+#endif
+int nbvtf_write( uint8_t *reference, int w, int h, int mipmap,
+ EImageFormat_t format, int qual, uint32_t usr_flags, const char *dest )
+{
+ if( !nbvtf_power2x(w,h) )
+ {
+ NBVTF_ERR( "nbvtf_write:err image dimentions were not power of two (%d %d)\n", w, h );
+ return 0;
+ }
+
+ mipimg_t mip_offsets[ 16 ];
+ int num_mips;
+
+ uint8_t *src;
+
+ // Convert to directx normal
+ if( usr_flags & TEXTUREFLAGS_NORMAL )
+ {
+ src = malloc( w*h*4 );
+ for( int i = 0; i < w*h; i ++ )
+ {
+ src[i*4+0] = reference[i*4+0];
+ src[i*4+1] = 0xFF-reference[i*4+1];
+ src[i*4+2] = reference[i*4+2];
+ src[i*4+3] = reference[i*4+3];
+ }
+ }
+ else
+ src = reference;
+
+ uint8_t *mip_data = nbvtf_create_mipmaps( src, w, h, mip_offsets, &num_mips );
+
+ if( !mip_data )
+ {
+ NBVTF_ERR( "nbvtf_write:err mipmap data failed to generate" );
+
+ if( usr_flags & TEXTUREFLAGS_NORMAL )
+ free( src );
+
+ return 0;
+ }
+
+ vtfheader_t header = {0};
+
+ header.usig = 0x00465456;
+ header.headerSize = sizeof( vtfheader_t );
+ header.version[0] = 7;
+ header.version[1] = 2;
+
+ header.width = w;
+ header.height = h;
+ header.flags = usr_flags;
+
+ // Append format flags
+ if( !mipmap )
+ {
+ header.flags |= TEXTUREFLAGS_NOLOD;
+ header.flags |= TEXTUREFLAGS_NOMIP;
+ }
+
+ if( format == k_EImageFormat_DXT5 || format == k_EImageFormat_ABGR8888 )
+ {
+ header.flags |= TEXTUREFLAGS_EIGHTBITALPHA;
+ }
+
+ header.frames = 1;
+ header.firstFrame = 0;
+ nbvtf_reflectivity( mip_data + mip_offsets[ num_mips-1 ].src_offset, 1,1, header.reflectivity );
+ header.bumpmapScale = 1.0f;
+
+ header.highResImageFormat = format;
+ header.mipmapCount = mipmap?
+ nbvtf__min(num_mips,NBVTF_MAX_MIPLEVELS)+1: 1;
+
+ header.lowResImageFormat = k_EImageFormat_DXT1;
+
+ header.depth = 1;
+ header.numResources = 0;
+
+ int lr_index = nbvtf_lowres_index( w, h );
+
+ uint8_t *lr_src;
+
+ if( lr_index )
+ {
+ mipimg_t *mip = mip_offsets + (lr_index-1);
+ lr_src = mip_data + mip->src_offset;
+
+ header.lowResImageWidth = mip->w;
+ header.lowResImageHeight = mip->h;
+ }
+ else
+ {
+ lr_src = src;
+
+ header.lowResImageWidth = w;
+ header.lowResImageHeight = h;
+ }
+
+ uint32_t size_highres = nbvtf_sizeimg( w, h, format );
+ uint32_t size_lowres =
+ nbvtf_dxt_sizeimg( header.lowResImageWidth, header.lowResImageHeight, 0 );
+
+ uint8_t *working_buffer =
+ (uint8_t *)malloc( nbvtf__max( size_highres, size_lowres ) );
+
+ if( !working_buffer )
+ {
+ NBVTF_ERR( "nbvtf_write:err out of memory allocating working buffer\n" );
+ free( mip_data );
+
+ if( usr_flags & TEXTUREFLAGS_NORMAL )
+ free( src );
+
+ return 0;
+ }
+
+ FILE *file = fopen( dest, "wb" );
+
+ if( !file )
+ {
+ NBVTF_ERR( "nbvtf_write:err could not open file stream for writing\n" );
+
+ free( working_buffer );
+ free( mip_data );
+
+ if( usr_flags & TEXTUREFLAGS_NORMAL )
+ free( src );
+
+ return 0;
+ }
+
+ // Write header
+ fwrite( &header, sizeof( vtfheader_t ), 1, file );
+
+ // Write low res
+ nbvtf_write_img_data(
+ lr_src, header.lowResImageWidth, header.lowResImageHeight,
+ k_EImageFormat_DXT1, qual, working_buffer, file
+ );
+
+ // Write texture data
+ if( mipmap )
+ {
+ // !! Experimental !!
+ int start = nbvtf__max( 0, num_mips-NBVTF_MAX_MIPLEVELS );
+
+ for( int i = start; i < num_mips; i ++ )
+ {
+ mipimg_t *mip = mip_offsets + (num_mips - i -1);
+ nbvtf_write_img_data( mip_data + mip->src_offset, mip->w, mip->h,
+ format, qual, working_buffer, file );
+ }
+ }
+
+ // Write high resolution
+ nbvtf_write_img_data( src, w, h, format, qual, working_buffer, file );
+
+ fclose( file );
+
+ free( working_buffer );
+ free( mip_data );
+
+ if( usr_flags & TEXTUREFLAGS_NORMAL )
+ free( src );
+
+ return 1;
+}
+
+#ifdef NBVTF_AS_SO
+__attribute__((visibility("default")))
+#endif
+int nbvtf_convert( const char *src, int w, int h, int mipmap,
+ EImageFormat_t format, int qual, uint32_t usr_flags, const char *dest )
+{
+ if( (w && h) && !nbvtf_power2x(w,h) )
+ {
+ NBVTF_ERR( "nbvtf_convert:err requested dimentions were not power of two (%d %d)\n", w, h );
+ return 0;
+ }
+
+ int x,y,n;
+ uint8_t *data = stbi_load( src, &x, &y, &n, 4 );
+
+ if( data )
+ {
+ if( !nbvtf_power2x(x,y) )
+ {
+ NBVTF_ERR( "nbvtf_convert:err loaded image dimentions were not power two (%d %d)\n", x, y );
+ stbi_image_free( data );
+ return 0;
+ }
+
+ // Image size needs retargeting
+ if( (w && h) && ( x > w || y > h ) )
+ nbvtf_downscale( data, x, y, w, h, data );
+
+ int status = nbvtf_write( data, w, h, mipmap, format, qual,
+ usr_flags, dest );
+
+ stbi_image_free( data );
+
+ return status;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+/*
+LICENSE - Applies to nbvtf.h, pynbvtf.py, librgbcx.cc, librgbcx.h, vtf_cmd.c
+------------------------------------------------------------------------------
+Public Domain (www.unlicense.org)
+This is free and unencumbered software released into the public domain.
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+software, either in source code form or as a compiled binary, for any purpose,
+commercial or non-commercial, and by any means.
+In jurisdictions that recognize copyright laws, the author or authors of this
+software dedicate any and all copyright interest in the software to the public
+domain. We make this dedication for the benefit of the public at large and to
+the detriment of our heirs and successors. We intend this dedication to be an
+overt act of relinquishment in perpetuity of all present and future rights to
+this software under copyright law.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+------------------------------------------------------------------------------
+*/