texture-compressor
Advanced tools
Comparing version 0.1.0 to 0.1.2
538
docs/main.js
@@ -1,494 +0,122 @@ | ||
// Fileloader | ||
// ---------- | ||
function fileLoader(url, responseType = '') { | ||
return new Promise((resolve, reject) => { | ||
const request = new XMLHttpRequest(); | ||
var camera, scene, renderer; | ||
var meshes = []; | ||
request.responseType = responseType || ''; | ||
request.onreadystatechange = () => { | ||
if (request.readyState !== 4) return; | ||
init(); | ||
animate(); | ||
if (request.readyState === 4 && request.status === 200) { | ||
resolve(request.response, request.status); | ||
} else { | ||
reject(request.status); | ||
} | ||
}; | ||
function init() { | ||
var appElement = document.getElementById('app'); | ||
request.open('GET', url, true); | ||
request.send(); | ||
}); | ||
} | ||
renderer = new THREE.WebGLRenderer({ antialias: true }); | ||
renderer.setClearColor(0x000000, 1); | ||
renderer.setPixelRatio(window.devicePixelRatio); | ||
renderer.setSize(window.innerWidth, window.innerHeight); | ||
document.body.appendChild(renderer.domElement); | ||
var formats = { | ||
astc: renderer.extensions.get('WEBGL_compressed_texture_astc'), | ||
etc1: renderer.extensions.get('WEBGL_compressed_texture_etc1'), | ||
etc2: renderer.extensions.get('WEBGL_compressed_texture_etc'), | ||
s3tc: renderer.extensions.get('WEBGL_compressed_texture_s3tc'), | ||
pvrtc: renderer.extensions.get('WEBGL_compressed_texture_pvrtc') | ||
}; | ||
// Parse KTX | ||
// --------- | ||
function parseKTX(buffer, facesExpected = 1, loadMipmaps = true) { | ||
// https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/ | ||
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 2000); | ||
camera.position.z = 1000; | ||
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_astc/ | ||
const COMPRESSED_RGBA_ASTC_4x4_KHR = 0x93B0; | ||
const COMPRESSED_RGBA_ASTC_5x4_KHR = 0x93B1; | ||
const COMPRESSED_RGBA_ASTC_5x5_KHR = 0x93B2; | ||
const COMPRESSED_RGBA_ASTC_6x5_KHR = 0x93B3; | ||
const COMPRESSED_RGBA_ASTC_6x6_KHR = 0x93B4; | ||
const COMPRESSED_RGBA_ASTC_8x5_KHR = 0x93B5; | ||
const COMPRESSED_RGBA_ASTC_8x6_KHR = 0x93B6; | ||
const COMPRESSED_RGBA_ASTC_8x8_KHR = 0x93B7; | ||
const COMPRESSED_RGBA_ASTC_10x5_KHR = 0x93B8; | ||
const COMPRESSED_RGBA_ASTC_10x6_KHR = 0x93B9; | ||
const COMPRESSED_RGBA_ASTC_10x8_KHR = 0x93BA; | ||
const COMPRESSED_RGBA_ASTC_10x10_KHR = 0x93BB; | ||
const COMPRESSED_RGBA_ASTC_12x10_KHR = 0x93BC; | ||
const COMPRESSED_RGBA_ASTC_12x12_KHR = 0x93BD; | ||
// const COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR = 0x93D0; | ||
// const COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR = 0x93D1; | ||
// const COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR = 0x93D2; | ||
// const COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR = 0x93D3; | ||
// const COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR = 0x93D4; | ||
// const COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR = 0x93D5; | ||
// const COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR = 0x93D6; | ||
// const COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR = 0x93D7; | ||
// const COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR = 0x93D8; | ||
// const COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR = 0x93D9; | ||
// const COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR = 0x93DA; | ||
// const COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR = 0x93DB; | ||
// const COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR = 0x93DC; | ||
// const COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR = 0x93DD; | ||
scene = new THREE.Scene(); | ||
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_etc/ | ||
const COMPRESSED_R11_EAC = 0x9270; | ||
const COMPRESSED_SIGNED_R11_EAC = 0x9271; | ||
const COMPRESSED_RG11_EAC = 0x9272; | ||
const COMPRESSED_SIGNED_RG11_EAC = 0x9273; | ||
const COMPRESSED_RGB8_ETC2 = 0x9274; | ||
const COMPRESSED_SRGB8_ETC2 = 0x9275; | ||
const COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9276; | ||
const COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9277; | ||
const COMPRESSED_RGBA8_ETC2_EAC = 0x9278; | ||
const COMPRESSED_SRGB8_ALPHA8_ETC2_EAC = 0x9279; | ||
var geometry = new THREE.PlaneGeometry(200, 200, 200); | ||
var material1, material2, material3, material4; | ||
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_etc1/ | ||
const COMPRESSED_RGB_ETC1_WEBGL = 0x8D64; | ||
// TODO: add cubemap support | ||
var loader = new THREE.KTXLoader(); | ||
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/ | ||
const COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0; | ||
const COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1; | ||
const COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2; | ||
const COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3; | ||
if (formats.astc) { | ||
material1 = new THREE.MeshBasicMaterial({ map: loader.load('./example/flippedY/example-astc-4x4.ktx') }); | ||
appElement.appendChild(document.createTextNode(' ASTC-4x4 ')); | ||
meshes.push(new THREE.Mesh(geometry, material1)); | ||
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_pvrtc/ | ||
const COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00; | ||
const COMPRESSED_RGB_PVRTC_2BPPV1_IMG = 0x8C01; | ||
const COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02; | ||
const COMPRESSED_RGBA_PVRTC_2BPPV1_IMG = 0x8C03; | ||
const identifier = new Uint8Array(buffer, 0, 12); | ||
if (identifier[0] !== 0xAB || | ||
identifier[1] !== 0x4B || | ||
identifier[2] !== 0x54 || | ||
identifier[3] !== 0x58 || | ||
identifier[4] !== 0x20 || | ||
identifier[5] !== 0x31 || | ||
identifier[6] !== 0x31 || | ||
identifier[7] !== 0xBB || | ||
identifier[8] !== 0x0D || | ||
identifier[9] !== 0x0A || | ||
identifier[10] !== 0x1A || | ||
identifier[11] !== 0x0A | ||
) { | ||
console.error('Texture missing KTX identifier'); | ||
material2 = new THREE.MeshBasicMaterial({ map: loader.load('./example/flippedY/example-astc-8x8.ktx') }); | ||
appElement.appendChild(document.createTextNode(' ASTC-8x8 ')); | ||
meshes.push(new THREE.Mesh(geometry, material2)); | ||
} | ||
// Load the rest of the header in 32 bit int | ||
const header = new Int32Array(buffer, 12, 13); | ||
const glType = header[1]; | ||
const glTypeSize = header[2]; | ||
const glFormat = header[3]; | ||
const glInternalFormat = header[4]; | ||
const glBaseInternalFormat = header[5]; | ||
const pixelWidth = header[6]; | ||
const pixelHeight = header[7]; | ||
const pixelDepth = header[8]; | ||
const numberOfArrayElements = header[9]; | ||
const numberOfFaces = header[10]; | ||
let numberOfMipmapLevels = header[11]; | ||
const bytesOfKeyValueData = header[12]; | ||
if (glType !== 0) { | ||
console.error('Only compressed formats are supported'); | ||
} else { | ||
numberOfMipmapLevels = Math.max(1, numberOfMipmapLevels); | ||
if (formats.etc1) { | ||
material1 = new THREE.MeshBasicMaterial({ map: loader.load('./example/flippedY/example-etc1.ktx') }); | ||
appElement.appendChild(document.createTextNode(' ETC1 ')); | ||
meshes.push(new THREE.Mesh(geometry, material1)); | ||
} | ||
if (pixelHeight === 0 || pixelDepth !== 0) { | ||
console.error('Only 2D textures are supported'); | ||
} | ||
if (formats.etc2) { | ||
material1 = new THREE.MeshBasicMaterial({ map: loader.load('./example/flippedY/example-etc2.ktx') }); | ||
appElement.appendChild(document.createTextNode(' ETC2 ')); | ||
meshes.push(new THREE.Mesh(geometry, material1)); | ||
if (numberOfArrayElements !== 0) { | ||
console.error('Texture arrays are not supported'); | ||
material2 = new THREE.MeshBasicMaterial({ map: loader.load('./example/flippedY/example-etc2A.ktx') }); | ||
appElement.appendChild(document.createTextNode(' ETC2A ')); | ||
meshes.push(new THREE.Mesh(geometry, material2)); | ||
} | ||
if (numberOfFaces !== facesExpected) { | ||
console.error(`Number of faces expected ${facesExpected} but found ${numberOfFaces}`); | ||
} | ||
if (formats.pvrtc) { | ||
material1 = new THREE.MeshBasicMaterial({ map: loader.load('./example/flippedY/example-pvrtc2BPP.ktx') }); | ||
appElement.appendChild(document.createTextNode(' PVRTC-2BPP ')); | ||
meshes.push(new THREE.Mesh(geometry, material1)); | ||
let id; | ||
let compression; | ||
material2 = new THREE.MeshBasicMaterial({ map: loader.load('./example/flippedY/example-pvrtc2BPPA.ktx') }); | ||
appElement.appendChild(document.createTextNode(' PVRTC-2BPPA ')); | ||
meshes.push(new THREE.Mesh(geometry, material2)); | ||
switch (glInternalFormat) { | ||
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_astc/ | ||
case COMPRESSED_RGBA_ASTC_4x4_KHR: | ||
id = 'ASTC_RGBA_4x4'; | ||
compression = 'astc'; | ||
break; | ||
material3 = new THREE.MeshBasicMaterial({ map: loader.load('./example/flippedY/example-pvrtc4BPP.ktx') }); | ||
appElement.appendChild(document.createTextNode(' PVRTC-4BPP ')); | ||
meshes.push(new THREE.Mesh(geometry, material3)); | ||
case COMPRESSED_RGBA_ASTC_5x4_KHR: | ||
id = 'ASTC_RGBA_5x4'; | ||
compression = 'astc'; | ||
break; | ||
case COMPRESSED_RGBA_ASTC_5x5_KHR: | ||
id = 'ASTC_RGBA_5x5'; | ||
compression = 'astc'; | ||
break; | ||
case COMPRESSED_RGBA_ASTC_6x5_KHR: | ||
id = 'ASTC_RGBA_6x5'; | ||
compression = 'astc'; | ||
break; | ||
case COMPRESSED_RGBA_ASTC_6x6_KHR: | ||
id = 'ASTC_RGBA_6x6'; | ||
compression = 'astc'; | ||
break; | ||
case COMPRESSED_RGBA_ASTC_8x5_KHR: | ||
id = 'ASTC_RGBA_8x5'; | ||
compression = 'astc'; | ||
break; | ||
case COMPRESSED_RGBA_ASTC_8x6_KHR: | ||
id = 'ASTC_RGBA_8x6'; | ||
compression = 'astc'; | ||
break; | ||
case COMPRESSED_RGBA_ASTC_8x8_KHR: | ||
id = 'ASTC_RGBA_8x8'; | ||
compression = 'astc'; | ||
break; | ||
case COMPRESSED_RGBA_ASTC_10x5_KHR: | ||
id = 'ASTC_RGBA_10x5'; | ||
compression = 'astc'; | ||
break; | ||
case COMPRESSED_RGBA_ASTC_10x6_KHR: | ||
id = 'ASTC_RGBA_10x6'; | ||
compression = 'astc'; | ||
break; | ||
case COMPRESSED_RGBA_ASTC_10x8_KHR: | ||
id = 'ASTC_RGBA_10x8'; | ||
compression = 'astc'; | ||
break; | ||
case COMPRESSED_RGBA_ASTC_10x10_KHR: | ||
id = 'ASTC_RGBA_10x10'; | ||
compression = 'astc'; | ||
break; | ||
case COMPRESSED_RGBA_ASTC_12x10_KHR: | ||
id = 'ASTC_RGBA_12x10'; | ||
compression = 'astc'; | ||
break; | ||
case COMPRESSED_RGBA_ASTC_12x12_KHR: | ||
id = 'ASTC_RGBA_12x12'; | ||
compression = 'astc'; | ||
break; | ||
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_etc/ | ||
case COMPRESSED_RGB8_ETC2: | ||
id = 'ETC2_RGB'; | ||
compression = 'etc'; | ||
break; | ||
case COMPRESSED_RGBA8_ETC2_EAC: | ||
id = 'ETC2_RGBA'; | ||
compression = 'etc'; | ||
break; | ||
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_etc1/ | ||
case COMPRESSED_RGB_ETC1_WEBGL: | ||
id = 'ETC1_RGB'; | ||
compression = 'etc1'; | ||
break; | ||
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/ | ||
case COMPRESSED_RGB_S3TC_DXT1_EXT: | ||
id = 'DXT1_RGB'; | ||
compression = 's3tc'; | ||
break; | ||
case COMPRESSED_RGBA_S3TC_DXT1_EXT: | ||
id = 'DXT1_RGBA'; | ||
compression = 's3tc'; | ||
break; | ||
case COMPRESSED_RGBA_S3TC_DXT3_EXT: | ||
id = 'DXT3_RGBA'; | ||
compression = 's3tc'; | ||
break; | ||
case COMPRESSED_RGBA_S3TC_DXT5_EXT: | ||
id = 'DXT5_RGBA'; | ||
compression = 's3tc'; | ||
break; | ||
// https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_pvrtc/ | ||
case COMPRESSED_RGB_PVRTC_4BPPV1_IMG: | ||
id = 'PVRTC_RGB_4BPP'; | ||
compression = 'pvrtc'; | ||
break; | ||
case COMPRESSED_RGB_PVRTC_2BPPV1_IMG: | ||
id = 'PVRTC_RGB_2BPP'; | ||
compression = 'pvrtc'; | ||
break; | ||
case COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: | ||
id = 'PVRTC_RGBA_4BPP'; | ||
compression = 'pvrtc'; | ||
break; | ||
case COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: | ||
id = 'PVRTC_RGBA_2BPP'; | ||
compression = 'pvrtc'; | ||
break; | ||
default: | ||
console.warn(`glInternalFormat: ${glInternalFormat} was not regocnized`); | ||
break; | ||
material4 = new THREE.MeshBasicMaterial({ map: loader.load('./example/flippedY/example-pvrtc4BPPA.ktx') }); | ||
appElement.appendChild(document.createTextNode(' PVRTC-4BPPA ')); | ||
meshes.push(new THREE.Mesh(geometry, material4)); | ||
} | ||
const mipmaps = []; | ||
let dataOffset = (12 + (13 * 4)) + bytesOfKeyValueData; | ||
let width = pixelWidth; | ||
let height = pixelHeight; | ||
const mipmapCount = loadMipmaps ? numberOfMipmapLevels : 1; | ||
if (formats.s3tc) { | ||
material1 = new THREE.MeshBasicMaterial({ map: loader.load('./example/flippedY/example-dxt1.ktx') }); | ||
appElement.appendChild(document.createTextNode(' DXT1 ')); | ||
meshes.push(new THREE.Mesh(geometry, material1)); | ||
for (let i = 0; i < mipmapCount; i++) { | ||
const imageSize = new Int32Array(buffer, dataOffset, 1)[0]; // Size per face | ||
material2 = new THREE.MeshBasicMaterial({ map: loader.load('./example/flippedY/example-dxt1A.ktx') }); | ||
appElement.appendChild(document.createTextNode(' DXT1A ')); | ||
meshes.push(new THREE.Mesh(geometry, material2)); | ||
for (let j = 0; j < numberOfFaces; j++) { | ||
const byteArray = new Uint8Array(buffer, dataOffset + 4, imageSize); | ||
mipmaps.push({ | ||
data: byteArray, | ||
width, | ||
height, | ||
}); | ||
dataOffset += imageSize + 4; | ||
dataOffset += 3 - ((imageSize + 3) % 4); | ||
} | ||
material3 = new THREE.MeshBasicMaterial({ map: loader.load('./example/flippedY/example-dxt3.ktx') }); | ||
appElement.appendChild(document.createTextNode(' DXT3 ')); | ||
meshes.push(new THREE.Mesh(geometry, material3)); | ||
width = Math.max(1.0, width * 0.5); | ||
height = Math.max(1.0, height * 0.5); | ||
material4 = new THREE.MeshBasicMaterial({ map: loader.load('./example/flippedY/example-dxt5.ktx') }); | ||
appElement.appendChild(document.createTextNode(' DXT5 ')); | ||
meshes.push(new THREE.Mesh(geometry, material4)); | ||
} | ||
return { | ||
id, | ||
compression, | ||
width: pixelWidth, | ||
height: pixelHeight, | ||
format: glInternalFormat, | ||
data: mipmaps[0].data, | ||
}; | ||
} | ||
var x = - meshes.length / 2 * 225; | ||
for (var i = 0; i < meshes.length; ++ i, x += 300) { | ||
// Parse binary | ||
// ------------ | ||
function parseBinary(filename, bin) { | ||
const containerType = filename.split('.').pop(); | ||
let result; | ||
if (containerType === 'ktx') { | ||
result = parseKTX(bin); | ||
} else { | ||
console.error('Output filename should have a .ktx extension'); | ||
var mesh = meshes[ i ]; | ||
mesh.position.x = x; | ||
mesh.position.y = 0; | ||
scene.add(mesh); | ||
} | ||
return result; | ||
} | ||
window.addEventListener('resize', onWindowResize, false); | ||
// Load binary | ||
// ----------- | ||
function loadBinary(filename) { | ||
return new Promise((resolve, reject) => { | ||
fileLoader(filename, 'arraybuffer') | ||
.then((bin) => { | ||
resolve(parseBinary(filename, bin)); | ||
}) | ||
.catch((error) => { | ||
reject(console.warn(error)); | ||
}); | ||
}); | ||
} | ||
function onWindowResize() { | ||
// WebGL utilities | ||
// --------------- | ||
function createImage(gl, file) { | ||
const texture = gl.createTexture(); | ||
gl.activeTexture(gl.TEXTURE0); | ||
gl.bindTexture(gl.TEXTURE_2D, texture); | ||
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, file.format, file.width, file.height, 0, file.data); | ||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); | ||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); | ||
camera.aspect = window.innerWidth / window.innerHeight; | ||
camera.updateProjectionMatrix(); | ||
const vertexShaderSource = ` | ||
attribute vec2 aPosition; | ||
attribute vec2 aTexCoord; | ||
renderer.setSize(window.innerWidth, window.innerHeight); | ||
varying vec2 vTexCoord; | ||
void main(void) { | ||
vTexCoord = aTexCoord; | ||
gl_Position = vec4(aPosition, 1.0, 1.0); | ||
} | ||
`; | ||
const fragmentShaderSource = ` | ||
precision mediump float; | ||
varying vec2 vTexCoord; | ||
uniform sampler2D diffuse; | ||
void main(void) { | ||
gl_FragColor = texture2D(diffuse, vTexCoord); | ||
// gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); | ||
} | ||
`; | ||
const program = gl.createProgram(); | ||
const vertexShader = gl.createShader(gl.VERTEX_SHADER); | ||
gl.shaderSource(vertexShader, vertexShaderSource); | ||
gl.compileShader(vertexShader); | ||
gl.attachShader(program, vertexShader); | ||
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); | ||
gl.shaderSource(fragmentShader, fragmentShaderSource); | ||
gl.compileShader(fragmentShader); | ||
gl.attachShader(program, fragmentShader); | ||
gl.linkProgram(program); | ||
gl.useProgram(program); | ||
const quadBuffer = gl.createBuffer(); | ||
gl.bindBuffer(gl.ARRAY_BUFFER, quadBuffer); | ||
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ | ||
-1, 1, 0, 0, | ||
1, 1, 1, 0, | ||
1, -1, 1, 1, | ||
-1, 1, 0, 0, | ||
1, -1, 1, 1, | ||
-1, -1, 0, 1, | ||
]), gl.STATIC_DRAW); | ||
gl.clearColor(0, 0, 0, 0); | ||
gl.enableVertexAttribArray(0); | ||
gl.enableVertexAttribArray(1); | ||
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 16, 0); | ||
gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 16, 8); | ||
gl.uniform1i(gl.getUniformLocation(program, 'diffuse'), 0); | ||
gl.clear(gl.COLOR_BUFFER_BIT); | ||
gl.drawArrays(gl.TRIANGLES, 0, 6); | ||
} | ||
// Initialize | ||
// ---------- | ||
function initialize() { | ||
const app = document.getElementById('app'); | ||
// TODO: Fix Promise.all issues on IE 11 | ||
const result = Promise.all([ | ||
loadBinary('./example/example-astc-4x4.ktx'), | ||
loadBinary('./example/example-astc-5x4.ktx'), | ||
loadBinary('./example/example-astc-5x5.ktx'), | ||
loadBinary('./example/example-astc-6x5.ktx'), | ||
loadBinary('./example/example-astc-6x6.ktx'), | ||
loadBinary('./example/example-astc-8x5.ktx'), | ||
loadBinary('./example/example-astc-8x6.ktx'), | ||
loadBinary('./example/example-astc-8x8.ktx'), | ||
loadBinary('./example/example-astc-10x5.ktx'), | ||
loadBinary('./example/example-astc-10x6.ktx'), | ||
loadBinary('./example/example-astc-10x8.ktx'), | ||
loadBinary('./example/example-astc-10x10.ktx'), | ||
loadBinary('./example/example-astc-12x10.ktx'), | ||
loadBinary('./example/example-astc-12x12.ktx'), | ||
loadBinary('./example/example-dxt1.ktx'), | ||
loadBinary('./example/example-dxt1A.ktx'), | ||
loadBinary('./example/example-dxt3.ktx'), | ||
loadBinary('./example/example-dxt5.ktx'), | ||
loadBinary('./example/example-etc1.ktx'), | ||
loadBinary('./example/example-etc2.ktx'), | ||
loadBinary('./example/example-etc2A.ktx'), | ||
loadBinary('./example/example-pvrtc2BPP.ktx'), | ||
loadBinary('./example/example-pvrtc2BPPA.ktx'), | ||
loadBinary('./example/example-pvrtc4BPP.ktx'), | ||
loadBinary('./example/example-pvrtc4BPPA.ktx'), | ||
]); | ||
const pixelRatio = window.devicePixelRatio; | ||
result | ||
.then((data) => { | ||
data.map((file) => { | ||
const element = document.getElementById(`${file.id}`); | ||
const canvas = document.createElement('canvas'); | ||
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); | ||
console.log(gl); | ||
canvas.width = file.width * pixelRatio; | ||
canvas.height = file.height * pixelRatio; | ||
canvas.style.width = `${file.width}px`; | ||
canvas.style.height = `${file.height}px`; | ||
gl.enable(gl.SCISSOR_TEST); | ||
gl.viewport(0, 0, file.width * pixelRatio, file.height * pixelRatio); | ||
gl.scissor(0, 0, file.width * pixelRatio, file.height * pixelRatio); | ||
// Get extension to match file | ||
const extension = | ||
gl.getExtension(`WEBGL_compressed_texture_${file.compression}`) || | ||
gl.getExtension(`WEBKIT_WEBGL_compressed_texture_${file.compression}`) || | ||
gl.getExtension(`MOZ_WEBGL_compressed_texture_${file.compression}`); | ||
if (extension) { | ||
element.appendChild(canvas); | ||
createImage(gl, file); | ||
} else { | ||
const errorMessageElement = document.createElement('div'); | ||
errorMessageElement.className = 'error-message'; | ||
const errorMessage = errorMessageElement.innerHTML = `WEBGL_compressed_texture_${file.compression} extension is not available`; | ||
element.appendChild(errorMessageElement); | ||
console.warn(`WEBGL_compressed_texture_${file.compression} extension is not available - ${file.format}`); | ||
} | ||
}); | ||
}); | ||
function animate() { | ||
requestAnimationFrame(animate); | ||
renderer.render(scene, camera); | ||
} | ||
initialize(); |
@@ -89,2 +89,9 @@ // Native | ||
// Vertical flip flag | ||
parser.addArgument(['-y', '--flipY'], { | ||
help: 'Output file flipped vertically', | ||
action: 'storeTrue', | ||
required: false, | ||
}); | ||
const args = parser.parseArgs(); | ||
@@ -91,0 +98,0 @@ |
@@ -14,2 +14,3 @@ // Native | ||
flags, | ||
flipY, | ||
input, | ||
@@ -46,3 +47,3 @@ output, | ||
function runCompression(flippedImage) { | ||
function runCompression(image) { | ||
// Bitrate flag (2.0 bbp = 8x8 blocksize) | ||
@@ -59,3 +60,3 @@ const bitrateFlag = convertNumberToDecimalString(bitrate); | ||
const flagMapping = [ | ||
'-cl', flippedImage, // Encode with LDR-linear submode | ||
'-cl', image, // Encode with LDR-linear submode | ||
output, | ||
@@ -143,34 +144,40 @@ `${bitrateFlag}`, | ||
// File reading necssary for temporary Y flipping because Astcenc flips the image internally | ||
jimp.read(input) | ||
.then((file) => { | ||
// Flip Y (Astcenc flips the image during encoding) | ||
file.flip(false, true); | ||
// Write temporary file to a temporary directory | ||
fsExtra.ensureDir(tempDirectory) | ||
.then(() => { | ||
file.getBuffer(jimp.AUTO, (error, buffer) => { | ||
if (error) { | ||
console.error(error); | ||
} | ||
// Ignore flipping Y if the flag is passed as Astcenc flips Y by default | ||
if (flipY) { | ||
runCompression(input); | ||
} else { | ||
jimp.read(input) | ||
.then((file) => { | ||
// Flip Y by default to oppose direction as Astcenc flips it during compression | ||
file.flip(false, true); | ||
// Write file out manually as jimp.write doesn't offer a callback after writing | ||
fs.writeFile(`${tempDirectory}/${tempFilename}`, buffer, (error) => { | ||
// Write temporary file to a temporary directory | ||
fsExtra.ensureDir(tempDirectory) | ||
.then(() => { | ||
file.getBuffer(jimp.AUTO, (error, buffer) => { | ||
if (error) { | ||
console.error(error); | ||
} else { | ||
// Compress flipped instead of the direct input | ||
runCompression(`${tempDirectory}/${tempFilename}`); | ||
} | ||
// Clean up the temporary directory and the temporary image | ||
fsExtra.remove(`${tempDirectory}/${tempFilename}`); | ||
fsExtra.remove(tempDirectory); | ||
} | ||
// Write file out manually as jimp.write doesn't offer a callback after writing | ||
fs.writeFile(`${tempDirectory}/${tempFilename}`, buffer, (error) => { | ||
if (error) { | ||
console.error(error); | ||
} else { | ||
// Compress flipped instead of the direct input | ||
runCompression(`${tempDirectory}/${tempFilename}`); | ||
// Clean up the temporary directory and the temporary image | ||
fsExtra.remove(`${tempDirectory}/${tempFilename}`); | ||
fsExtra.remove(tempDirectory); | ||
} | ||
}); | ||
}); | ||
}); | ||
}); | ||
}) | ||
.catch((error) => { | ||
console.error(error); | ||
}); | ||
}) | ||
.catch((error) => { | ||
console.error(error); | ||
}); | ||
} | ||
} else { | ||
@@ -177,0 +184,0 @@ console.error(`${inputFileExtension} is not supported.`); |
@@ -8,2 +8,3 @@ // Native | ||
flags, | ||
flipY, | ||
input, | ||
@@ -65,3 +66,4 @@ output, | ||
'-pot', '+', | ||
]; | ||
`${flipY ? '-flip' : null}`, `${flipY ? 'y' : null}`, | ||
].filter(e => e !== 'null'); | ||
@@ -68,0 +70,0 @@ const toolPath = path.join(compressionToolDirectory, 'PVRTexToolCLI'); |
@@ -8,2 +8,3 @@ // Native | ||
flags, | ||
flipY, | ||
input, | ||
@@ -73,3 +74,4 @@ output, | ||
'-pot', '+', | ||
]; | ||
`${flipY ? '-flip' : null}`, `${flipY ? 'y' : null}`, | ||
].filter(e => e !== 'null'); | ||
@@ -76,0 +78,0 @@ const toolPath = path.join(compressionToolDirectory, 'PVRTexToolCLI'); |
@@ -9,2 +9,3 @@ // Native | ||
flags, | ||
flipY, | ||
input, | ||
@@ -69,3 +70,4 @@ output, | ||
'-mipMode', 'None', | ||
]; | ||
`${flipY ? '-yflip' : null}`, | ||
].filter(e => e !== 'null'); | ||
@@ -72,0 +74,0 @@ const toolPath = path.join(compressionToolDirectory, 'crunch'); |
{ | ||
"name": "texture-compressor", | ||
"version": "0.1.0", | ||
"version": "0.1.2", | ||
"description": "CLI tool for texture compression using ASTC, ETC, PVRTC and S3TC in a KTX container.", | ||
"main": "index.js", | ||
"scripts": { | ||
"generate": "./docs/generate.sh", | ||
"generate-flipped": "./docs/generate_flipped.sh", | ||
"start": "http-server --cors -s -o", | ||
@@ -8,0 +10,0 @@ "lint": "eslint lib/**/*.js", |
@@ -19,8 +19,4 @@ # Texture compressor | ||
[Live demo](https://timvanscherpenzeel.github.io/texture-compressor/) | ||
[Live demo](https://timvanscherpenzeel.github.io/texture-compressor/) using [KTXLoader](https://github.com/mrdoob/three.js/blob/dev/examples/js/loaders/KTXLoader.js) from [Three.js](https://github.com/mrdoob/three.js/). | ||
## Docs | ||
The source code of the `KTX loader` is available on in [docs/main.js](https://github.com/TimvanScherpenzeel/texture-compressor/blob/master/docs/main.js) in the `docs` folder. | ||
## Support table | ||
@@ -80,3 +76,7 @@ | ||
$ node ./bin/texture-compressor -i ./docs/example/example.png -o ./docs/example/example-astc.ktx -m astc -c astc | ||
# example-astc-4x4 | ||
$ node ./bin/texture-compressor -i ./docs/example/example.png -o ./docs/example/example-astc-4x4.ktx -m astc -c astc -b 4x4 | ||
# example-astc-8x8 | ||
$ node ./bin/texture-compressor -i ./docs/example/example.png -o ./docs/example/example-astc-8x8.ktx -m astc -c astc -b 8x8 | ||
``` | ||
@@ -90,5 +90,10 @@ | ||
# example-etc1 (-flipY does not work with ETC1) | ||
$ node ./bin/texture-compressor -i ./docs/example/example.png -o ./docs/example/example-etc1.ktx -m etc -c etc1 | ||
# example-etc2 | ||
$ node ./bin/texture-compressor -i ./docs/example/example.png -o ./docs/example/example-etc2.ktx -m etc -c etc2 | ||
# example-etc2A | ||
$ node ./bin/texture-compressor -i ./docs/example/example.png -o ./docs/example/example-etc2A.ktx -m etc -c etc2 -t | ||
``` | ||
@@ -102,3 +107,13 @@ | ||
$ node ./bin/texture-compressor -i ./docs/example/example.png -o ./docs/example/example-pvrtc1.ktx -m pvr -c pvrtc1 | ||
# example-pvrtc2BPP | ||
$ node ./bin/texture-compressor -i ./docs/example/example.png -o ./docs/example/example-pvrtc2BPP.ktx -m pvr -c pvrtc1 | ||
# example-pvrtc2BPPA | ||
$ node ./bin/texture-compressor -i ./docs/example/example.png -o ./docs/example/example-pvrtc2BPPA.ktx -m pvr -c pvrtc1 -t | ||
# example-pvrtc4BPP | ||
$ node ./bin/texture-compressor -i ./docs/example/example.png -o ./docs/example/example-pvrtc4BPP.ktx -m pvr -c pvrtc1 -b 4 | ||
# example-pvrtc4BPPA | ||
$ node ./bin/texture-compressor -i ./docs/example/example.png -o ./docs/example/example-pvrtc4BPPA.ktx -m pvr -c pvrtc1 -b 4 -t | ||
``` | ||
@@ -112,6 +127,12 @@ | ||
# example-dxt1 | ||
$ node ./bin/texture-compressor -i ./docs/example/example.png -o ./docs/example/example-dxt1.ktx -m s3tc -c dxt1 | ||
# example-dxt1A | ||
$ node ./bin/texture-compressor -i ./docs/example/example.png -o ./docs/example/example-dxt1A.ktx -m s3tc -c dxt1 -t | ||
# example-dxt3 | ||
$ node ./bin/texture-compressor -i ./docs/example/example.png -o ./docs/example/example-dxt3.ktx -m s3tc -c dxt3 | ||
# example-dxt5 | ||
$ node ./bin/texture-compressor -i ./docs/example/example.png -o ./docs/example/example-dxt5.ktx -m s3tc -c dxt5 | ||
@@ -136,2 +157,3 @@ ``` | ||
-t, --transparant [true / false, default: false] [not required] | ||
-y, --flipY [tue / false, default: false] [not required] | ||
@@ -138,0 +160,0 @@ ### Tool flags |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 13 instances in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 12 instances in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
36034369
57
3048
168
38