blob: 9bcf41e147c8d6a8fed5418ddde43e01e7ea2861 [file] [log] [blame]
<!--
/*
** Copyright (c) 2012-2016 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="../../resources/js-test-style.css"/>
<script src="../../js/js-test-pre.js"></script>
<script src="../../js/webgl-test-utils.js"></script>
<title>WebGL WEBGL_compressed_texture_s3tc_srgb Conformance Tests</title>
<style>
img {
border: 1px solid black;
margin-right: 1em;
}
.testimages {
}
.testimages br {
clear: both;
}
.testimages > div {
float: left;
margin: 1em;
}
</style>
</head>
<body>
<div id="description"></div>
<canvas id="canvas" width="8" height="8" style="width: 8px; height: 8px;"></canvas>
<div id="console"></div>
<script>
"use strict";
description("This test verifies the functionality of the WEBGL_compressed_texture_s3tc_srgb extension, if it is available.");
debug("");
// This is the original image from webgl-compressed-texture-s3tc.html but
// scaled to 0x40..0xbf inclusive and alpha changed from 0x69 to 0x7f.
var img_4x4_rgba_raw = new Uint8Array([
0xbf,0x40,0x40,0x7f,0x40,0xbf,0x40,0xff,0xbf,0x40,0x40,0xff,0x40,0xbf,0x40,0xff,
0x40,0xbf,0x40,0xff,0x40,0xbf,0x40,0xff,0xbf,0x40,0x40,0xff,0x40,0xbf,0x40,0xff,
0xbf,0x40,0x40,0xff,0xbf,0x40,0x40,0xff,0xbf,0x40,0x40,0xff,0x40,0xbf,0x40,0xff,
0x40,0xbf,0x40,0xff,0x40,0xbf,0x40,0xff,0x40,0xbf,0x40,0xff,0x40,0xbf,0x40,0xff,
]);
var img_4x4_rgb_dxt1 = new Uint8Array([
0x08,0xba,0xe8,0x45,0x44,0x45,0x40,0x55,
]);
var img_4x4_rgba_dxt1 = new Uint8Array([
0xa8,0x4d,0x48,0xb2,0x13,0x10,0x15,0x00,
]);
var img_4x4_rgba_dxt3 = new Uint8Array([
0xf7,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x08,0xba,0xe8,0x45,0x44,0x45,0x40,0x55,
]);
var img_4x4_rgba_dxt5 = new Uint8Array([
0xff,0x7f,0x01,0x00,0x00,0x00,0x00,0x00,0x08,0xba,0xe8,0x45,0x44,0x45,0x40,0x55,
]);
var img_8x8_rgba_raw = new Uint8Array([
0xdf,0xa0,0xa0,0x7f,0x40,0xbf,0x40,0xff,0xbf,0x40,0x40,0xff,0x40,0xbf,0x40,0xff,0xbf,0xbf,0x40,0xff,0x40,0x40,0xbf,0xff,0xbf,0xbf,0x40,0xff,0x40,0x40,0xbf,0xff,
0x40,0xbf,0x40,0x7f,0x40,0xbf,0x40,0xff,0xbf,0x40,0x40,0xff,0x40,0xbf,0x40,0xff,0x40,0x40,0xbf,0xff,0x40,0x40,0xbf,0xff,0xbf,0xbf,0x40,0xff,0x40,0x40,0xbf,0xff,
0xbf,0x40,0x40,0xff,0xbf,0x40,0x40,0xff,0xbf,0x40,0x40,0xff,0x40,0xbf,0x40,0xff,0xbf,0xbf,0x40,0xff,0xbf,0xbf,0x40,0xff,0xbf,0xbf,0x40,0xff,0x40,0x40,0xbf,0xff,
0x40,0xbf,0x40,0xff,0x40,0xbf,0x40,0xff,0x40,0xbf,0x40,0xff,0x40,0xbf,0x40,0xff,0x40,0x40,0xbf,0xff,0x40,0x40,0xbf,0xff,0x40,0x40,0xbf,0xff,0x40,0x40,0xbf,0xff,
0x40,0xbf,0x40,0xff,0xbf,0x40,0xbf,0xff,0x40,0xbf,0x40,0xff,0xbf,0x40,0xbf,0xff,0x40,0x40,0xbf,0xff,0x40,0xbf,0xbf,0xff,0x40,0x40,0xbf,0xff,0x40,0xbf,0xbf,0xff,
0xbf,0x40,0xbf,0xff,0xbf,0x40,0xbf,0xff,0x40,0xbf,0x40,0xff,0xbf,0x40,0xbf,0xff,0x40,0xbf,0xbf,0xff,0x40,0xbf,0xbf,0xff,0x40,0x40,0xbf,0xff,0x40,0xbf,0xbf,0xff,
0x40,0xbf,0x40,0x7f,0x40,0xbf,0x40,0xff,0x40,0xbf,0x40,0xff,0xbf,0x40,0xbf,0xff,0x40,0x40,0xbf,0xff,0x40,0x40,0xbf,0xff,0x40,0x40,0xbf,0xff,0x40,0xbf,0xbf,0xff,
0xbf,0x40,0xbf,0x7f,0xbf,0x40,0xbf,0xff,0xbf,0x40,0xbf,0xff,0xbf,0x40,0xbf,0xff,0x40,0xbf,0xbf,0xff,0x40,0xbf,0xbf,0xff,0x40,0xbf,0xbf,0xff,0x40,0xbf,0xbf,0xff,
]);
var img_8x8_rgb_dxt1 = new Uint8Array([
0x08,0xba,0xe8,0x45,0x44,0x45,0x40,0x55,0xe8,0xbd,0x17,0x42,0x44,0x45,0x40,0x55,0x17,0xba,0xe8,0x45,0x11,0x10,0x15,0x00,0xf7,0x45,0x17,0x42,0x11,0x10,0x15,0x00,
]);
var img_8x8_rgba_dxt1 = new Uint8Array([
0xa8,0x4d,0x48,0xb2,0x13,0x10,0x15,0x00,0xe8,0xbd,0x17,0x42,0x44,0x45,0x40,0x55,0x17,0xba,0xe8,0x45,0x11,0x10,0x15,0x00,0xf7,0x45,0x17,0x42,0x11,0x10,0x15,0x00,
]);
var img_8x8_rgba_dxt3 = new Uint8Array([
0xf7,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x08,0xba,0xe8,0x45,0x44,0x45,0x40,0x55,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe8,0xbd,0x17,0x42,0x44,0x45,0x40,0x55,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x17,0xba,0xe8,0x45,0x11,0x10,0x15,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf7,0x45,0x17,0x42,0x11,0x10,0x15,0x00,
]);
var img_8x8_rgba_dxt5 = new Uint8Array([
0xff,0x7f,0x01,0x00,0x00,0x00,0x00,0x00,0x08,0xba,0xe8,0x45,0x44,0x45,0x40,0x55,0xff,0xff,0x49,0x92,0x24,0x49,0x92,0x24,0xe8,0xbd,0x17,0x42,0x44,0x45,0x40,0x55,0xff,0xff,0x49,0x92,0x24,0x49,0x92,0x24,0x17,0xba,0xe8,0x45,0x11,0x10,0x15,0x00,0xff,0xff,0x49,0x92,0x24,0x49,0x92,0x24,0xf7,0x45,0x17,0x42,0x11,0x10,0x15,0x00,
]);
var wtu = WebGLTestUtils;
var contextVersion = wtu.getDefault3DContextVersion();
var canvas = document.getElementById("canvas");
var gl = wtu.create3DContext(canvas, {antialias: false});
var program = wtu.setupTexturedQuad(gl);
var ext = null;
var vao = null;
var validFormats = {
COMPRESSED_SRGB_S3TC_DXT1_EXT : 0x8C4C,
COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT : 0x8C4D,
COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT : 0x8C4E,
COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT : 0x8C4F,
};
var name;
var supportedFormats;
if (!gl) {
testFailed("WebGL context does not exist");
} else {
testPassed("WebGL context exists");
// Run tests with extension disabled
runTestDisabled();
// Query the extension and store globally so shouldBe can access it
ext = wtu.getExtensionWithKnownPrefixes(gl, "WEBGL_compressed_texture_s3tc_srgb");
if (!ext) {
testPassed("No WEBGL_compressed_texture_s3tc_srgb support -- this is legal");
runSupportedTest(false);
} else {
testPassed("Successfully enabled WEBGL_compressed_texture_s3tc_srgb extension");
runSupportedTest(true);
runTestExtension();
}
}
function runSupportedTest(extensionEnabled) {
var name = wtu.getSupportedExtensionWithKnownPrefixes(gl, "WEBGL_compressed_texture_s3tc_srgb");
if (name !== undefined) {
if (extensionEnabled) {
testPassed("WEBGL_compressed_texture_s3tc_srgb listed as supported and getExtension succeeded");
} else {
testFailed("WEBGL_compressed_texture_s3tc_srgb listed as supported but getExtension failed");
}
} else {
if (extensionEnabled) {
testFailed("WEBGL_compressed_texture_s3tc_srgb not listed as supported but getExtension succeeded");
} else {
testPassed("WEBGL_compressed_texture_s3tc_srgb not listed as supported and getExtension failed -- this is legal");
}
}
}
function runTestDisabled() {
debug("Testing binding enum with extension disabled");
supportedFormats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS);
shouldBe("supportedFormats", "[]");
}
function formatExists(format, supportedFormats) {
for (var ii = 0; ii < supportedFormats.length; ++ii) {
if (format == supportedFormats[ii]) {
testPassed("supported format " + formatToString(format) + " is exists");
return;
}
}
testFailed("supported format " + formatToString(format) + " does not exist");
}
function formatToString(format) {
for (var p in ext) {
if (ext[p] == format) {
return p;
}
}
return "0x" + format.toString(16);
}
function runTestExtension() {
debug("Testing WEBGL_compressed_texture_s3tc_srgb");
// check that all format enums exist.
for (name in validFormats) {
var expected = "0x" + validFormats[name].toString(16);
var actual = "ext['" + name + "']";
shouldBe(actual, expected);
}
supportedFormats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS);
// There should be exactly 4 formats for both WebGL 1.0 and WebGL 2.0.
shouldBe("supportedFormats.length", "4");
// check that all 4 formats exist
for (var name in validFormats.length) {
formatExists(validFormats[name], supportedFormats);
}
// Test each format
testDXT1_SRGB();
testDXT1_SRGB_ALPHA();
testDXT3_SRGB_ALPHA();
testDXT5_SRGB_ALPHA();
}
function testDXT1_SRGB() {
var tests = [
{ width: 4,
height: 4,
channels: 3,
data: img_4x4_rgb_dxt1,
format: ext.COMPRESSED_SRGB_S3TC_DXT1_EXT,
hasAlpha: false,
},
{ width: 8,
height: 8,
channels: 3,
data: img_8x8_rgb_dxt1,
format: ext.COMPRESSED_SRGB_S3TC_DXT1_EXT,
hasAlpha: false,
}
];
testDXTTextures(tests);
}
function testDXT1_SRGB_ALPHA() {
var tests = [
{ width: 4,
height: 4,
channels: 4,
data: img_4x4_rgba_dxt1,
format: ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT,
// This is a special case -- the texture is still opaque
// though it's RGBA.
hasAlpha: false,
},
{ width: 8,
height: 8,
channels: 4,
data: img_8x8_rgba_dxt1,
format: ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT,
// This is a special case -- the texture is still opaque
// though it's RGBA.
hasAlpha: false,
}
];
testDXTTextures(tests);
}
function testDXT3_SRGB_ALPHA() {
var tests = [
{ width: 4,
height: 4,
channels: 4,
data: img_4x4_rgba_dxt3,
format: ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT,
hasAlpha: true,
},
{ width: 8,
height: 8,
channels: 4,
data: img_8x8_rgba_dxt3,
format: ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT,
hasAlpha: true,
}
];
testDXTTextures(tests);
}
function testDXT5_SRGB_ALPHA() {
var tests = [
{ width: 4,
height: 4,
channels: 4,
data: img_4x4_rgba_dxt5,
format: ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT,
hasAlpha: true,
},
{ width: 8,
height: 8,
channels: 4,
data: img_8x8_rgba_dxt5,
format: ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT,
hasAlpha: true,
}
];
testDXTTextures(tests);
}
function testDXTTextures(tests) {
debug("<hr/>");
for (var ii = 0; ii < tests.length; ++ii) {
testDXTTexture(tests[ii], false);
if (contextVersion >= 2) {
debug("<br/>");
testDXTTexture(tests[ii], true);
}
}
}
function uncompressDXTBlockSRGB(
destBuffer, destX, destY, destWidth, src, srcOffset, format) {
function make565(src, offset) {
return src[offset + 0] + src[offset + 1] * 256;
}
function make8888From565(c) {
return [
Math.floor(((c >> 11) & 0x1F) * 255 / 31),
Math.floor(((c >> 5) & 0x3F) * 255 / 63),
Math.floor(((c >> 0) & 0x1F) * 255 / 31),
255
];
}
function mix(mult, c0, c1, div) {
var r = [];
for (var ii = 0; ii < c0.length; ++ii) {
r[ii] = Math.floor((c0[ii] * mult + c1[ii]) / div);
}
return r;
}
var isDXT1 = format == ext.COMPRESSED_SRGB_S3TC_DXT1_EXT ||
format == ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
var colorOffset = srcOffset + (isDXT1 ? 0 : 8);
var color0 = make565(src, colorOffset + 0);
var color1 = make565(src, colorOffset + 2);
var c0gtc1 = color0 > color1 || !isDXT1;
var rgba0 = make8888From565(color0);
var rgba1 = make8888From565(color1);
var colors = [
rgba0,
rgba1,
c0gtc1 ? mix(2, rgba0, rgba1, 3) : mix(1, rgba0, rgba1, 2),
c0gtc1 ? mix(2, rgba1, rgba0, 3) : [0, 0, 0, 255]
];
// yea I know there is a lot of math in this inner loop.
// so sue me.
for (var yy = 0; yy < 4; ++yy) {
var pixels = src[colorOffset + 4 + yy];
for (var xx = 0; xx < 4; ++xx) {
var dstOff = ((destY + yy) * destWidth + destX + xx) * 4;
var code = (pixels >> (xx * 2)) & 0x3;
var srcColor = colors[code];
var alpha;
switch (format) {
case ext.COMPRESSED_SRGB_S3TC_DXT1_EXT:
alpha = 255;
break;
case ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
alpha = (code == 3 && !c0gtc1) ? 0 : 255;
break;
case ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
{
var alpha0 = src[srcOffset + yy * 2 + Math.floor(xx / 2)];
var alpha1 = (alpha0 >> ((xx % 2) * 4)) & 0xF;
alpha = alpha1 | (alpha1 << 4);
}
break;
case ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
{
var alpha0 = src[srcOffset + 0];
var alpha1 = src[srcOffset + 1];
var alphaOff = Math.floor(yy / 2) * 3 + 2;
var alphaBits =
src[srcOffset + alphaOff + 0] +
src[srcOffset + alphaOff + 1] * 256 +
src[srcOffset + alphaOff + 2] * 65536;
var alphaShift = (yy % 2) * 12 + xx * 3;
var alphaCode = (alphaBits >> alphaShift) & 0x7;
if (alpha0 > alpha1) {
switch (alphaCode) {
case 0:
alpha = alpha0;
break;
case 1:
alpha = alpha1;
break;
default:
alpha = ((8 - alphaCode) * alpha0 + (alphaCode - 1) * alpha1) / 7;
break;
}
} else {
switch (alphaCode) {
case 0:
alpha = alpha0;
break;
case 1:
alpha = alpha1;
break;
case 6:
alpha = 0;
break;
case 7:
alpha = 255;
break;
default:
alpha = ((6 - alphaCode) * alpha0 + (alphaCode - 1) * alpha1) / 5;
break;
}
}
}
break;
default:
throw "bad format";
}
destBuffer[dstOff + 0] = sRGBChannelToLinear(srcColor[0]);
destBuffer[dstOff + 1] = sRGBChannelToLinear(srcColor[1]);
destBuffer[dstOff + 2] = sRGBChannelToLinear(srcColor[2]);
destBuffer[dstOff + 3] = alpha;
}
}
}
function getBlockSize(format) {
var isDXT1 = format == ext.COMPRESSED_SRGB_S3TC_DXT1_EXT ||
format == ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
return isDXT1 ? 8 : 16;
}
function uncompressDXTSRGB(width, height, data, format) {
if (width % 4 || height % 4) throw "bad width or height";
var dest = new Uint8Array(width * height * 4);
var blocksAcross = width / 4;
var blocksDown = height / 4;
var blockSize = getBlockSize(format);
for (var yy = 0; yy < blocksDown; ++yy) {
for (var xx = 0; xx < blocksAcross; ++xx) {
uncompressDXTBlockSRGB(
dest, xx * 4, yy * 4, width, data,
(yy * blocksAcross + xx) * blockSize, format);
}
}
return dest;
}
function copyRect(data, srcX, srcY, dstX, dstY, width, height, stride) {
var bytesPerLine = width * 4;
var srcOffset = srcX * 4 + srcY * stride;
var dstOffset = dstX * 4 + dstY * stride;
for (; height > 0; --height) {
for (var ii = 0; ii < bytesPerLine; ++ii) {
data[dstOffset + ii] = data[srcOffset + ii];
}
srcOffset += stride;
dstOffset += stride;
}
}
function testDXTTexture(test, useTexStorage) {
var data = new Uint8Array(test.data);
var width = test.width;
var height = test.height;
var format = test.format;
var uncompressedData = uncompressDXTSRGB(width, height, data, format);
canvas.width = width;
canvas.height = height;
gl.viewport(0, 0, width, height);
debug("testing " + formatToString(format) + " " + width + "x" + height +
(useTexStorage ? " via texStorage2D" : " via compressedTexImage2D"));
var tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
if (useTexStorage) {
gl.texStorage2D(gl.TEXTURE_2D, 1, format, width, height);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "allocating compressed texture via texStorage2D");
wtu.clearAndDrawUnitQuad(gl);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing unit quad");
var clearColor = (test.hasAlpha ? [0, 0, 0, 0] : [0, 0, 0, 255]);
wtu.checkCanvas(gl, clearColor, "texture should be initialized to black");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, format, data);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture data via compressedTexSubImage2D");
} else {
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, data);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture");
}
gl.generateMipmap(gl.TEXTURE_2D);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "trying to generate mipmaps from compressed texture");
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after clearing generateMipmap error");
wtu.clearAndDrawUnitQuad(gl);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing unit quad 1");
compareRect(width, height, test.channels, width, height, uncompressedData, data, format, "NEAREST");
// Test again with linear filtering.
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
wtu.clearAndDrawUnitQuad(gl);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing unit quad 2");
compareRect(width, height, test.channels, width, height, uncompressedData, data, format, "LINEAR");
if (!useTexStorage) {
// It's not allowed to redefine textures defined via texStorage2D.
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 1, data);
wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "non 0 border");
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width + 4, height, 0, data);
wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height + 4, 0, data);
wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 4, height, 0, data);
wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 4, 0, data);
wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 1, height, 0, data);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions");
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 2, height, 0, data);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions");
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 1, 0, data);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions");
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 2, 0, data);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions");
if (width == 4) {
gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, 1, height, 0, data);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "valid dimensions for level > 0");
gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, 2, height, 0, data);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "valid dimensions for level > 0");
}
if (height == 4) {
gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, width, 1, 0, data);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "valid dimensions for level > 0");
gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, width, 2, 0, data);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "valid dimensions for level > 0");
}
}
// pick a wrong format that uses the same amount of data.
var wrongFormat;
switch (format) {
case ext.COMPRESSED_SRGB_S3TC_DXT1_EXT:
wrongFormat = ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;
break;
case ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
wrongFormat = ext.COMPRESSED_SRGB_S3TC_DXT1_EXT;
break;
case ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
wrongFormat = ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;
break;
case ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
wrongFormat = ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;
break;
}
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, wrongFormat, data);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "format does not match");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 4, 0, width, height, format, data);
wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "dimension out of range");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 4, width, height, format, data);
wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "dimension out of range");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width + 4, height, format, data);
wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height + 4, format, data);
wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width - 4, height, format, data);
wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height - 4, format, data);
wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width - 1, height, format, data);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width - 2, height, format, data);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height - 1, format, data);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height - 2, format, data);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions");
var subData = new Uint8Array(data.buffer, 0, getBlockSize(format));
if (width == 8 && height == 8) {
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 1, 0, 4, 4, format, subData);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid offset");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 1, 4, 4, format, subData);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid offset");
}
var stride = width * 4;
for (var yoff = 0; yoff < height; yoff += 4) {
for (var xoff = 0; xoff < width; xoff += 4) {
copyRect(uncompressedData, 0, 0, xoff, yoff, 4, 4, stride);
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, xoff, yoff, 4, 4, format, subData);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture");
// First test NEAREST filtering.
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
wtu.clearAndDrawUnitQuad(gl);
compareRect(width, height, test.channels, width, height, uncompressedData, data, format, "NEAREST");
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing unit quad");
// Next test LINEAR filtering.
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
wtu.clearAndDrawUnitQuad(gl);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing unit quad");
compareRect(width, height, test.channels, width, height, uncompressedData, data, format, "LINEAR");
}
}
}
function insertImg(element, caption, img) {
var div = document.createElement("div");
div.appendChild(img);
var label = document.createElement("div");
label.appendChild(document.createTextNode(caption));
div.appendChild(label);
element.appendChild(div);
}
function makeImage(imageWidth, imageHeight, dataWidth, data, alpha) {
var scale = 8;
var c = document.createElement("canvas");
c.width = imageWidth * scale;
c.height = imageHeight * scale;
var ctx = c.getContext("2d");
for (var yy = 0; yy < imageHeight; ++yy) {
for (var xx = 0; xx < imageWidth; ++xx) {
var offset = (yy * dataWidth + xx) * 4;
ctx.fillStyle = "rgba(" +
data[offset + 0] + "," +
data[offset + 1] + "," +
data[offset + 2] + "," +
(alpha ? data[offset + 3] / 255 : 1) + ")";
ctx.fillRect(xx * scale, yy * scale, scale, scale);
}
}
return wtu.makeImageFromCanvas(c);
}
// See EXT_texture_sRGB, Section 3.8.x, sRGB Texture Color Conversion.
function sRGBChannelToLinear(value) {
value = value / 255;
if (value <= 0.04045) {
value = value / 12.92;
} else {
value = Math.pow((value + 0.055) / 1.055, 2.4);
}
return Math.trunc(value * 255 + 0.5);
}
function compareRect(
actualWidth, actualHeight, actualChannels,
dataWidth, dataHeight, expectedData,
testData, testFormat, filteringMode) {
var actual = new Uint8Array(actualWidth * actualHeight * 4);
gl.readPixels(
0, 0, actualWidth, actualHeight, gl.RGBA, gl.UNSIGNED_BYTE, actual);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "reading back pixels");
var div = document.createElement("div");
div.className = "testimages";
insertImg(div, "expected", makeImage(
actualWidth, actualHeight, dataWidth, expectedData,
actualChannels == 4));
insertImg(div, "actual", makeImage(
actualWidth, actualHeight, actualWidth, actual,
actualChannels == 4));
div.appendChild(document.createElement('br'));
document.getElementById("console").appendChild(div);
// This threshold is required because the values we get back from the
// implementation don't exactly match our javascript implementation.
// This is probably due to allowances in the way sRGB interacts with S3TC.
var threshold = 2;
var failed = false;
for (var yy = 0; yy < actualHeight; ++yy) {
for (var xx = 0; xx < actualWidth; ++xx) {
var actualOffset = (yy * actualWidth + xx) * 4;
var expectedOffset = (yy * dataWidth + xx) * 4;
var expected = [
expectedData[expectedOffset + 0],
expectedData[expectedOffset + 1],
expectedData[expectedOffset + 2],
(actualChannels == 3 ? 255 : expectedData[expectedOffset + 3])
];
for (var jj = 0; jj < 4; ++jj) {
if (Math.abs(actual[actualOffset + jj] - expected[jj]) > threshold) {
failed = true;
var was = actual.slice(actualOffset, actualOffset + 4);
testFailed('at (' + xx + ', ' + yy + ') expected: '
+ expected + ' ± ' + threshold + '; was ' + was);
}
}
}
}
if (!failed) {
testPassed("texture rendered correctly with " + filteringMode + " filtering");
}
}
debug("");
var successfullyParsed = true;
</script>
<script src="../../js/js-test-post.js"></script>
</body>
</html>