compressed-texture-utils.js (11746B)
1 /* 2 Copyright (c) 2019 The Khronos Group Inc. 3 Use of this source code is governed by an MIT-style license that can be 4 found in the LICENSE.txt file. 5 */ 6 7 "use strict"; 8 9 let CompressedTextureUtils = (function() { 10 11 let formatToString = function(ext, format) { 12 for (let p in ext) { 13 if (ext[p] == format) { 14 return p; 15 } 16 } 17 return "0x" + format.toString(16); 18 }; 19 20 /** 21 * Make an image element from Uint8Array bitmap data. 22 * @param {number} imageHeight Height of the data in pixels. 23 * @param {number} imageWidth Width of the data in pixels. 24 * @param {number} dataWidth Width of each row in the data buffer, in pixels. 25 * @param {Uint8Array} data Image data buffer to display. Each pixel takes up 4 bytes in the array regardless of the alpha parameter. 26 * @param {boolean} alpha True if alpha data should be taken from data. Otherwise alpha channel is set to 255. 27 * @return {HTMLImageElement} The image element. 28 */ 29 let makeScaledImage = function(imageWidth, imageHeight, dataWidth, data, alpha, opt_scale) { 30 let scale = opt_scale ? opt_scale : 8; 31 let c = document.createElement("canvas"); 32 c.width = imageWidth * scale; 33 c.height = imageHeight * scale; 34 let ctx = c.getContext("2d"); 35 for (let yy = 0; yy < imageHeight; ++yy) { 36 for (let xx = 0; xx < imageWidth; ++xx) { 37 let offset = (yy * dataWidth + xx) * 4; 38 ctx.fillStyle = "rgba(" + 39 data[offset + 0] + "," + 40 data[offset + 1] + "," + 41 data[offset + 2] + "," + 42 (alpha ? data[offset + 3] / 255 : 1) + ")"; 43 ctx.fillRect(xx * scale, yy * scale, scale, scale); 44 } 45 } 46 return wtu.makeImageFromCanvas(c); 47 }; 48 49 let insertCaptionedImg = function(parent, caption, img) { 50 let div = document.createElement("div"); 51 div.appendChild(img); 52 let label = document.createElement("div"); 53 label.appendChild(document.createTextNode(caption)); 54 div.appendChild(label); 55 parent.appendChild(div); 56 }; 57 58 /** 59 * @param {WebGLRenderingContextBase} gl 60 * @param {Object} compressedFormats Mapping from format names to format enum values. 61 * @param expectedByteLength A function that takes in width, height and format and returns the expected buffer size in bytes. 62 */ 63 let testCompressedFormatsUnavailableWhenExtensionDisabled = function(gl, compressedFormats, expectedByteLength, testSize) { 64 let tex = gl.createTexture(); 65 gl.bindTexture(gl.TEXTURE_2D, tex); 66 for (let name in compressedFormats) { 67 if (compressedFormats.hasOwnProperty(name)) { 68 gl.compressedTexImage2D(gl.TEXTURE_2D, 0, compressedFormats[name], testSize, testSize, 0, new Uint8Array(expectedByteLength(testSize, testSize, compressedFormats[name]))); 69 wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "Trying to use format " + name + " with extension disabled."); 70 if (gl.texStorage2D) { 71 gl.texStorage2D(gl.TEXTURE_2D, 1, compressedFormats[name], testSize, testSize); 72 wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "Trying to use format " + name + " with texStorage2D with extension disabled."); 73 } 74 } 75 } 76 gl.bindTexture(gl.TEXTURE_2D, null); 77 gl.deleteTexture(tex); 78 }; 79 80 /** 81 * @param {WebGLRenderingContextBase} gl 82 * @param {Object} expectedFormats Mapping from format names to format enum values. 83 */ 84 let testCompressedFormatsListed = function(gl, expectedFormats) { 85 debug(""); 86 debug("Testing that every format is listed by the compressed texture formats query"); 87 88 let supportedFormats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS); 89 90 let failed; 91 let count = 0; 92 for (let name in expectedFormats) { 93 if (expectedFormats.hasOwnProperty(name)) { 94 ++count; 95 let format = expectedFormats[name]; 96 failed = true; 97 for (let ii = 0; ii < supportedFormats.length; ++ii) { 98 if (format == supportedFormats[ii]) { 99 testPassed("supported format " + name + " exists"); 100 failed = false; 101 break; 102 } 103 } 104 if (failed) { 105 testFailed("supported format " + name + " does not exist"); 106 } 107 } 108 } 109 if (supportedFormats.length != count) { 110 testFailed("Incorrect number of supported formats, was " + supportedFormats.length + " should be " + count); 111 } 112 }; 113 114 /** 115 * @param {Object} ext Compressed texture extension object. 116 * @param {Object} expectedFormats Mapping from format names to format enum values. 117 */ 118 let testCorrectEnumValuesInExt = function(ext, expectedFormats) { 119 debug(""); 120 debug("Testing that format enum values in the extension object are correct"); 121 122 for (name in expectedFormats) { 123 if (expectedFormats.hasOwnProperty(name)) { 124 if (isResultCorrect(ext[name], expectedFormats[name])) { 125 testPassed("Enum value for " + name + " matches 0x" + ext[name].toString(16)); 126 } else { 127 testFailed("Enum value for " + name + " mismatch: 0x" + ext[name].toString(16) + " should be 0x" + expectedFormats[name].toString(16)); 128 } 129 } 130 } 131 }; 132 133 /** 134 * @param {WebGLRenderingContextBase} gl 135 * @param {Object} validFormats Mapping from format names to format enum values. 136 * @param expectedByteLength A function that takes in width, height and format and returns the expected buffer size in bytes. 137 * @param getBlockDimensions A function that takes in a format and returns block size in pixels. 138 */ 139 let testFormatRestrictionsOnBufferSize = function(gl, validFormats, expectedByteLength, getBlockDimensions) { 140 debug(""); 141 debug("Testing format restrictions on texture upload buffer size"); 142 143 let tex = gl.createTexture(); 144 gl.bindTexture(gl.TEXTURE_2D, tex); 145 for (let formatId in validFormats) { 146 if (validFormats.hasOwnProperty(formatId)) { 147 let format = validFormats[formatId]; 148 let blockSize = getBlockDimensions(format); 149 let expectedSize = expectedByteLength(blockSize.width * 4, blockSize.height * 4, format); 150 let data = new Uint8Array(expectedSize); 151 gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, blockSize.width * 3, blockSize.height * 4, 0, data); 152 wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, formatId + " data size does not match dimensions (too small width)"); 153 gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, blockSize.width * 5, blockSize.height * 4, 0, data); 154 wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, formatId + " data size does not match dimensions (too large width)"); 155 gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, blockSize.width * 4, blockSize.height * 3, 0, data); 156 wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, formatId + " data size does not match dimensions (too small height)"); 157 gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, blockSize.width * 4, blockSize.height * 5, 0, data); 158 wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, formatId + " data size does not match dimensions (too large height)"); 159 } 160 } 161 }; 162 163 /** 164 * @param {WebGLRenderingContextBase} gl 165 * @param {Object} validFormats Mapping from format names to format enum values. 166 * @param expectedByteLength A function that takes in width, height and format and returns the expected buffer size in bytes. 167 * @param getBlockDimensions A function that takes in a format and returns block size in pixels. 168 * @param {number} width Width of the image in pixels. 169 * @param {number} height Height of the image in pixels. 170 * @param {Object} subImageConfigs configs for compressedTexSubImage calls 171 */ 172 let testTexSubImageDimensions = function(gl, ext, validFormats, expectedByteLength, getBlockDimensions, width, height, subImageConfigs) { 173 let tex = gl.createTexture(); 174 gl.bindTexture(gl.TEXTURE_2D, tex); 175 176 for (let formatId in validFormats) { 177 if (validFormats.hasOwnProperty(formatId)) { 178 let format = validFormats[formatId]; 179 let blockSize = getBlockDimensions(format); 180 debug("testing " + ctu.formatToString(ext, format)); 181 let expectedSize = expectedByteLength(width, height, format); 182 let data = new Uint8Array(expectedSize); 183 184 gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, data); 185 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "setting up compressed texture"); 186 187 for (let i = 0, len = subImageConfigs.length; i < len; ++i) { 188 let c = subImageConfigs[i]; 189 let subData = new Uint8Array(expectedByteLength(c.width, c.height, format)); 190 gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, c.xoffset, c.yoffset, c.width, c.height, format, subData); 191 wtu.glErrorShouldBe(gl, c.expectation, c.message); 192 } 193 } 194 } 195 196 gl.bindTexture(gl.TEXTURE_2D, null); 197 gl.deleteTexture(tex); 198 }; 199 200 let testTexImageLevelDimensions = function(gl, ext, validFormats, expectedByteLength, getBlockDimensions, imageConfigs) { 201 let tex = gl.createTexture(); 202 gl.bindTexture(gl.TEXTURE_2D, tex); 203 204 for (let formatId in validFormats) { 205 if (validFormats.hasOwnProperty(formatId)) { 206 let format = validFormats[formatId]; 207 let blockSize = getBlockDimensions(format); 208 debug("testing " + ctu.formatToString(ext, format)); 209 210 for (let i = 0, len = imageConfigs.length; i < len; ++i) { 211 let c = imageConfigs[i]; 212 let data = new Uint8Array(expectedByteLength(c.width, c.height, format)); 213 gl.compressedTexImage2D(gl.TEXTURE_2D, c.level, format, c.width, c.height, 0, data); 214 wtu.glErrorShouldBe(gl, c.expectation, c.message); 215 } 216 } 217 } 218 219 gl.bindTexture(gl.TEXTURE_2D, null); 220 gl.deleteTexture(tex); 221 } 222 223 let testTexStorageLevelDimensions = function(gl, ext, validFormats, expectedByteLength, getBlockDimensions, imageConfigs) { 224 for (let formatId in validFormats) { 225 let tex = gl.createTexture(); 226 gl.bindTexture(gl.TEXTURE_2D, tex); 227 228 if (validFormats.hasOwnProperty(formatId)) { 229 let format = validFormats[formatId]; 230 let blockSize = getBlockDimensions(format); 231 debug("testing " + ctu.formatToString(ext, format)); 232 233 for (let i = 0, len = imageConfigs.length; i < len; ++i) { 234 let c = imageConfigs[i]; 235 let data = new Uint8Array(expectedByteLength(c.width, c.height, format)); 236 if (i == 0) { 237 gl.texStorage2D(gl.TEXTURE_2D, imageConfigs.length, format, c.width, c.height); 238 wtu.glErrorShouldBe(gl, c.expectation, c.message); 239 } 240 gl.compressedTexSubImage2D(gl.TEXTURE_2D, i, 0, 0, c.width, c.height, format, data); 241 wtu.glErrorShouldBe(gl, c.expectation, c.message); 242 } 243 } 244 gl.bindTexture(gl.TEXTURE_2D, null); 245 gl.deleteTexture(tex); 246 } 247 } 248 249 return { 250 formatToString: formatToString, 251 insertCaptionedImg: insertCaptionedImg, 252 makeScaledImage: makeScaledImage, 253 testCompressedFormatsListed: testCompressedFormatsListed, 254 testCompressedFormatsUnavailableWhenExtensionDisabled: testCompressedFormatsUnavailableWhenExtensionDisabled, 255 testCorrectEnumValuesInExt: testCorrectEnumValuesInExt, 256 testFormatRestrictionsOnBufferSize: testFormatRestrictionsOnBufferSize, 257 testTexSubImageDimensions: testTexSubImageDimensions, 258 testTexImageLevelDimensions: testTexImageLevelDimensions, 259 testTexStorageLevelDimensions: testTexStorageLevelDimensions, 260 }; 261 262 })();