/* lutconv2.c : Improved, general purpose LUT converter. Turns 2D LUT textures into DDS Volume Maps for convenience. (C)2017 Marisa Kirisame, UnSX Team. Released under the GNU General Public License version 3 (or later). */ #include #include #include #include #include #include #define dword uint32_t #define byte uint8_t #pragma pack(push,1) typedef struct { dword magic, size, flags, height, width, pitch, depth, mipmaps, reserved1[11], pf_size, pf_flags, pf_fourcc, pf_bitcount, pf_rmask, pf_gmask, pf_bmask, pf_amask, caps[4], reserved2; } ddsheader_t; #pragma pack(pop) ddsheader_t ddshead = { .magic = 0x20534444, .size = 124, .flags = 0x0080100f, .height = 0, .width = 0, .pitch = 0, .depth = 0, .mipmaps = 0, .reserved1 = {0,0,0,0,0,0,0,0,0,0,0}, .pf_size = 32, .pf_flags = 0x41, .pf_fourcc = 0, .pf_bitcount = 32, .pf_rmask = 0xff, .pf_gmask = 0xff00, .pf_bmask = 0xff0000, .pf_amask = 0xff000000, .caps = {0x1000,0x200000,0,0}, .reserved2 = 0 }; uint8_t *indata, *outdata; int width, height, depth, alpha; /* vertical strip LUT, the format I use */ int vertconv( void ) { int x, y, z, i, s; ddshead.pf_flags = 0x41; ddshead.pf_bitcount = 32; /* guess the format */ if ( (width == 8) && (height == 4096) ) { /* VGA 8x16 font table */ printf("conv: VGA 8x16 font table\n"); ddshead.width = width = 8; ddshead.height = height = 16; ddshead.depth = depth = 256; ddshead.pitch = 8*4; s = width*height*depth*4; outdata = malloc(s); i = 0; x = 0; while ( i < s ) { outdata[i++] = indata[x++]; outdata[i++] = indata[x++]; outdata[i++] = indata[x++]; if ( alpha ) outdata[i++] = indata[x++]; else outdata[i++] = 255; } return 1; } else if ( (width == 16) && (height == 256) ) { /* unpadded 16x16x16 LUT */ printf("conv: vertical 16x16x16 LUT\n"); ddshead.width = width = 16; ddshead.height = height = 16; ddshead.depth = depth = 16; ddshead.pitch = 16*4; s = width*height*depth*4; outdata = malloc(s); i = 0; x = 0; while ( i < s ) { outdata[i++] = indata[x++]; outdata[i++] = indata[x++]; outdata[i++] = indata[x++]; if ( alpha ) outdata[i++] = indata[x++]; else outdata[i++] = 255; } return 1; } else if ( (width == 32) && (height == 512) ) { /* padded 16x16x16 LUT */ printf("conv: vertical padded 16x16x16 LUT\n"); ddshead.width = width = 16; ddshead.height = height = 16; ddshead.depth = depth = 16; ddshead.pitch = 16*4; s = width*height*depth*4; outdata = malloc(s); i = 0; x = 8; y = 8; z = 0; while ( i < s ) { outdata[i++] = indata[(x+y*32+z*512)*(3+alpha)]; outdata[i++] = indata[(x+y*32+z*512)*(3+alpha)+1]; outdata[i++] = indata[(x+y*32+z*512)*(3+alpha)+2]; if ( alpha ) outdata[i++] = indata[(x+y*32+z*512) *(3+alpha)+3]; else outdata[i++] = 255; x++; if ( x >= 24 ) { x = 8; y++; } if ( y >= 24 ) { y = 8; z++; } } return 1; } else if ( (width == 64) && (height == 64) ) { /* square 16x16x16 LUT (assuming column-major grid) */ printf("conv: column-major 4x4 grid 16x16x16 LUT\n"); ddshead.width = width = 16; ddshead.height = height = 16; ddshead.depth = depth = 16; ddshead.pitch = 16*4; s = width*height*depth*4; outdata = malloc(s); i = 0; x = 0; y = 0; z = 0; while ( i < s ) { outdata[i++] = indata[(x+y*64+(z&0x3)*1024 +(z&0xc)*4)*(3+alpha)]; outdata[i++] = indata[(x+y*64+(z&0x3)*1024 +(z&0xc)*4)*(3+alpha)+1]; outdata[i++] = indata[(x+y*64+(z&0x3)*1024 +(z&0xc)*4)*(3+alpha)+2]; if ( alpha ) outdata[i++] = indata[indata[(x+y*64+(z&0x3) *1024+(z&0xc)*4)*(3+alpha)+3]]; else outdata[i++] = 255; x++; if ( x >= 16 ) { x = 0; y++; } if ( y >= 16 ) { y = 0; z++; } } return 1; } else if ( (width == 64) && (height == 4096) ) { /* unpadded 64x64x64 LUT */ printf("conv: vertical 64x64x64 LUT\n"); ddshead.width = width = 64; ddshead.height = height = 64; ddshead.depth = depth = 64; ddshead.pitch = 64*4; s = width*height*depth*4; outdata = malloc(s); i = 0; x = 0; while ( i < s ) { outdata[i++] = indata[x++]; outdata[i++] = indata[x++]; outdata[i++] = indata[x++]; if ( alpha ) outdata[i++] = indata[x++]; else outdata[i++] = 255; } return 1; } else if ( (width == 128) && (height == 8192) ) { /* padded 64x64x64 LUT */ printf("conv: vertical padded 64x64x64 LUT\n"); ddshead.width = width = 64; ddshead.height = height = 64; ddshead.depth = depth = 64; ddshead.pitch = 64*4; s = width*height*depth*4; outdata = malloc(s); i = 0; x = 32; y = 32; z = 0; while ( i < s ) { outdata[i++] = indata[(x+y*128+z*16384)*(3+alpha)]; outdata[i++] = indata[(x+y*128+z*16384)*(3+alpha)+1]; outdata[i++] = indata[(x+y*128+z*16384)*(3+alpha)+2]; if ( alpha ) outdata[i++] = indata[(x+y*128+z*16384) *(3+alpha)+3]; else outdata[i++] = 255; x++; if ( x >= 96 ) { x = 32; y++; } if ( y >= 96 ) { y = 32; z++; } } return 1; } else if ( (width == 512) && (height == 512) ) { /* square 64x64x64 LUT (assuming column-major grid) */ printf("conv: column-major 8x8 grid 64x64x64 LUT\n"); ddshead.width = width = 64; ddshead.height = height = 64; ddshead.depth = depth = 64; ddshead.pitch = 64*4; s = width*height*depth*4; outdata = malloc(s); i = 0; x = 0; y = 0; z = 0; while ( i < s ) { outdata[i++] = indata[(x+y*512+(z&0x7)*32768 +(z&0x38)*8)*(3+alpha)]; outdata[i++] = indata[(x+y*512+(z&0x7)*32768 +(z&0x38)*8)*(3+alpha)+1]; outdata[i++] = indata[(x+y*512+(z&0x7)*32768 +(z&0x38)*8)*(3+alpha)+2]; if ( alpha ) outdata[i++] = indata[indata[(x+y*512+(z&0x7) *32768+(z&0x38)*16)*(3+alpha)+3]]; else outdata[i++] = 255; x++; if ( x >= 64 ) { x = 0; y++; } if ( y >= 64 ) { y = 0; z++; } } return 1; } else if ( (width == 256) && (height == 65536) ) { /* unpadded 256x256x256 LUT (rare) */ printf("conv: vertical 256x256x256 LUT\n"); ddshead.width = width = 256; ddshead.height = height = 256; ddshead.depth = depth = 256; ddshead.pitch = 256*4; s = width*height*depth*4; outdata = malloc(s); i = 0; x = 0; while ( i < s ) { outdata[i++] = indata[x++]; outdata[i++] = indata[x++]; outdata[i++] = indata[x++]; if ( alpha ) outdata[i++] = indata[x++]; else outdata[i++] = 255; } return 1; } else if ( (width == 512) && (height == 131072) ) { /* padded 256x256x256 LUT (very rare) */ printf("conv: vertical padded 256x256x256 LUT\n"); ddshead.width = width = 256; ddshead.height = height = 256; ddshead.depth = depth = 256; ddshead.pitch = 256*4; s = width*height*depth*4; outdata = malloc(s); i = 0; x = 128; y = 128; z = 0; while ( i < s ) { outdata[i++] = indata[(x+y*512+z*262144)*(3+alpha)]; outdata[i++] = indata[(x+y*512+z*262144)*(3+alpha)+1]; outdata[i++] = indata[(x+y*512+z*262144)*(3+alpha)+2]; if ( alpha ) outdata[i++] = indata[(x+y*512+z*262144) *(3+alpha)+3]; else outdata[i++] = 255; x++; if ( x >= 384 ) { x = 128; y++; } if ( y >= 384 ) { y = 128; z++; } } return 1; } else if ( (width == 4096) && (height == 4096) ) { /* square 256x256x256 LUT (assuming column-major grid) */ printf("conv: column-major 16x16 grid 256x256x256 LUT\n"); ddshead.width = width = 256; ddshead.height = height = 256; ddshead.depth = depth = 256; ddshead.pitch = 256*4; s = width*height*depth*4; outdata = malloc(s); i = 0; x = 0; y = 0; z = 0; while ( i < s ) { outdata[i++] = indata[(x+y*4096+(z&0x0F)*1048576 +(z&0xF0)*16)*(3+alpha)]; outdata[i++] = indata[(x+y*4096+(z&0x0F)*1048576 +(z&0xF0)*16)*(3+alpha)+1]; outdata[i++] = indata[(x+y*4096+(z&0x0F)*1048576 +(z&0xF0)*16)*(3+alpha)+2]; if ( alpha ) outdata[i++] = indata[indata[(x+y*4096+(z&0x0F) *1048576+(z&0xF0)*16)*(3+alpha)+3]]; else outdata[i++] = 255; x++; if ( x >= 256 ) { x = 0; y++; } if ( y >= 256 ) { y = 0; z++; } } return 1; } printf("conv: Never heard of a LUT texture of %ux%u\n",width,height); return 0; } /* horizontal strip LUT, what everyone else except me uses */ int horconv( void ) { int x, y, z, i, s; ddshead.pf_flags = 0x41; ddshead.pf_bitcount = 32; /* guess the format */ if ( (width == 256) && (height == 16) ) { /* 16x16x16 LUT */ printf("conv: horizontal 16x16x16 LUT\n"); ddshead.width = width = 16; ddshead.height = height = 16; ddshead.depth = depth = 16; ddshead.pitch = 16*4; s = width*height*depth*4; outdata = malloc(s); i = 0; x = 0; y = 0; z = 0; while ( i < s ) { outdata[i++] = indata[(x+y*256+z*16)*(3+alpha)]; outdata[i++] = indata[(x+y*256+z*16)*(3+alpha)+1]; outdata[i++] = indata[(x+y*256+z*16)*(3+alpha)+2]; if ( alpha ) outdata[i++] = indata[(x+y*256+z*16) *(3+alpha)+3]; else outdata[i++] = 255; x++; if ( x >= 16 ) { x = 0; y++; } if ( y >= 16 ) { y = 0; z++; } } return 1; } if ( (width == 512) && (height == 32) ) { /* padded 16x16x16 LUT */ printf("conv: horizontal padded 16x16x16 LUT\n"); ddshead.width = width = 16; ddshead.height = height = 16; ddshead.depth = depth = 16; ddshead.pitch = 16*4; s = width*height*depth*4; outdata = malloc(s); i = 0; x = 8; y = 8; z = 0; while ( i < s ) { outdata[i++] = indata[(x+y*512+z*32)*(3+alpha)]; outdata[i++] = indata[(x+y*512+z*32)*(3+alpha)+1]; outdata[i++] = indata[(x+y*512+z*32)*(3+alpha)+2]; if ( alpha ) outdata[i++] = indata[(x+y*512+z*32) *(3+alpha)+3]; else outdata[i++] = 255; x++; if ( x >= 24 ) { x = 8; y++; } if ( y >= 24 ) { y = 8; z++; } } return 1; } printf("conv: Never heard of a LUT texture of %ux%u\n",width,height); return 0; } /* png loader code, boilerplate written according to official docs */ int loadpng( const char *filename ) { if ( !filename ) { printf("png: no filename passed\n"); return 1; } png_structp pngp; png_infop infp; unsigned int sread = 0; int col, inter; FILE *pf; if ( !(pf = fopen(filename,"rb")) ) { printf("png: could not open file: %s\n",strerror(errno)); return 1; } unsigned char header[8]; fread(header,8,1,pf); if ( png_sig_cmp(header,0,8) ) { printf("png: bad signature, not a PNG file\n"); return 1; } sread += 8; pngp = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0); if ( !pngp ) { printf("png: failed to create png_struct\n"); fclose(pf); return 1; } infp = png_create_info_struct(pngp); if ( !infp ) { printf("png: failed to create png_info\n"); fclose(pf); png_destroy_read_struct(&pngp,0,0); return 1; } if ( setjmp(png_jmpbuf(pngp)) ) { printf("png: failed to set up error handler\n"); png_destroy_read_struct(&pngp,&infp,0); fclose(pf); return 1; } png_init_io(pngp,pf); png_set_sig_bytes(pngp,sread); png_read_png(pngp,infp,PNG_TRANSFORM_STRIP_16|PNG_TRANSFORM_PACKING |PNG_TRANSFORM_EXPAND,0); png_uint_32 w,h; int dep; png_get_IHDR(pngp,infp,&w,&h,&dep,&col,&inter,0,0); if ( (col != PNG_COLOR_TYPE_RGB) && (col != PNG_COLOR_TYPE_RGB_ALPHA) ) { printf("png: only RGB or RGBA are supported\n"); png_destroy_read_struct(&pngp,&infp,0); fclose(pf); return 1; } if ( dep != 8 ) { printf("png: only 8-bit channels are supported\n"); png_destroy_read_struct(&pngp,&infp,0); fclose(pf); return 1; } width = w; height = h; alpha = (col==PNG_COLOR_TYPE_RGB_ALPHA); int rbytes = png_get_rowbytes(pngp,infp); indata = malloc(rbytes*h); png_bytepp rptr = png_get_rows(pngp,infp); for ( unsigned i=0; iheight)?horconv():vertconv(); free(indata); if ( !res ) { printf("Failed to process %s\n",argv[i]); continue; } strcpy(fname,argv[i]); ext = strrchr(fname,'.'); if ( ext ) *ext = 0; strcat(fname,".dds"); fout = fopen(fname,"wb"); if ( !fout ) { printf("Cannot open %s: %s\n",fname,strerror(errno)); free(outdata); continue; } fwrite(&ddshead,sizeof(ddsheader_t),1,fout); fwrite(outdata,width*height*depth*4,1,fout); fclose(fout); free(outdata); } return 0; }