<!--
Copyright (c) 2019 The Khronos Group Inc.
Use of this source code is governed by an MIT-style license that can be
found in the LICENSE.txt file.
-->

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>WebGL texture mipmap conformance test.</title>
<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>
</head>
<body>
<canvas id="example" width="4" height="4" style="width: 16px; height: 16px;"></canvas>
<div id="description"></div>
<div id="console"></div>
<script id="vshader" type="x-shader/x-vertex">
attribute vec4 vPosition;
attribute vec2 texCoord0;
varying vec2 texCoord;
void main()
{
    gl_Position = vPosition;
    texCoord = texCoord0;
}
</script>

<script id="fshader" type="x-shader/x-fragment">
precision mediump float;
uniform sampler2D tex;
varying vec2 texCoord;
void main()
{
    gl_FragColor = texture2D(tex, texCoord);
}
</script>
<script>
"use strict";
var wtu = WebGLTestUtils;
var canvas = document.getElementById("example");
var gl = wtu.create3DContext(canvas, undefined, 2);

description("Test srgb emulation for generateMipmap.");
function generateMipmap()
{
    debug("Generate mipmaps for sRGB texture");

    wtu.setupUnitQuad(gl, 0, 1);
    var program = wtu.setupProgram(
        gl, ['vshader', 'fshader'], ['vPosition', 'texCoord0'], [0, 1]);

    gl.disable(gl.DEPTH_TEST);
    gl.disable(gl.BLEND);

    var colors = {
        blank: [0, 0, 0, 0],
        srgba: [0, 63, 127, 255],
    };

    var texLoc = gl.getUniformLocation(program, "tex");
    gl.uniform1i(texLoc, 0);

    var width = 128;
    var height = 128;
    canvas.width = width;
    canvas.height = height;
    gl.viewport(0, 0, width, height);

    var srgbTex = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, srgbTex);
    // Set full texture as srgba color first.
    wtu.fillTexture(gl, srgbTex, width, height, colors['srgba'], 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.SRGB8_ALPHA8);
    // Set up-left region of the texture as red color.
    // In order to make sure bi-linear interpolation operates on different colors, red region
    // is 1 pixel smaller than a quarter of the full texture on each side.
    var redWidth = width / 2 - 1;
    var redHeight = height / 2 - 1;
    var buf = new Uint8Array(redWidth * redHeight * 4);
    for (var i = 0; i < redWidth * redHeight; i++) {
        buf[4 * i + 0] = 255;
        buf[4 * i + 1] = 0;
        buf[4 * i + 2] = 0;
        buf[4 * i + 3] = 255;
    }
    gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, redWidth, redHeight, gl.RGBA, gl.UNSIGNED_BYTE, buf);
    gl.generateMipmap(gl.TEXTURE_2D);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_NEAREST);

    // Decode the srgba texture to a linear texture which will be used as reference.
    var linearTex = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, linearTex);
    wtu.fillTexture(gl, linearTex, width, height, wtu.sRGBToLinear(colors['srgba']), 0, gl.RGBA, gl.UNSIGNED_BYTE);
    // Set up-left region of the texture as red color.
    // In order to make sure bi-linear interpolation operates on different colors, red region
    // is 1 pixel smaller than a quarter of the full texture on each side.
    for (var i = 0; i < redWidth * redHeight; i++) {
        buf[4 * i + 0] = 255;
        buf[4 * i + 1] = 0;
        buf[4 * i + 2] = 0;
        buf[4 * i + 3] = 255;
    }
    gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, redWidth, redHeight, gl.RGBA, gl.UNSIGNED_BYTE, buf);
    gl.generateMipmap(gl.TEXTURE_2D);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_NEAREST);

    // Change canvas to a small size.
    width = 64;
    height = 64;
    canvas.width = width;
    canvas.height = height;
    gl.viewport(0, 0, width, height);

    // Draw with srgb texture and linear texture respectively.
    gl.bindTexture(gl.TEXTURE_2D, srgbTex);
    wtu.clearAndDrawUnitQuad(gl);
    var result = new Uint8Array(width * height * 4);
    gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, result);
    gl.bindTexture(gl.TEXTURE_2D, linearTex);
    wtu.clearAndDrawUnitQuad(gl);
    var reference = new Uint8Array(width * height * 4);
    gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, reference);

    gl.deleteTexture(srgbTex);
    gl.deleteTexture(linearTex);

    var tolerance = 7;
    var diff = new Uint8Array(width * height * 4);
    var failed = wtu.comparePixels(result, reference, tolerance, diff);
    if (failed) {
        testFailed("Generate wrong mipmaps for sRGB texture.");
        wtu.displayImageDiff(result, reference, diff, width, height);
    } else {
        testPassed("Generate correct mipmaps for sRGB texture.");
    }
}

function generateMipmap_immutableTexture()
{
    debug("Generate mipmaps for immutable texture.");
    var tex = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, tex);
    gl.texStorage2D(gl.TEXTURE_2D, Math.log2(canvas.width), gl.SRGB8_ALPHA8, canvas.width, canvas.height);
    gl.generateMipmap(gl.TEXTURE_2D);
    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "GenerateMipmap should succeed.");

    gl.deleteTexture(tex);
}

function generateMipmap_widthHeightNotEqual()
{
    debug("Generate mipmaps when width and height are not equal.");
    var tex = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, tex);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.SRGB8_ALPHA8, 64, 32, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
    gl.generateMipmap(gl.TEXTURE_2D);
    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "GenerateMipmap should succeed.");

    gl.deleteTexture(tex);
}

function generateMipmap_maxLevelLessThanFullMipmapLevel()
{
    debug("Generate mipmaps when maxLevel is less than full mipmap level.");

    wtu.setupUnitQuad(gl, 0, 1);
    var program = wtu.setupProgram(
        gl, ['vshader', 'fshader'], ['vPosition', 'texCoord0'], [0, 1]);

    var colors = [0, 63, 127, 255];

    var texLoc = gl.getUniformLocation(program, "tex");
    gl.uniform1i(texLoc, 0);

    var width = 16;
    var height = 16;
    canvas.width = width;
    canvas.height = height;
    gl.viewport(0, 0, width, height);

    var srgbTex = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, srgbTex);
    wtu.fillTexture(gl, srgbTex, width, height, colors, 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.SRGB8_ALPHA8);

    // Set max level, check if the max level mipmap is generated.
    var max_level = 3;
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAX_LEVEL, max_level);
    gl.generateMipmap(gl.TEXTURE_2D);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_NEAREST);

    width >>= max_level;
    height >>= max_level;
    canvas.width = width;
    canvas.height = height;
    gl.viewport(0, 0, width, height);

    gl.bindTexture(gl.TEXTURE_2D, srgbTex);
    wtu.clearAndDrawUnitQuad(gl);

    var reference = wtu.sRGBToLinear(colors);
    var msg;
    wtu.checkCanvasRect(gl, 0, 0, width, height, reference, msg, [1,1,1,1]);

    gl.deleteTexture(srgbTex);
}

generateMipmap();
generateMipmap_immutableTexture();
generateMipmap_widthHeightNotEqual();
generateMipmap_maxLevelLessThanFullMipmapLevel();

var successfullyParsed = true;
</script>
<script src="../../../js/js-test-post.js"></script>

</body>
</html>

