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.
10 <meta charset=
"utf-8">
11 <link rel=
"stylesheet" href=
"../../resources/js-test-style.css"/>
12 <script src=
"../../js/js-test-pre.js"></script>
13 <script src=
"../../js/webgl-test-utils.js"></script>
14 <script src=
"../../js/tests/compressed-texture-utils.js"></script>
15 <title>WebGL WEBGL_compressed_texture_s3tc and EXT_texture_compression_rgtc Conformance Tests
</title>
18 border:
1px solid black;
33 <div id=
"description"></div>
34 <canvas id=
"canvas" width=
"8" height=
"8" style=
"width: 8px; height: 8px;"></canvas>
35 <div id=
"console"></div>
38 description("This test verifies the functionality of the WEBGL_compressed_texture_s3tc extension, if it is available. It also tests the related formats from the EXT_texture_compression_rgtc extension.");
42 // Acceptable interpolation error depends on endpoints:
43 // 1.0 / 255.0 + 0.03 * max(abs(endpoint0 - endpoint1), abs(endpoint0_p - endpoint1_p))
44 // For simplicity, assume the worst case (e0 is 0.0, e1 is 1.0). After conversion to unorm8, it is 9.
45 const DEFAULT_COLOR_ERROR
= 9;
51 e0 < e1, so it uses 3-color mode
57 3: [ 0, 0, 0, 255] // for BC1 RGB
58 3: [ 0, 0, 0, 0] // for BC1 RGBA
65 Extending this block with opaque alpha and uploading as BC2 or BC3
66 will generate wrong colors because BC2 and BC3 do not have 3-color mode.
68 var img_4x4_rgba_dxt1
= new Uint8Array([
69 0xE0, 0x07, 0x00, 0xF8, 0x1B, 0x1A, 0x15, 0x00
75 Quantized alpha values
84 BC2 has only 4-color mode
97 var img_4x4_rgba_dxt3
= new Uint8Array([
98 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE,
99 0x00, 0xF8, 0xE0, 0x07, 0xE4, 0xE5, 0xEA, 0xFF
105 Alpha block (aka DXT5A)
108 e0 > e1, so using 6 intermediate points
110 255, 0, 219, 182, 146, 109, 73, 36
120 BC3 has only 4-color mode
133 var img_4x4_rgba_dxt5
= new Uint8Array([
134 0xFF, 0x00, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6,
135 0x00, 0xF8, 0xE0, 0x07, 0x1B, 0x5B, 0xAB, 0xFF
138 // BC4 - just the alpha block from BC3 above, interpreted as the red channel.
139 // See http://www.reedbeta.com/blog/understanding-bcn-texture-compression-formats/#bc4
140 // for format details.
141 var img_4x4_r_bc4
= new Uint8Array([
142 0xFF, 0x00, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6,
145 // BC5 - Two BC3 alpha blocks, interpreted as the red and green channels.
146 var img_4x4_rg_bc5
= new Uint8Array([
147 0xFF, 0x00, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6,
148 0x00, 0xFF, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6,
151 // Signed BC4 - change endpoints to use full -1 to 1 range.
152 var img_4x4_signed_r_bc4
= new Uint8Array([
153 0x7F, 0x80, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6,
156 // Signed BC5 - Two BC3 alpha blocks, interpreted as the red and green channels.
157 var img_4x4_signed_rg_bc5
= new Uint8Array([
158 0x7F, 0x80, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6,
159 0x80, 0x7F, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6,
164 8x8 block endpoints use half-intensity values (appear darker than 4x4)
166 var img_8x8_rgba_dxt1
= new Uint8Array([
167 0xe0,0x03,0x00,0x78,0x13,0x10,0x15,0x00,
168 0x0f,0x00,0xe0,0x7b,0x11,0x10,0x15,0x00,
169 0xe0,0x03,0x0f,0x78,0x44,0x45,0x40,0x55,
170 0x0f,0x00,0xef,0x03,0x44,0x45,0x40,0x55
172 var img_8x8_rgba_dxt3
= new Uint8Array([
173 0xf6,0xff,0xf6,0xff,0xff,0xff,0xff,0xff,0x00,0x78,0xe0,0x03,0x44,0x45,0x40,0x55,
174 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe0,0x7b,0x0f,0x00,0x44,0x45,0x40,0x55,
175 0xff,0xff,0xff,0xff,0xf6,0xff,0xf6,0xff,0x0f,0x78,0xe0,0x03,0x11,0x10,0x15,0x00,
176 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0x03,0x0f,0x00,0x11,0x10,0x15,0x00
178 var img_8x8_rgba_dxt5
= new Uint8Array([
179 0xff,0x69,0x01,0x10,0x00,0x00,0x00,0x00,0x00,0x78,0xe0,0x03,0x44,0x45,0x40,0x55,
180 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0x7b,0x0f,0x00,0x44,0x45,0x40,0x55,
181 0xff,0x69,0x00,0x00,0x00,0x01,0x10,0x00,0x0f,0x78,0xe0,0x03,0x11,0x10,0x15,0x00,
182 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xef,0x03,0xef,0x00,0x11,0x10,0x15,0x00
184 var img_8x8_r_bc4
= new Uint8Array([
185 0x7F, 0x00, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6,
186 0x7F, 0x00, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6,
187 0x7F, 0x00, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6,
188 0x7F, 0x00, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6,
190 var img_8x8_rg_bc5
= new Uint8Array([
191 0x7F, 0x00, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6, 0x00, 0x7F, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6,
192 0x7F, 0x00, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6, 0x00, 0x7F, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6,
193 0x7F, 0x00, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6, 0x00, 0x7F, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6,
194 0x7F, 0x00, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6, 0x00, 0x7F, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6,
197 var wtu
= WebGLTestUtils
;
198 var ctu
= CompressedTextureUtils
;
199 var contextVersion
= wtu
.getDefault3DContextVersion();
200 var canvas
= document
.getElementById("canvas");
201 var gl
= wtu
.create3DContext(canvas
, {antialias
: false});
202 var program
= wtu
.setupTexturedQuad(gl
);
207 COMPRESSED_RGB_S3TC_DXT1_EXT
: 0x83F0,
208 COMPRESSED_RGBA_S3TC_DXT1_EXT
: 0x83F1,
209 COMPRESSED_RGBA_S3TC_DXT3_EXT
: 0x83F2,
210 COMPRESSED_RGBA_S3TC_DXT5_EXT
: 0x83F3,
213 var supportedFormats
;
216 testFailed("WebGL context does not exist");
218 testPassed("WebGL context exists");
220 // Run tests with extension disabled
221 ctu
.testCompressedFormatsUnavailableWhenExtensionDisabled(gl
, validFormats
, expectedByteLength
, 4);
223 // Query the extension and store globally so shouldBe can access it
224 ext
= wtu
.getExtensionWithKnownPrefixes(gl
, "WEBGL_compressed_texture_s3tc");
226 testPassed("No WEBGL_compressed_texture_s3tc support -- this is legal");
227 wtu
.runExtensionSupportedTest(gl
, "WEBGL_compressed_texture_s3tc", false);
229 testPassed("Successfully enabled WEBGL_compressed_texture_s3tc extension");
231 wtu
.runExtensionSupportedTest(gl
, "WEBGL_compressed_texture_s3tc", true);
234 ext_rgtc
= wtu
.getExtensionWithKnownPrefixes(gl
, "EXT_texture_compression_rgtc");
237 // Make ctu.formatToString work for rgtc enums.
238 for (const name
in ext_rgtc
)
239 ext
[name
] = ext_rgtc
[name
];
244 function expectedByteLength(width
, height
, format
) {
245 if (format
== validFormats
.COMPRESSED_RGBA_S3TC_DXT3_EXT
|| format
== validFormats
.COMPRESSED_RGBA_S3TC_DXT5_EXT
) {
246 return Math
.floor((width
+ 3) / 4) * Math
.floor((height
+ 3) / 4) * 16;
248 return Math
.floor((width
+ 3) / 4) * Math
.floor((height
+ 3) / 4) * 8;
251 function getBlockDimensions(format
) {
252 return {width
: 4, height
: 4};
255 function runTestExtension() {
257 debug("Testing WEBGL_compressed_texture_s3tc");
259 // Test that enum values are listed correctly in supported formats and in the extension object.
260 ctu
.testCompressedFormatsListed(gl
, validFormats
);
261 ctu
.testCorrectEnumValuesInExt(ext
, validFormats
);
262 // Test that texture upload buffer size is validated correctly.
263 ctu
.testFormatRestrictionsOnBufferSize(gl
, validFormats
, expectedByteLength
, getBlockDimensions
);
271 // Test compressed PBOs with a single format
272 if (contextVersion
>= 2) {
276 // Test TexImage validation on level dimensions combinations.
278 debug("When level equals 0, width and height must be a multiple of 4.");
279 debug("When level is larger than 0, this constraint doesn't apply.");
280 ctu
.testTexImageLevelDimensions(gl
, ext
, validFormats
, expectedByteLength
, getBlockDimensions
,
282 { level
: 0, width
: 4, height
: 3, expectation
: gl
.INVALID_OPERATION
, message
: "0: 4x3" },
283 { level
: 0, width
: 3, height
: 4, expectation
: gl
.INVALID_OPERATION
, message
: "0: 3x4" },
284 { level
: 0, width
: 2, height
: 2, expectation
: gl
.INVALID_OPERATION
, message
: "0: 2x2" },
285 { level
: 0, width
: 4, height
: 4, expectation
: gl
.NO_ERROR
, message
: "0: 4x4" },
286 { level
: 1, width
: 2, height
: 2, expectation
: gl
.NO_ERROR
, message
: "1: 2x2" },
287 { level
: 2, width
: 1, height
: 1, expectation
: gl
.NO_ERROR
, message
: "2: 1x1" },
290 ctu
.testTexSubImageDimensions(gl
, ext
, validFormats
, expectedByteLength
, getBlockDimensions
, 16, 16,
292 { xoffset
: 0, yoffset
: 0, width
: 4, height
: 3,
293 expectation
: gl
.INVALID_OPERATION
, message
: "height is not a multiple of 4" },
294 { xoffset
: 0, yoffset
: 0, width
: 3, height
: 4,
295 expectation
: gl
.INVALID_OPERATION
, message
: "width is not a multiple of 4" },
296 { xoffset
: 1, yoffset
: 0, width
: 4, height
: 4,
297 expectation
: gl
.INVALID_OPERATION
, message
: "xoffset is not a multiple of 4" },
298 { xoffset
: 0, yoffset
: 1, width
: 4, height
: 4,
299 expectation
: gl
.INVALID_OPERATION
, message
: "yoffset is not a multiple of 4" },
300 { xoffset
: 12, yoffset
: 12, width
: 4, height
: 4,
301 expectation
: gl
.NO_ERROR
, message
: "is valid" },
304 if (contextVersion
>= 2) {
306 debug("Testing NPOT textures");
307 ctu
.testTexImageLevelDimensions(gl
, ext
, validFormats
, expectedByteLength
, getBlockDimensions
,
309 { level
: 0, width
: 0, height
: 0, expectation
: gl
.NO_ERROR
, message
: "0: 0x0 is valid" },
310 { level
: 0, width
: 1, height
: 1, expectation
: gl
.INVALID_OPERATION
, message
: "0: 1x1 is invalid" },
311 { level
: 0, width
: 2, height
: 2, expectation
: gl
.INVALID_OPERATION
, message
: "0: 2x2 is invalid" },
312 { level
: 0, width
: 3, height
: 3, expectation
: gl
.INVALID_OPERATION
, message
: "0: 3x3 is invalid" },
313 { level
: 0, width
: 10, height
: 10, expectation
: gl
.INVALID_OPERATION
, message
: "0: 10x10 is invalid" },
314 { level
: 0, width
: 11, height
: 11, expectation
: gl
.INVALID_OPERATION
, message
: "0: 11x11 is invalid" },
315 { level
: 0, width
: 11, height
: 12, expectation
: gl
.INVALID_OPERATION
, message
: "0: 11x12 is invalid" },
316 { level
: 0, width
: 12, height
: 11, expectation
: gl
.INVALID_OPERATION
, message
: "0: 12x11 is invalid" },
317 { level
: 0, width
: 12, height
: 12, expectation
: gl
.NO_ERROR
, message
: "0: 12x12 is valid" },
318 { level
: 1, width
: 0, height
: 0, expectation
: gl
.NO_ERROR
, message
: "1: 0x0, is valid" },
319 { level
: 1, width
: 3, height
: 3, expectation
: gl
.INVALID_OPERATION
, message
: "1: 3x3, is invalid" },
320 { level
: 1, width
: 5, height
: 5, expectation
: gl
.INVALID_OPERATION
, message
: "1: 5x5, is invalid" },
321 { level
: 1, width
: 5, height
: 6, expectation
: gl
.INVALID_OPERATION
, message
: "1: 5x6, is invalid" },
322 { level
: 1, width
: 6, height
: 5, expectation
: gl
.INVALID_OPERATION
, message
: "1: 6x5, is invalid" },
323 { level
: 1, width
: 6, height
: 6, expectation
: gl
.NO_ERROR
, message
: "1: 6x6, is valid" },
324 { level
: 2, width
: 0, height
: 0, expectation
: gl
.NO_ERROR
, message
: "2: 0x0, is valid" },
325 { level
: 2, width
: 3, height
: 3, expectation
: gl
.NO_ERROR
, message
: "2: 3x3, is valid" },
326 { level
: 3, width
: 1, height
: 3, expectation
: gl
.NO_ERROR
, message
: "3: 1x3, is valid" },
327 { level
: 3, width
: 1, height
: 1, expectation
: gl
.NO_ERROR
, message
: "3: 1x1, is valid" },
331 debug("Testing partial updates");
332 ctu
.testTexSubImageDimensions(gl
, ext
, validFormats
, expectedByteLength
, getBlockDimensions
, 12, 12,
334 { xoffset
: 0, yoffset
: 0, width
: 4, height
: 3,
335 expectation
: gl
.INVALID_OPERATION
, message
: "height is not a multiple of 4" },
336 { xoffset
: 0, yoffset
: 0, width
: 3, height
: 4,
337 expectation
: gl
.INVALID_OPERATION
, message
: "width is not a multiple of 4" },
338 { xoffset
: 1, yoffset
: 0, width
: 4, height
: 4,
339 expectation
: gl
.INVALID_OPERATION
, message
: "xoffset is not a multiple of 4" },
340 { xoffset
: 0, yoffset
: 1, width
: 4, height
: 4,
341 expectation
: gl
.INVALID_OPERATION
, message
: "yoffset is not a multiple of 4" },
342 { xoffset
: 8, yoffset
: 8, width
: 4, height
: 4,
343 expectation
: gl
.NO_ERROR
, message
: "is valid" },
347 debug("Testing immutable NPOT textures");
348 ctu
.testTexStorageLevelDimensions(gl
, ext
, validFormats
, expectedByteLength
, getBlockDimensions
,
350 { width
: 12, height
: 12, expectation
: gl
.NO_ERROR
, message
: "0: 12x12 is valid" },
351 { width
: 6, height
: 6, expectation
: gl
.NO_ERROR
, message
: "1: 6x6, is valid" },
352 { width
: 3, height
: 3, expectation
: gl
.NO_ERROR
, message
: "2: 3x3, is valid" },
353 { width
: 1, height
: 1, expectation
: gl
.NO_ERROR
, message
: "3: 1x1, is valid" },
358 function runTestRGTC() {
364 format
: ext_rgtc
.COMPRESSED_RED_RGTC1_EXT
,
370 data
: img_4x4_signed_r_bc4
,
371 format
: ext_rgtc
.COMPRESSED_SIGNED_RED_RGTC1_EXT
,
377 data
: img_4x4_rg_bc5
,
378 format
: ext_rgtc
.COMPRESSED_RED_GREEN_RGTC2_EXT
,
384 data
: img_4x4_signed_rg_bc5
,
385 format
: ext_rgtc
.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT
,
387 error
: 18, // Signed, so twice the normal error.
388 // Experimentally needed by e.g. RTX 3070.
394 format
: ext_rgtc
.COMPRESSED_RED_RGTC1_EXT
,
400 subData
: img_4x4_r_bc4
,
405 data
: img_8x8_rg_bc5
,
406 format
: ext_rgtc
.COMPRESSED_RED_GREEN_RGTC2_EXT
,
412 subData
: img_4x4_rg_bc5
,
415 testDXTTextures(tests
);
418 function testDXT1_RGB() {
423 data
: img_4x4_rgba_dxt1
,
424 format
: ext
.COMPRESSED_RGB_S3TC_DXT1_EXT
,
430 data
: img_8x8_rgba_dxt1
,
431 format
: ext
.COMPRESSED_RGB_S3TC_DXT1_EXT
,
437 subData
: img_4x4_rgba_dxt1
440 testDXTTextures(tests
);
443 function testDXT1_RGBA() {
448 data
: img_4x4_rgba_dxt1
,
449 format
: ext
.COMPRESSED_RGBA_S3TC_DXT1_EXT
,
450 // This is a special case -- the texture is still opaque
457 data
: img_8x8_rgba_dxt1
,
458 format
: ext
.COMPRESSED_RGBA_S3TC_DXT1_EXT
,
459 // This is a special case -- the texture is still opaque
464 testDXTTextures(tests
);
467 function testDXT3_RGBA() {
472 data
: img_4x4_rgba_dxt3
,
473 format
: ext
.COMPRESSED_RGBA_S3TC_DXT3_EXT
,
479 data
: img_8x8_rgba_dxt3
,
480 format
: ext
.COMPRESSED_RGBA_S3TC_DXT3_EXT
,
486 subData
: img_4x4_rgba_dxt3
489 testDXTTextures(tests
);
492 function testDXT5_RGBA() {
497 data
: img_4x4_rgba_dxt5
,
498 format
: ext
.COMPRESSED_RGBA_S3TC_DXT5_EXT
,
504 data
: img_8x8_rgba_dxt5
,
505 format
: ext
.COMPRESSED_RGBA_S3TC_DXT5_EXT
,
511 subData
: img_4x4_rgba_dxt5
514 testDXTTextures(tests
);
517 function testDXTTextures(tests
) {
519 for (var ii
= 0; ii
< tests
.length
; ++ii
) {
520 testDXTTexture(tests
[ii
], false);
521 if (contextVersion
>= 2) {
523 testDXTTexture(tests
[ii
], true);
528 function uncompressDXTBlock(
529 destBuffer
, destX
, destY
, destWidth
, src
, srcOffset
, format
) {
530 // Decoding routines follow D3D11 functional spec wrt
531 // endpoints unquantization and interpolation.
532 // Some hardware may produce slightly different values - it's normal.
534 function make565(src
, offset
) {
535 return src
[offset
+ 0] + (src
[offset
+ 1] << 8);
537 function make8888From565(c
) {
538 // These values exactly match hw decoder when selectors are 0 or 1.
539 function replicateBits(v
, w
) {
540 return (v
<< (8 - w
)) | (v
>> (w
+ w
- 8));
543 replicateBits((c
>> 11) & 0x1F, 5),
544 replicateBits((c
>> 5) & 0x3F, 6),
545 replicateBits((c
>> 0) & 0x1F, 5),
549 function mix(mult
, c0
, c1
, div
) {
551 for (var ii
= 0; ii
< c0
.length
; ++ii
) {
552 // For green channel (6 bits), this interpolation exactly matches hw decoders
554 // For red and blue channels (5 bits), this interpolation exactly
555 // matches only some hw decoders and stays within acceptable range for others.
556 r
[ii
] = Math
.floor((c0
[ii
] * mult
+ c1
[ii
]) / div
+ 0.5);
560 var isBC45
= ext_rgtc
&&
561 (format
== ext_rgtc
.COMPRESSED_RED_RGTC1_EXT
||
562 format
== ext_rgtc
.COMPRESSED_RED_GREEN_RGTC2_EXT
||
563 format
== ext_rgtc
.COMPRESSED_SIGNED_RED_RGTC1_EXT
||
564 format
== ext_rgtc
.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT
);
565 let colorOffset
= srcOffset
;
567 var isDXT1
= format
== ext
.COMPRESSED_RGB_S3TC_DXT1_EXT
||
568 format
== ext
.COMPRESSED_RGBA_S3TC_DXT1_EXT
;
572 var color0
= make565(src
, colorOffset
+ 0);
573 var color1
= make565(src
, colorOffset
+ 2);
574 var c0gtc1
= color0
> color1
|| !isDXT1
;
575 var rgba0
= make8888From565(color0
);
576 var rgba1
= make8888From565(color1
);
580 c0gtc1
? mix(2, rgba0
, rgba1
, 3) : mix(1, rgba0
, rgba1
, 2),
581 c0gtc1
? mix(2, rgba1
, rgba0
, 3) : [0, 0, 0, 255]
584 const isSigned
= ext_rgtc
&& (format
== ext_rgtc
.COMPRESSED_SIGNED_RED_RGTC1_EXT
|| format
== ext_rgtc
.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT
);
585 const signedSrc
= new Int8Array(src
);
587 // yea I know there is a lot of math in this inner loop.
589 for (var yy
= 0; yy
< 4; ++yy
) {
590 var pixels
= src
[colorOffset
+ 4 + yy
];
591 for (var xx
= 0; xx
< 4; ++xx
) {
592 var dstOff
= ((destY
+ yy
) * destWidth
+ destX
+ xx
) * 4;
594 var code
= (pixels
>> (xx
* 2)) & 0x3;
595 var srcColor
= colors
[code
];
599 let decodeAlpha
= (offset
) => {
601 var alpha0
= (isSigned
? signedSrc
: src
)[offset
+ 0];
602 var alpha1
= (isSigned
? signedSrc
: src
)[offset
+ 1];
603 var alphaOff
= (yy
>> 1) * 3 + 2;
605 src
[offset
+ alphaOff
+ 0] +
606 src
[offset
+ alphaOff
+ 1] * 256 +
607 src
[offset
+ alphaOff
+ 2] * 65536;
608 var alphaShift
= (yy
% 2) * 12 + xx
* 3;
609 var alphaCode
= (alphaBits
>> alphaShift
) & 0x7;
610 if (alpha0
> alpha1
) {
619 alpha
= Math
.floor(((8 - alphaCode
) * alpha0
+ (alphaCode
- 1) * alpha1
) / 7.0 + 0.5);
637 alpha
= Math
.floor(((6 - alphaCode
) * alpha0
+ (alphaCode
- 1) * alpha1
) / 5.0 + 0.5);
645 case ext
.COMPRESSED_RGB_S3TC_DXT1_EXT
:
648 case ext
.COMPRESSED_RGBA_S3TC_DXT1_EXT
:
649 alpha
= (code
== 3 && !c0gtc1
) ? 0 : 255;
651 case ext
.COMPRESSED_RGBA_S3TC_DXT3_EXT
:
653 var alpha0
= src
[srcOffset
+ yy
* 2 + (xx
>> 1)];
654 var alpha1
= (alpha0
>> ((xx
% 2) * 4)) & 0xF;
655 alpha
= alpha1
| (alpha1
<< 4);
658 case ext_rgtc
.COMPRESSED_RED_GREEN_RGTC2_EXT
:
659 case ext_rgtc
.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT
:
660 rgChannel2
= decodeAlpha(srcOffset
+ 8);
662 case ext
.COMPRESSED_RGBA_S3TC_DXT5_EXT
:
663 case ext_rgtc
.COMPRESSED_RED_RGTC1_EXT
:
664 case ext_rgtc
.COMPRESSED_SIGNED_RED_RGTC1_EXT
:
665 alpha
= decodeAlpha(srcOffset
);
671 destBuffer
[dstOff
+ 0] = alpha
;
672 destBuffer
[dstOff
+ 1] = rgChannel2
;
673 destBuffer
[dstOff
+ 2] = 0;
674 destBuffer
[dstOff
+ 3] = 255;
676 destBuffer
[dstOff
+ 0] = Math
.max(0, alpha
) * 2;
677 destBuffer
[dstOff
+ 1] = Math
.max(0, rgChannel2
) * 2;
680 destBuffer
[dstOff
+ 0] = srcColor
[0];
681 destBuffer
[dstOff
+ 1] = srcColor
[1];
682 destBuffer
[dstOff
+ 2] = srcColor
[2];
683 destBuffer
[dstOff
+ 3] = alpha
;
689 function getBlockSize(format
) {
690 var isDXT1
= format
== ext
.COMPRESSED_RGB_S3TC_DXT1_EXT
||
691 format
== ext
.COMPRESSED_RGBA_S3TC_DXT1_EXT
;
692 var isBC4
= ext_rgtc
&& (format
== ext_rgtc
.COMPRESSED_RED_RGTC1_EXT
|| format
== ext_rgtc
.COMPRESSED_SIGNED_RED_RGTC1_EXT
);
693 return isDXT1
|| isBC4
? 8 : 16;
696 function uncompressDXT(width
, height
, data
, format
) {
697 if (width
% 4 || height
% 4) throw "bad width or height";
699 var dest
= new Uint8Array(width
* height
* 4);
700 var blocksAcross
= width
/ 4;
701 var blocksDown
= height
/ 4;
702 var blockSize
= getBlockSize(format
);
703 for (var yy
= 0; yy
< blocksDown
; ++yy
) {
704 for (var xx
= 0; xx
< blocksAcross
; ++xx
) {
706 dest
, xx
* 4, yy
* 4, width
, data
,
707 (yy
* blocksAcross
+ xx
) * blockSize
, format
);
713 function uncompressDXTIntoSubRegion(width
, height
, subX0
, subY0
, subWidth
, subHeight
, data
, format
)
715 if (width
% 4 || height
% 4 || subX0
% 4 || subY0
% 4 || subWidth
% 4 || subHeight
% 4)
716 throw "bad dimension";
718 var dest
= new Uint8Array(width
* height
* 4);
719 // Zero-filled DXT1 or BC4/5 texture represents [0, 0, 0, 255]
720 if (format
== ext
.COMPRESSED_RGB_S3TC_DXT1_EXT
|| format
== ext
.COMPRESSED_RGBA_S3TC_DXT1_EXT
||
721 format
== ext
.COMPRESSED_RED_RGTC1_EXT
|| format
== ext
.COMPRESSED_SIGNED_RED_RGTC1_EXT
||
722 format
== ext
.COMPRESSED_RED_GREEN_RGTC2_EXT
|| format
== ext
.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT
) {
723 for (var i
= 3; i
< dest
.length
; i
+= 4) dest
[i
] = 255;
725 var blocksAcross
= subWidth
/ 4;
726 var blocksDown
= subHeight
/ 4;
727 var blockSize
= getBlockSize(format
);
728 for (var yy
= 0; yy
< blocksDown
; ++yy
) {
729 for (var xx
= 0; xx
< blocksAcross
; ++xx
) {
731 dest
, subX0
+ xx
* 4, subY0
+ yy
* 4, width
, data
,
732 (yy
* blocksAcross
+ xx
) * blockSize
, format
);
738 function copyRect(data
, srcX
, srcY
, dstX
, dstY
, width
, height
, stride
) {
739 var bytesPerLine
= width
* 4;
740 var srcOffset
= srcX
* 4 + srcY
* stride
;
741 var dstOffset
= dstX
* 4 + dstY
* stride
;
742 for (; height
> 0; --height
) {
743 for (var ii
= 0; ii
< bytesPerLine
; ++ii
) {
744 data
[dstOffset
+ ii
] = data
[srcOffset
+ ii
];
751 function testDXTTexture(test
, useTexStorage
) {
752 test
.error
= test
.error
|| DEFAULT_COLOR_ERROR
;
754 var data
= new Uint8Array(test
.data
);
755 var width
= test
.width
;
756 var height
= test
.height
;
757 var format
= test
.format
;
759 var uncompressedData
= uncompressDXT(width
, height
, data
, format
);
761 canvas
.width
= width
;
762 canvas
.height
= height
;
763 gl
.viewport(0, 0, width
, height
);
764 debug("testing " + ctu
.formatToString(ext
, format
) + " " + width
+ "x" + height
+
765 (useTexStorage
? " via texStorage2D" : " via compressedTexImage2D"));
767 var tex
= gl
.createTexture();
768 gl
.bindTexture(gl
.TEXTURE_2D
, tex
);
769 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_WRAP_S
, gl
.CLAMP_TO_EDGE
);
770 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_WRAP_T
, gl
.CLAMP_TO_EDGE
);
771 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_MIN_FILTER
, gl
.NEAREST
);
772 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_MAG_FILTER
, gl
.NEAREST
);
775 var uncompressedDataSub
= uncompressDXTIntoSubRegion(
776 width
, height
, test
.subX0
, test
.subY0
, test
.subWidth
, test
.subHeight
, test
.subData
, format
);
777 var tex1
= gl
.createTexture();
778 gl
.bindTexture(gl
.TEXTURE_2D
, tex1
);
779 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_WRAP_S
, gl
.CLAMP_TO_EDGE
);
780 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_WRAP_T
, gl
.CLAMP_TO_EDGE
);
781 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_MIN_FILTER
, gl
.NEAREST
);
782 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_MAG_FILTER
, gl
.NEAREST
);
784 gl
.texStorage2D(gl
.TEXTURE_2D
, 1, format
, width
, height
);
785 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "allocating compressed texture via texStorage2D");
786 gl
.compressedTexSubImage2D(
787 gl
.TEXTURE_2D
, 0, test
.subX0
, test
.subY0
, test
.subWidth
, test
.subHeight
, format
, test
.subData
);
788 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "uploading compressed texture data via compressedTexSubImage2D");
790 wtu
.clearAndDrawUnitQuad(gl
);
791 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawing unit quad 1");
792 compareRect(width
, height
, test
.channels
, uncompressedDataSub
, "NEAREST", test
.error
);
794 // Clean up and recover
795 gl
.deleteTexture(tex1
);
796 gl
.bindTexture(gl
.TEXTURE_2D
, tex
);
799 gl
.texStorage2D(gl
.TEXTURE_2D
, 1, format
, width
, height
);
800 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "allocating compressed texture via texStorage2D");
801 wtu
.clearAndDrawUnitQuad(gl
);
802 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawing unit quad");
803 var clearColor
= (test
.hasAlpha
? [0, 0, 0, 0] : [0, 0, 0, 255]);
804 wtu
.checkCanvas(gl
, clearColor
, "texture should be initialized to black");
805 gl
.compressedTexSubImage2D(gl
.TEXTURE_2D
, 0, 0, 0, width
, height
, format
, data
);
806 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "uploading compressed texture data via compressedTexSubImage2D");
808 gl
.compressedTexImage2D(gl
.TEXTURE_2D
, 0, format
, width
, height
, 0, data
);
809 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "uploading compressed texture");
811 gl
.generateMipmap(gl
.TEXTURE_2D
);
812 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "trying to generate mipmaps from compressed texture");
813 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "after clearing generateMipmap error");
814 wtu
.clearAndDrawUnitQuad(gl
);
815 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawing unit quad 1");
816 compareRect(width
, height
, test
.channels
, uncompressedData
, "NEAREST", test
.error
);
817 // Test again with linear filtering.
818 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_MIN_FILTER
, gl
.LINEAR
);
819 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_MAG_FILTER
, gl
.LINEAR
);
820 wtu
.clearAndDrawUnitQuad(gl
);
821 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawing unit quad 2");
822 compareRect(width
, height
, test
.channels
, uncompressedData
, "LINEAR", test
.error
);
824 if (!useTexStorage
) {
825 // It's not allowed to redefine textures defined via texStorage2D.
826 gl
.compressedTexImage2D(gl
.TEXTURE_2D
, 0, format
, width
, height
, 1, data
);
827 wtu
.glErrorShouldBe(gl
, gl
.INVALID_VALUE
, "non 0 border");
829 gl
.compressedTexImage2D(gl
.TEXTURE_2D
, 0, format
, width
+ 4, height
, 0, data
);
830 wtu
.glErrorShouldBe(gl
, gl
.INVALID_VALUE
, "data size does not match dimensions");
831 gl
.compressedTexImage2D(gl
.TEXTURE_2D
, 0, format
, width
, height
+ 4, 0, data
);
832 wtu
.glErrorShouldBe(gl
, gl
.INVALID_VALUE
, "data size does not match dimensions");
833 gl
.compressedTexImage2D(gl
.TEXTURE_2D
, 0, format
, width
- 4, height
, 0, data
);
834 wtu
.glErrorShouldBe(gl
, gl
.INVALID_VALUE
, "data size does not match dimensions");
835 gl
.compressedTexImage2D(gl
.TEXTURE_2D
, 0, format
, width
, height
- 4, 0, data
);
836 wtu
.glErrorShouldBe(gl
, gl
.INVALID_VALUE
, "data size does not match dimensions");
838 gl
.compressedTexImage2D(gl
.TEXTURE_2D
, 0, format
, width
- 1, height
, 0, data
);
839 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "invalid dimensions");
840 gl
.compressedTexImage2D(gl
.TEXTURE_2D
, 0, format
, width
- 2, height
, 0, data
);
841 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "invalid dimensions");
842 gl
.compressedTexImage2D(gl
.TEXTURE_2D
, 0, format
, width
, height
- 1, 0, data
);
843 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "invalid dimensions");
844 gl
.compressedTexImage2D(gl
.TEXTURE_2D
, 0, format
, width
, height
- 2, 0, data
);
845 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "invalid dimensions");
848 // The width/height of the implied base level must be a multiple of the block size.
849 gl
.compressedTexImage2D(gl
.TEXTURE_2D
, 1, format
, 1, height
, 0, data
);
850 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "invalid dimensions for level > 0");
851 gl
.compressedTexImage2D(gl
.TEXTURE_2D
, 1, format
, 2, height
, 0, data
);
852 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "valid dimensions for level > 0");
855 // The width/height of the implied base level must be a multiple of the block size.
856 gl
.compressedTexImage2D(gl
.TEXTURE_2D
, 1, format
, width
, 1, 0, data
);
857 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "invalid dimensions for level > 0");
858 gl
.compressedTexImage2D(gl
.TEXTURE_2D
, 1, format
, width
, 2, 0, data
);
859 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "valid dimensions for level > 0");
863 // pick a wrong format that uses the same amount of data.
866 case ext
.COMPRESSED_RGB_S3TC_DXT1_EXT
:
867 wrongFormat
= ext
.COMPRESSED_RGBA_S3TC_DXT1_EXT
;
869 case ext
.COMPRESSED_RGBA_S3TC_DXT1_EXT
:
870 wrongFormat
= ext
.COMPRESSED_RGB_S3TC_DXT1_EXT
;
872 case ext
.COMPRESSED_RGBA_S3TC_DXT3_EXT
:
873 wrongFormat
= ext
.COMPRESSED_RGBA_S3TC_DXT5_EXT
;
875 case ext
.COMPRESSED_RGBA_S3TC_DXT5_EXT
:
876 wrongFormat
= ext
.COMPRESSED_RGBA_S3TC_DXT3_EXT
;
878 case ext_rgtc
.COMPRESSED_RED_RGTC1_EXT
:
879 case ext_rgtc
.COMPRESSED_SIGNED_RED_RGTC1_EXT
:
880 wrongFormat
= ext_rgtc
.COMPRESSED_RED_GREEN_RGTC2_EXT
;
882 case ext_rgtc
.COMPRESSED_RED_GREEN_RGTC2_EXT
:
883 case ext_rgtc
.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT
:
884 wrongFormat
= ext_rgtc
.COMPRESSED_RED_RGTC1_EXT
;
888 gl
.compressedTexSubImage2D(gl
.TEXTURE_2D
, 0, 0, 0, width
, height
, wrongFormat
, data
);
889 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "format does not match");
891 gl
.compressedTexSubImage2D(gl
.TEXTURE_2D
, 0, 4, 0, width
, height
, format
, data
);
892 wtu
.glErrorShouldBe(gl
, gl
.INVALID_VALUE
, "dimension out of range");
893 gl
.compressedTexSubImage2D(gl
.TEXTURE_2D
, 0, 0, 4, width
, height
, format
, data
);
894 wtu
.glErrorShouldBe(gl
, gl
.INVALID_VALUE
, "dimension out of range");
896 gl
.compressedTexSubImage2D(gl
.TEXTURE_2D
, 0, 0, 0, width
+ 4, height
, format
, data
);
897 wtu
.glErrorShouldBe(gl
, gl
.INVALID_VALUE
, "data size does not match dimensions");
898 gl
.compressedTexSubImage2D(gl
.TEXTURE_2D
, 0, 0, 0, width
, height
+ 4, format
, data
);
899 wtu
.glErrorShouldBe(gl
, gl
.INVALID_VALUE
, "data size does not match dimensions");
900 gl
.compressedTexSubImage2D(gl
.TEXTURE_2D
, 0, 0, 0, width
- 4, height
, format
, data
);
901 wtu
.glErrorShouldBe(gl
, gl
.INVALID_VALUE
, "data size does not match dimensions");
902 gl
.compressedTexSubImage2D(gl
.TEXTURE_2D
, 0, 0, 0, width
, height
- 4, format
, data
);
903 wtu
.glErrorShouldBe(gl
, gl
.INVALID_VALUE
, "data size does not match dimensions");
905 gl
.compressedTexSubImage2D(gl
.TEXTURE_2D
, 0, 0, 0, width
- 1, height
, format
, data
);
906 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "invalid dimensions");
907 gl
.compressedTexSubImage2D(gl
.TEXTURE_2D
, 0, 0, 0, width
- 2, height
, format
, data
);
908 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "invalid dimensions");
909 gl
.compressedTexSubImage2D(gl
.TEXTURE_2D
, 0, 0, 0, width
, height
- 1, format
, data
);
910 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "invalid dimensions");
911 gl
.compressedTexSubImage2D(gl
.TEXTURE_2D
, 0, 0, 0, width
, height
- 2, format
, data
);
912 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "invalid dimensions");
914 var subData
= new Uint8Array(data
.buffer
, 0, getBlockSize(format
));
916 if (width
== 8 && height
== 8) {
917 gl
.compressedTexSubImage2D(gl
.TEXTURE_2D
, 0, 1, 0, 4, 4, format
, subData
);
918 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "invalid offset");
919 gl
.compressedTexSubImage2D(gl
.TEXTURE_2D
, 0, 0, 1, 4, 4, format
, subData
);
920 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "invalid offset");
923 var stride
= width
* 4;
924 for (var yoff
= 0; yoff
< height
; yoff
+= 4) {
925 for (var xoff
= 0; xoff
< width
; xoff
+= 4) {
926 copyRect(uncompressedData
, 0, 0, xoff
, yoff
, 4, 4, stride
);
927 gl
.compressedTexSubImage2D(gl
.TEXTURE_2D
, 0, xoff
, yoff
, 4, 4, format
, subData
);
928 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "uploading compressed texture");
929 // First test NEAREST filtering.
930 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_MIN_FILTER
, gl
.NEAREST
);
931 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_MAG_FILTER
, gl
.NEAREST
);
932 wtu
.clearAndDrawUnitQuad(gl
);
933 compareRect(width
, height
, test
.channels
, uncompressedData
, "NEAREST", test
.error
);
934 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawing unit quad");
935 // Next test LINEAR filtering.
936 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_MIN_FILTER
, gl
.LINEAR
);
937 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_MAG_FILTER
, gl
.LINEAR
);
938 wtu
.clearAndDrawUnitQuad(gl
);
939 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawing unit quad");
940 compareRect(width
, height
, test
.channels
, uncompressedData
, "LINEAR", test
.error
);
945 function testDXT5_RGBA_PBO() {
947 debug("testing PBO uploads");
951 var data
= img_8x8_rgba_dxt5
;
952 var format
= ext
.COMPRESSED_RGBA_S3TC_DXT5_EXT
;
953 var uncompressedData
= uncompressDXT(width
, height
, data
, format
);
955 var tex
= gl
.createTexture();
957 // First, PBO size = image size
958 var pbo1
= gl
.createBuffer();
959 gl
.bindBuffer(gl
.PIXEL_UNPACK_BUFFER
, pbo1
);
960 gl
.bufferData(gl
.PIXEL_UNPACK_BUFFER
, data
, gl
.STATIC_DRAW
);
961 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "uploading a PBO");
963 gl
.bindTexture(gl
.TEXTURE_2D
, tex
);
964 gl
.texStorage2D(gl
.TEXTURE_2D
, 1, format
, width
, height
);
965 gl
.compressedTexSubImage2D(gl
.TEXTURE_2D
, 0, 0, 0, width
, height
, format
, data
.length
, 0);
966 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "uploading a texture from a PBO");
968 gl
.bindBuffer(gl
.PIXEL_UNPACK_BUFFER
, null);
969 wtu
.clearAndDrawUnitQuad(gl
);
970 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawing unit quad");
971 compareRect(width
, height
, channels
, uncompressedData
, "NEAREST", DEFAULT_COLOR_ERROR
);
973 // Clear the texture before the next test
974 gl
.bindBuffer(gl
.PIXEL_UNPACK_BUFFER
, null);
975 gl
.compressedTexSubImage2D(gl
.TEXTURE_2D
, 0, 0, 0, width
, height
, format
, new Uint8Array(data
.length
));
977 // Second, image is just a subrange of the PBO
978 var pbo2
= gl
.createBuffer();
979 gl
.bindBuffer(gl
.PIXEL_UNPACK_BUFFER
, pbo2
);
980 gl
.bufferData(gl
.PIXEL_UNPACK_BUFFER
, data
.length
*3, gl
.STATIC_DRAW
);
981 gl
.bufferSubData(gl
.PIXEL_UNPACK_BUFFER
, data
.length
, data
);
982 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "uploading a PBO subrange");
983 gl
.compressedTexSubImage2D(gl
.TEXTURE_2D
, 0, 0, 0, width
, height
, format
, data
.length
, data
.length
);
984 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "uploading a texture from a PBO subrange");
985 gl
.bindBuffer(gl
.PIXEL_UNPACK_BUFFER
, null);
986 wtu
.clearAndDrawUnitQuad(gl
);
987 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawing unit quad");
988 compareRect(width
, height
, channels
, uncompressedData
, "NEAREST", DEFAULT_COLOR_ERROR
);
991 function compareRect(width
, height
, channels
, expectedData
, filteringMode
, colorError
) {
992 var actual
= new Uint8Array(width
* height
* 4);
993 gl
.readPixels(0, 0, width
, height
, gl
.RGBA
, gl
.UNSIGNED_BYTE
, actual
);
994 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "reading back pixels");
996 var div
= document
.createElement("div");
997 div
.className
= "testimages";
998 ctu
.insertCaptionedImg(div
, "expected", ctu
.makeScaledImage(width
, height
, width
, expectedData
, true));
999 ctu
.insertCaptionedImg(div
, "actual", ctu
.makeScaledImage(width
, height
, width
, actual
, true));
1000 div
.appendChild(document
.createElement('br'));
1001 document
.getElementById("console").appendChild(div
);
1004 for (var yy
= 0; yy
< height
; ++yy
) {
1005 for (var xx
= 0; xx
< width
; ++xx
) {
1006 var offset
= (yy
* width
+ xx
) * 4;
1007 var expected
= expectedData
.slice(offset
, offset
+ 4);
1008 const was
= actual
.slice(offset
, offset
+ 4);
1010 // Compare RGB values
1011 for (var jj
= 0; jj
< 3; ++jj
) {
1012 if (Math
.abs(was
[jj
] - expected
[jj
]) > colorError
) {
1014 testFailed(`RGB at (${xx}, ${yy}) expected: ${expected}` +
1015 ` +/- ${colorError}, was ${was}`);
1020 if (channels
== 3) {
1021 // BC1 RGB is allowed to be mapped to BC1 RGBA.
1022 // In such a case, 3-color mode black value can be transparent:
1023 // [0, 0, 0, 0] instead of [0, 0, 0, 255].
1025 if (actual
[offset
+ 3] != expected
[3]) {
1026 // Got non-opaque value for opaque format
1028 // Check RGB values. Notice, that the condition here
1029 // is more permissive than needed since we don't have
1030 // compressed data at this point.
1035 debug("<b>DXT1 RGB is mapped to DXT1 RGBA</b>");
1038 testFailed('Alpha at (' + xx
+ ', ' + yy
+
1039 ') expected: ' + expected
[3] + ' was ' + was
);
1043 // Compare Alpha values
1044 // Acceptable interpolation error depends on endpoints:
1045 // 1.0 / 65535.0 + 0.03 * max(abs(endpoint0 - endpoint1), abs(endpoint0_p - endpoint1_p))
1046 // For simplicity, assume the worst case (e0 is 0.0, e1 is 1.0). After conversion to unorm8, it is 8.
1047 if (Math
.abs(was
[3] - expected
[3]) > 8) {
1049 testFailed('Alpha at (' + xx
+ ', ' + yy
+
1050 ') expected: ' + expected
+ ' +/- 8 was ' + was
);
1056 testPassed("texture rendered correctly with " + filteringMode
+ " filtering");
1061 var successfullyParsed
= true;
1063 <script src=
"../../js/js-test-post.js"></script>