Major API revision
[convexer.git] / nbvtf / nbvtf.h
1 // nbvtf.h - v1.03 - Writer for Valve Texture Format - public domain
2 // Written by Harry 'hgn' Godden
3 //
4 // Credits:
5 // Rich Geldreich - bc7enc (BC1/BC3 High Quality texture compression)
6 // Fabian "ryg" Giesen, stb - stb_dxt (BC1/BC3 realtime compressors)
7 // Sean Barrett - stb_image.h, stb_image_write.h (Image I/O)
8 //
9 // Note:
10 // This library assumes normal maps are input in OpenGL(correct) format, and will be converted into DirectX(incorrect) at
11 // compile time automatically. Do not submit DirectX normals into this software.
12 //
13 // Since this project uses stb_image, use '#define STB_IMAGE_IMPLEMENTATION' before including
14 // Additionally, to use high quality DXT, '#define USE_LIBRGBCX'
15 //
16 // USAGE:
17 // Call these functions:
18 // int nbvtf_convert( const char *src, int w, int h, int mipmap, EImageFormat_t format, uint32_t usr_flags, const char *dest );
19 // int nbvtf_write( uint8_t *src, int w, int h, int mipmap, EImageFormat_t format, uint32_t usr_flags, const char *dest );
20 //
21 // Param definitions:
22 // src - RGBA byte data of image
23 // w - width of image
24 // h - height of image
25 // mipmap - enable mipmap generation (box filter), 1/0
26 // format - Choose from: k_EImageFormat_DXT1, compressedk_EImageFormat_DXT5, k_EImageFormat_BGR888, k_EImageFormat_ABGR8888
27 // usr_flags - You can append any flags but only really need TEXTUREFLAGS_NORMAL if texture is normal map
28 // dest - file path to write vtf to
29 // qual - Image quality 0-18 (rgbcx, stb always highqual)
30 //
31 // Convert specific:
32 // src - file path of source image to convert
33 // w, h - MAXIMUM image dimentions of final product. Set as 0 to be automatic
34 //
35 // version history:
36 // v1.03 - Added quality switch
37 // v1.02 - Improved box filtering, small bug fixes
38 // v1.01 - switch to OpenGL normal format for input
39 // v1.00 - (hgn) first release
40 //
41 // LICENSE
42 // See end of file for license information.
43
44 #ifdef __cplusplus
45 extern "C" {
46 #endif
47
48 #define NBVTF_MAX_MIPLEVELS 9
49 #define nbvtf__min(a,b) (((a)<(b))?(a):(b))
50 #define nbvtf__max(a,b) (((a)>(b))?(a):(b))
51
52 #ifdef NBVTF_AS_SO
53 #include <stdio.h>
54 #include <string.h>
55 #include <stdint.h>
56 #include <math.h>
57
58 #define NBVTF_SHOW_STDERR
59 #define STB_IMAGE_IMPLEMENTATION
60 #endif
61
62 #include "stb/stb_image.h"
63
64 #ifdef USE_LIBRGBCX
65 #include "librgbcx.h"
66 #else
67 #define STB_DXT_IMPLEMENTATION
68 #include "stb/stb_dxt.h"
69 #endif
70
71 #ifdef NBVTF_SHOW_STDERR
72 #define NBVTF_ERR(...)
73 #else
74 #define NBVTF_ERR(...) fprintf( stderr, __VA_ARGS__ )
75 #endif
76
77 #pragma pack(push, 1)
78
79 typedef enum EImageFormat
80 {
81 // Name // Supported?
82 k_EImageFormat_NONE = -1,
83 k_EImageFormat_RGBA8888 = 0, // YES
84 k_EImageFormat_ABGR8888,
85 k_EImageFormat_RGB888, // YES
86 k_EImageFormat_BGR888,
87 k_EImageFormat_RGB565,
88 k_EImageFormat_I8,
89 k_EImageFormat_IA88,
90 k_EImageFormat_P8,
91 k_EImageFormat_A8,
92 k_EImageFormat_RGB888_BLUESCREEN,
93 k_EImageFormat_BGR888_BLUESCREEN,
94 k_EImageFormat_ARGB8888,
95 k_EImageFormat_BGRA8888,
96 k_EImageFormat_DXT1, // YES
97 k_EImageFormat_DXT3,
98 k_EImageFormat_DXT5, // YES
99 k_EImageFormat_BGRX8888,
100 k_EImageFormat_BGR565,
101 k_EImageFormat_BGRX5551,
102 k_EImageFormat_BGRA4444,
103 k_EImageFormat_DXT1_ONEBITALPHA,
104 k_EImageFormat_BGRA5551,
105 k_EImageFormat_UV88,
106 k_EImageFormat_UVWQ8888,
107 k_EImageFormat_RGBA16161616F,
108 k_EImageFormat_RGBA16161616,
109 k_EImageFormat_UVLX8888
110 } EImageFormat_t;
111
112 const char *vtf_format_strings[] =
113 {
114 // Name // Supported?
115 "RGBA8888",
116 "ABGR8888",
117 "RGB888",
118 "BGR888",
119 "RGB565",
120 "I8",
121 "IA88",
122 "P8",
123 "A8",
124 "RGB888_BLUESCREEN",
125 "BGR888_BLUESCREEN",
126 "ARGB8888",
127 "BGRA8888",
128 #ifdef USE_LIBRGBCX
129 "DXT1 (rgbcx.h)",
130 #else
131 "DXT1 (stb_dxt.h)",
132 #endif
133 "DXT3",
134 #ifdef USE_LIBRGBCX
135 "DXT5 (rgbcx.h)",
136 #else
137 "DXT5 (stb_dxt.h)",
138 #endif
139 "BGRX8888",
140 "BGR565",
141 "BGRX5551",
142 "BGRA4444",
143 "DXT1_ONEBITALPHA",
144 "BGRA5551",
145 "UV88",
146 "UVWQ8888",
147 "RGBA16161616F",
148 "RGBA16161616",
149 "UVLX8888"
150 };
151
152 enum flag
153 {
154 // Flags from the *.txt config file
155 TEXTUREFLAGS_POINTSAMPLE = 0x00000001,
156 TEXTUREFLAGS_TRILINEAR = 0x00000002,
157 TEXTUREFLAGS_CLAMPS = 0x00000004,
158 TEXTUREFLAGS_CLAMPT = 0x00000008,
159 TEXTUREFLAGS_ANISOTROPIC = 0x00000010,
160 TEXTUREFLAGS_HINT_DXT5 = 0x00000020,
161 TEXTUREFLAGS_PWL_CORRECTED = 0x00000040,
162 TEXTUREFLAGS_NORMAL = 0x00000080,
163 TEXTUREFLAGS_NOMIP = 0x00000100,
164 TEXTUREFLAGS_NOLOD = 0x00000200,
165 TEXTUREFLAGS_ALL_MIPS = 0x00000400,
166 TEXTUREFLAGS_PROCEDURAL = 0x00000800,
167
168 // These are automatically generated by vtex from the texture data.
169 TEXTUREFLAGS_ONEBITALPHA = 0x00001000,
170 TEXTUREFLAGS_EIGHTBITALPHA = 0x00002000,
171
172 // Newer flags from the *.txt config file
173 TEXTUREFLAGS_ENVMAP = 0x00004000,
174 TEXTUREFLAGS_RENDERTARGET = 0x00008000,
175 TEXTUREFLAGS_DEPTHRENDERTARGET = 0x00010000,
176 TEXTUREFLAGS_NODEBUGOVERRIDE = 0x00020000,
177 TEXTUREFLAGS_SINGLECOPY = 0x00040000,
178 TEXTUREFLAGS_PRE_SRGB = 0x00080000,
179
180 TEXTUREFLAGS_UNUSED_00100000 = 0x00100000,
181 TEXTUREFLAGS_UNUSED_00200000 = 0x00200000,
182 TEXTUREFLAGS_UNUSED_00400000 = 0x00400000,
183
184 TEXTUREFLAGS_NODEPTHBUFFER = 0x00800000,
185
186 TEXTUREFLAGS_UNUSED_01000000 = 0x01000000,
187
188 TEXTUREFLAGS_CLAMPU = 0x02000000,
189 TEXTUREFLAGS_VERTEXTEXTURE = 0x04000000,
190 TEXTUREFLAGS_SSBUMP = 0x08000000,
191
192 TEXTUREFLAGS_UNUSED_10000000 = 0x10000000,
193
194 TEXTUREFLAGS_BORDER = 0x20000000,
195
196 TEXTUREFLAGS_UNUSED_40000000 = 0x40000000,
197 TEXTUREFLAGS_UNUSED_80000000 = 0x80000000,
198 };
199
200 typedef struct vtfheader
201 {
202 union
203 {
204 char signature[4]; // File signature ("VTF\0"). (or as little-endian integer, 0x00465456)
205 uint32_t usig;
206 };
207
208 unsigned int version[2]; // version[0].version[1] (currently 7.2).
209 unsigned int headerSize; // Size of the header struct (16 byte aligned; currently 80 bytes) + size of the resources dictionary (7.3+).
210 unsigned short width; // Width of the largest mipmap in pixels. Must be a power of 2.
211 unsigned short height; // Height of the largest mipmap in pixels. Must be a power of 2.
212 unsigned int flags; // VTF flags.
213 unsigned short frames; // Number of frames, if animated (1 for no animation).
214 unsigned short firstFrame; // First frame in animation (0 based).
215 unsigned char padding0[4]; // reflectivity padding (16 byte alignment).
216 float reflectivity[3]; // reflectivity vector.
217 unsigned char padding1[4]; // reflectivity padding (8 byte packing).
218 float bumpmapScale; // Bumpmap scale.
219 unsigned int highResImageFormat; // High resolution image format.
220 unsigned char mipmapCount; // Number of mipmaps.
221 unsigned int lowResImageFormat; // Low resolution image format (always DXT1).
222 unsigned char lowResImageWidth; // Low resolution image width.
223 unsigned char lowResImageHeight; // Low resolution image height.
224
225 // 7.2+
226 unsigned short depth; // Depth of the largest mipmap in pixels.
227 // Must be a power of 2. Can be 0 or 1 for a 2D texture (v7.2 only).
228
229 // 7.3+
230 unsigned char padding2[3]; // depth padding (4 byte alignment).
231 unsigned int numResources; // Number of resources this vtf has
232
233 unsigned char padding3[8]; // Necessary on certain compilers
234 } vtfheader_t;
235
236 #pragma pack(pop)
237
238 typedef struct mipimg
239 {
240 uint32_t w;
241 uint32_t h;
242 uint32_t src_offset;
243 } mipimg_t;
244
245 int nbvtf_power2( uint32_t x )
246 {
247 return (x != 0) && ((x & (x - 1)) == 0);
248 }
249
250 int nbvtf_power2x( uint32_t y, uint32_t x )
251 {
252 return nbvtf_power2( y ) && nbvtf_power2( x );
253 }
254
255 int nbvtf_lower( int *x, int *y )
256 {
257 if( *x == 1 && *y == 1 )
258 {
259 return 0;
260 }
261
262 *x = nbvtf__max( 1, (*x)/2 );
263 *y = nbvtf__max( 1, (*y)/2 );
264
265 return 1;
266 }
267
268 int nbvtf_lowres_index( int w, int h )
269 {
270 int x, y;
271 int i = 0;
272
273 x = w;
274 y = h;
275
276 while(1)
277 {
278 if( (x <= 16) && ( y <= 16 ) )
279 {
280 return i;
281 }
282
283 i ++;
284
285 nbvtf_lower( &x, &y );
286 }
287 }
288
289 // Simple box filter downscale
290 void nbvtf_downscale( uint8_t *src, int w, int h, int dw, int dh, uint8_t *dest )
291 {
292 int bx = w/dw;
293 int by = h/dh;
294 int div = bx*by;
295
296 for( int y = 0; y < dh; y ++ )
297 for( int x = 0; x < dw; x ++ )
298 {
299 // Average block colours
300 uint32_t tr = 0, tg = 0, tb = 0, ta = 0;
301
302 for( int yy = 0; yy < by; yy ++ )
303 for( int xx = 0; xx < bx; xx ++ )
304 {
305 uint8_t *psrc = &src[ (x*bx+xx + (y*by+yy)*w)*4 ];
306 tr+=psrc[0];
307 tg+=psrc[1];
308 tb+=psrc[2];
309 ta+=psrc[3];
310 }
311
312 uint8_t *pdst = &dest[ (y*dw + x)*4 ];
313 pdst[0] = tr / div;
314 pdst[1] = tg / div;
315 pdst[2] = tb / div;
316 pdst[3] = ta / div;
317 }
318 }
319
320 uint8_t *nbvtf_create_mipmaps( uint8_t *src, int w, int h, mipimg_t *offsets, int *num )
321 {
322 int memory = 0;
323 int x, y, i;
324 uint32_t offset;
325
326 x = w;
327 y = h;
328 while( nbvtf_lower( &x, &y ) )
329 memory += x*y*4;
330
331 uint8_t *mipmem = (uint8_t *)malloc( memory );
332
333 if( mipmem )
334 {
335 x = w;
336 y = h;
337 i = 0;
338 offset = 0;
339
340 uint8_t *dest = mipmem;
341
342 while(1)
343 {
344 if( !nbvtf_lower( &x, &y ) )
345 break;
346
347 nbvtf_downscale( src, w, h, x, y, dest );
348
349 offsets[ i ].src_offset = offset;
350 offsets[ i ].w = x;
351 offsets[ i ].h = y;
352 i ++;
353
354 offset += x*y*4;
355 dest = mipmem + offset;
356 }
357
358 *num = i;
359 return mipmem;
360 }
361 else
362 {
363 NBVTF_ERR( "nbvtf_write:err out of memory allocating mipmap buffer!\n" );
364 return NULL;
365 }
366 }
367
368 void nbvtf_reflectivity( uint8_t *src, int w, int h, float *dest )
369 {
370 uint32_t totals[3] = {0,0,0};
371
372 for( int i = 0; i < w*h; i ++ )
373 {
374 totals[0] += src[i*4+0];
375 totals[1] += src[i*4+1];
376 totals[2] += src[i*4+2];
377 }
378
379 dest[0] = (float)( totals[0] / (w*h) ) / 255.0f;
380 dest[1] = (float)( totals[1] / (w*h) ) / 255.0f;
381 dest[2] = (float)( totals[2] / (w*h) ) / 255.0f;
382 }
383
384 #ifdef NBVTF_ALLOW_EXPORT
385 void nbvtf_debug_view_mips( uint8_t *src, int w, int h )
386 {
387 int x, y, i;
388 char fnbuf[512];
389
390 x = w;
391 y = h;
392 i = 1;
393
394 uint8_t *dest = src;
395
396 while( nbvtf_lower( &x, &y ) )
397 {
398 sprintf( fnbuf, "mip_%d.png", i ++ );
399
400 stbi_write_png( fnbuf, x,y, 4, dest, x*4 );
401 dest += x*y*4;
402 }
403 }
404 #endif
405
406 void nbvtf_dxt_pad( uint8_t *src, int bx, int by, int w, int h, uint8_t *dest )
407 {
408 int px = bx*4;
409 int py = by*4;
410
411 uint32_t *stream = (uint32_t *)src;
412 uint32_t *stream_out = (uint32_t *)dest;
413
414 for( int y = 0; y < 4; y ++ )
415 {
416 for( int x = 0; x < 4; x ++ )
417 {
418 stream_out[ y*4+x ] = stream[ nbvtf__min( py+y, h-1 )*w + nbvtf__min( px+x, w-1 ) ];
419 }
420 }
421 }
422
423 uint32_t nbvtf_dxt_sizeimg( int w, int h, int alpha )
424 {
425 uint32_t blocks_x, blocks_y;
426 int block_size = alpha? 16: 8;
427
428 blocks_x = ((uint32_t)w) >> 2;
429 blocks_y = ((uint32_t)h) >> 2;
430
431 int padx = w % 4 != 0? 1: 0;
432 int pady = h % 4 != 0? 1: 0;
433
434 return (blocks_x+padx)*(blocks_y+pady)*block_size;
435 }
436
437 uint32_t nbvtf_sizeimg( int w, int h, EImageFormat_t format )
438 {
439 if( format == k_EImageFormat_DXT5 || format == k_EImageFormat_DXT1 )
440 {
441 return nbvtf_dxt_sizeimg( w, h, format == k_EImageFormat_DXT1? 0: 1 );
442 }
443
444 if( format == k_EImageFormat_BGR888 )
445 return w*h*3;
446
447 if( format == k_EImageFormat_ABGR8888 )
448 return w*h*4;
449
450 return 0;
451 }
452
453 void nbvtf_dxt_block( uint8_t *dest, uint8_t *src, int alpha, int qual )
454 {
455 #ifdef USE_LIBRGBCX
456 // TODO: move this somewehre else
457 static int init = 0;
458 if( !init )
459 {
460 rgbcx__init();
461 init = 1;
462 }
463
464 if( alpha )
465 {
466 rgbcx__encode_bc3( qual, dest, src );
467 }
468 else
469 {
470 rgbcx__encode_bc1( qual, dest, src, 0, 0 );
471 }
472 #else
473 stb_compress_dxt_block( dest, src, alpha, STB_DXT_HIGHQUAL );
474 #endif
475 }
476
477 void nbvtf_compress_dxt( uint8_t *src, int w, int h, int alpha, int qual,
478 uint8_t *dest )
479 {
480 uint32_t blocks_x, blocks_y;
481
482 blocks_x = ((uint32_t)w) >> 2;
483 blocks_y = ((uint32_t)h) >> 2;
484
485 int padx = w % 4 != 0? 1: 0;
486 int pady = h % 4 != 0? 1: 0;
487
488 int block_size = alpha? 16: 8;
489
490 uint8_t *dest_block = dest;
491
492 uint8_t working_block[ 4*4*4 ];
493
494 // Compress rows
495 for( int y = 0; y < blocks_y; y ++ )
496 {
497 for( int x = 0; x < blocks_x; x ++ )
498 {
499 uint8_t *src_begin = src + (y*w*4 + x*4)*4;
500 for( int i = 0; i < 4; i ++ )
501 {
502 memcpy( working_block + i*4*4, src_begin + w*4*i, 4*4 );
503 }
504
505 nbvtf_dxt_block( dest_block, working_block, alpha, qual );
506 dest_block += block_size;
507 }
508
509 if( padx )
510 {
511 nbvtf_dxt_pad( src, blocks_x, y, w, h, working_block );
512 nbvtf_dxt_block( dest_block, working_block, alpha, qual );
513 dest_block += block_size;
514 }
515 }
516
517 // Compress remainder row
518 if( pady )
519 {
520 for( int x = 0; x < blocks_x; x ++ )
521 {
522 nbvtf_dxt_pad( src, x, blocks_y, w, h, working_block );
523 nbvtf_dxt_block( dest_block, working_block, alpha, qual );
524 dest_block += block_size;
525 }
526 }
527
528 // Compress last little corner
529 if( padx && pady )
530 {
531 nbvtf_dxt_pad( src, blocks_x, blocks_y, w, h, working_block );
532 nbvtf_dxt_block( dest_block, working_block, alpha, qual );
533 }
534 }
535
536 void nbvtf_swizzle_to( uint8_t *src, int n, int nc, uint8_t *dest )
537 {
538 for( int i = 0; i < n; i ++ )
539 {
540 for( int j = 0; j < nc; j ++ )
541 {
542 dest[ i*nc+nc-j-1 ] = src[ i*4+j ];
543 }
544 }
545 }
546
547 void nbvtf_write_img_data( uint8_t *src, int w, int h,
548 EImageFormat_t format, int qual, uint8_t *wb, FILE *file )
549 {
550 switch( format )
551 {
552 case k_EImageFormat_DXT1:
553 nbvtf_compress_dxt( src, w, h, 0, qual, wb );
554 fwrite( wb, nbvtf_dxt_sizeimg( w, h, 0 ), 1, file );
555 break;
556 case k_EImageFormat_DXT5:
557 nbvtf_compress_dxt( src, w, h, 1, qual, wb );
558 fwrite( wb, nbvtf_dxt_sizeimg( w, h, 1 ), 1, file );
559 break;
560 case k_EImageFormat_ABGR8888:
561 nbvtf_swizzle_to( src, w*h, 4, wb );
562 fwrite( wb, w*h*4, 1, file );
563 break;
564 case k_EImageFormat_BGR888:
565 nbvtf_swizzle_to( src, w*h, 3, wb );
566 fwrite( wb, w*h*3, 1, file );
567 break;
568
569 default:
570 break;
571 }
572 }
573
574 #ifdef NBVTF_AS_SO
575 __attribute__((visibility("default")))
576 #endif
577 int nbvtf_write( uint8_t *reference, int w, int h, int mipmap,
578 EImageFormat_t format, int qual, uint32_t usr_flags, const char *dest )
579 {
580 if( !nbvtf_power2x(w,h) )
581 {
582 NBVTF_ERR( "nbvtf_write:err image dimentions were not power of two (%d %d)\n", w, h );
583 return 0;
584 }
585
586 mipimg_t mip_offsets[ 16 ];
587 int num_mips;
588
589 uint8_t *src;
590
591 // Convert to directx normal
592 if( usr_flags & TEXTUREFLAGS_NORMAL )
593 {
594 src = malloc( w*h*4 );
595 for( int i = 0; i < w*h; i ++ )
596 {
597 src[i*4+0] = reference[i*4+0];
598 src[i*4+1] = 0xFF-reference[i*4+1];
599 src[i*4+2] = reference[i*4+2];
600 src[i*4+3] = reference[i*4+3];
601 }
602 }
603 else
604 src = reference;
605
606 uint8_t *mip_data = nbvtf_create_mipmaps( src, w, h, mip_offsets, &num_mips );
607
608 if( !mip_data )
609 {
610 NBVTF_ERR( "nbvtf_write:err mipmap data failed to generate" );
611
612 if( usr_flags & TEXTUREFLAGS_NORMAL )
613 free( src );
614
615 return 0;
616 }
617
618 vtfheader_t header = {0};
619
620 header.usig = 0x00465456;
621 header.headerSize = sizeof( vtfheader_t );
622 header.version[0] = 7;
623 header.version[1] = 2;
624
625 header.width = w;
626 header.height = h;
627 header.flags = usr_flags;
628
629 // Append format flags
630 if( !mipmap )
631 {
632 header.flags |= TEXTUREFLAGS_NOLOD;
633 header.flags |= TEXTUREFLAGS_NOMIP;
634 }
635
636 if( format == k_EImageFormat_DXT5 || format == k_EImageFormat_ABGR8888 )
637 {
638 header.flags |= TEXTUREFLAGS_EIGHTBITALPHA;
639 }
640
641 header.frames = 1;
642 header.firstFrame = 0;
643 nbvtf_reflectivity( mip_data + mip_offsets[ num_mips-1 ].src_offset, 1,1, header.reflectivity );
644 header.bumpmapScale = 1.0f;
645
646 header.highResImageFormat = format;
647 header.mipmapCount = mipmap?
648 nbvtf__min(num_mips,NBVTF_MAX_MIPLEVELS)+1: 1;
649
650 header.lowResImageFormat = k_EImageFormat_DXT1;
651
652 header.depth = 1;
653 header.numResources = 0;
654
655 int lr_index = nbvtf_lowres_index( w, h );
656
657 uint8_t *lr_src;
658
659 if( lr_index )
660 {
661 mipimg_t *mip = mip_offsets + (lr_index-1);
662 lr_src = mip_data + mip->src_offset;
663
664 header.lowResImageWidth = mip->w;
665 header.lowResImageHeight = mip->h;
666 }
667 else
668 {
669 lr_src = src;
670
671 header.lowResImageWidth = w;
672 header.lowResImageHeight = h;
673 }
674
675 uint32_t size_highres = nbvtf_sizeimg( w, h, format );
676 uint32_t size_lowres =
677 nbvtf_dxt_sizeimg( header.lowResImageWidth, header.lowResImageHeight, 0 );
678
679 uint8_t *working_buffer =
680 (uint8_t *)malloc( nbvtf__max( size_highres, size_lowres ) );
681
682 if( !working_buffer )
683 {
684 NBVTF_ERR( "nbvtf_write:err out of memory allocating working buffer\n" );
685 free( mip_data );
686
687 if( usr_flags & TEXTUREFLAGS_NORMAL )
688 free( src );
689
690 return 0;
691 }
692
693 FILE *file = fopen( dest, "wb" );
694
695 if( !file )
696 {
697 NBVTF_ERR( "nbvtf_write:err could not open file stream for writing\n" );
698
699 free( working_buffer );
700 free( mip_data );
701
702 if( usr_flags & TEXTUREFLAGS_NORMAL )
703 free( src );
704
705 return 0;
706 }
707
708 // Write header
709 fwrite( &header, sizeof( vtfheader_t ), 1, file );
710
711 // Write low res
712 nbvtf_write_img_data(
713 lr_src, header.lowResImageWidth, header.lowResImageHeight,
714 k_EImageFormat_DXT1, qual, working_buffer, file
715 );
716
717 // Write texture data
718 if( mipmap )
719 {
720 // !! Experimental !!
721 int start = nbvtf__max( 0, num_mips-NBVTF_MAX_MIPLEVELS );
722
723 for( int i = start; i < num_mips; i ++ )
724 {
725 mipimg_t *mip = mip_offsets + (num_mips - i -1);
726 nbvtf_write_img_data( mip_data + mip->src_offset, mip->w, mip->h,
727 format, qual, working_buffer, file );
728 }
729 }
730
731 // Write high resolution
732 nbvtf_write_img_data( src, w, h, format, qual, working_buffer, file );
733
734 fclose( file );
735
736 free( working_buffer );
737 free( mip_data );
738
739 if( usr_flags & TEXTUREFLAGS_NORMAL )
740 free( src );
741
742 return 1;
743 }
744
745 #ifdef NBVTF_AS_SO
746 __attribute__((visibility("default")))
747 #endif
748 int nbvtf_convert( const char *src, int w, int h, int mipmap,
749 EImageFormat_t format, int qual, uint32_t usr_flags, const char *dest )
750 {
751 if( (w && h) && !nbvtf_power2x(w,h) )
752 {
753 NBVTF_ERR( "nbvtf_convert:err requested dimentions were not power of two (%d %d)\n", w, h );
754 return 0;
755 }
756
757 int x,y,n;
758 uint8_t *data = stbi_load( src, &x, &y, &n, 4 );
759
760 if( data )
761 {
762 if( !nbvtf_power2x(x,y) )
763 {
764 NBVTF_ERR( "nbvtf_convert:err loaded image dimentions were not power two (%d %d)\n", x, y );
765 stbi_image_free( data );
766 return 0;
767 }
768
769 // Image size needs retargeting
770 if( (w && h) && ( x > w || y > h ) )
771 nbvtf_downscale( data, x, y, w, h, data );
772
773 int status = nbvtf_write( data, w, h, mipmap, format, qual,
774 usr_flags, dest );
775
776 stbi_image_free( data );
777
778 return status;
779 }
780 else
781 {
782 return 0;
783 }
784 }
785
786 #ifdef __cplusplus
787 }
788 #endif
789
790 /*
791 LICENSE - Applies to nbvtf.h, pynbvtf.py, librgbcx.cc, librgbcx.h, vtf_cmd.c
792 ------------------------------------------------------------------------------
793 Public Domain (www.unlicense.org)
794 This is free and unencumbered software released into the public domain.
795 Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
796 software, either in source code form or as a compiled binary, for any purpose,
797 commercial or non-commercial, and by any means.
798 In jurisdictions that recognize copyright laws, the author or authors of this
799 software dedicate any and all copyright interest in the software to the public
800 domain. We make this dedication for the benefit of the public at large and to
801 the detriment of our heirs and successors. We intend this dedication to be an
802 overt act of relinquishment in perpetuity of all present and future rights to
803 this software under copyright law.
804 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
805 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
806 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
807 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
808 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
809 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
810 ------------------------------------------------------------------------------
811 */