<!--
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">
<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>
<script id="vshader" type="x-shader/x-vertex">
attribute vec3 pos;
attribute vec4 colorIn;
varying vec4 color;

void main()
{
    color = colorIn;
    gl_Position = vec4(pos.xyz, 1.0);
}
</script>

<script id="fshader" type="x-shader/x-fragment">
precision mediump float;
varying vec4 color;

void main()
{
    gl_FragColor = color;
}
</script>
</head>
<body>
<canvas id="example" width="32" height="32"></canvas>
<canvas id="example2" width="32" height="32"></canvas>
<div id="description"></div>
<div id="console"></div>
<script>
"use strict";

// The below declarations need to be global for "shouldBe" to see them
var wtu = WebGLTestUtils;
var gl = null;
var array = null;
var pixel = [ 0, 0, 0, 0 ];
var expectedColor = [ 0, 0, 0, 0 ];

function calculatePixelBytes(format, type)
{
    var size = 0;
    switch (format) {
    case gl.ALPHA:
        size = 1;
        break;
    case gl.RGB:
        size = 3;
        break;
    case gl.RGBA:
        size = 4;
        break;
    default:
        return -1;
    }
    switch (type) {
    case gl.UNSIGNED_BYTE:
        break;
    case gl.UNSIGNED_SHORT_5_6_5:
        if (format != gl.RGB)
            return -1;
        size = 2;
        break;
    case gl.UNSIGNED_SHORT_4_4_4_4:
    case gl.UNSIGNED_SHORT_5_5_5_1:
        if (format != gl.RGBA)
            return -1;
        size = 2;
        break;
    default:
        return -1;
    }
    return size;
}

function calculatePaddingBytes(bytesPerPixel, packAlignment, width)
{
    var padding = 0;
    switch (packAlignment) {
    case 1:
    case 2:
    case 4:
    case 8:
        padding = (bytesPerPixel * width) % packAlignment;
        if (padding > 0)
            padding = packAlignment - padding;
        break;
    default:
        return -1;
    }
    return padding;
}

function packColor(format, type, r, g, b, a)
{
    // FIXME: not sure if the color packing is correct for UNSIGNED_SHORT_*.
    var color = [ 0, 0, 0, 0 ];
    switch (type) {
    case gl.UNSIGNED_BYTE:
        switch (format) {
        case gl.ALPHA:
            color[0] = a;
            break;
        case gl.RGB:
            color[0] = r;
            color[1] = g;
            color[2] = b;
            break;
        case gl.RGBA:
            color[0] = r;
            color[1] = g;
            color[2] = b;
            color[3] = a;
            break;
        default:
            return null;
        }
        break;
    case gl.UNSIGNED_SHORT_5_6_5:
        if (format != gl.RGB)
            return null;
        r >>= 3;
        g >>= 2;
        b >>= 3;
        color[0] = (r << 11) + (g << 5) + b;
        break;
    case gl.UNSIGNED_SHORT_4_4_4_4:
        if (format != gl.RGBA)
            return null;
        r >>= 4;
        g >>= 4;
        b >>= 4;
        a >>= 4;
        color[0] = (r << 12) + (g << 8) + (b << 4) + a;
        break;
    case gl.UNSIGNED_SHORT_5_5_5_1:
        if (format != gl.RGBA)
            return null;
        r >>= 3;
        g >>= 3;
        b >>= 3;
        a >>= 7;
        color[0] = (r << 11) + (g << 6) + (b << 1) + a;
        break;
    Default:
        return null;
    }
    return color;
}

function runTestIteration(format, type, packAlignment, width, height)
{
    debug("Testing PACK_ALIGNMENT = " + packAlignment + ", width = " + width + ", height = " + height);
    gl.clearColor(1, 0.4, 0, 1);
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.pixelStorei(gl.PACK_ALIGNMENT, packAlignment);
    wtu.glErrorShouldBe(gl, gl.NO_ERROR);
    var bytesPerPixel = calculatePixelBytes(format, type);
    var padding = calculatePaddingBytes(bytesPerPixel, packAlignment, width);
    var size = bytesPerPixel * width * height + padding * (height - 1);
    if (type != gl.UNSIGNED_BYTE) {
        throw "test error: only UNSIGNED_BYTE is valid to ReadPixels";
    }
    if (size < 0)
        size = 0;
    array = new Uint8Array(size);
    gl.readPixels(0, 0, width, height, format, type, array);
    if (width < 0 || height < 0) {
        wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
        return;
    }

    wtu.glErrorShouldBe(gl, gl.NO_ERROR);
    if (!array.length)
        return;

    // Check the last pixel of the last row.
    var bytesPerRow = width * bytesPerPixel + padding;
    var pos = bytesPerRow * (height - 1) + (width - 1) * bytesPerPixel;
    var numComponents = bytesPerPixel;
    for (var i = 0; i < numComponents; ++i)
        pixel[i] = array[pos + i];
    for (var i = numComponents; i < 4; ++i)
        pixel[i] = 0;
    expectedColor = packColor(format, type, 255, 102, 0, 255);
    shouldBeNonNull("expectedColor");
    shouldBe("pixel", "expectedColor");
}

description('Verify readPixels() works fine with various PACK_ALIGNMENT values.');


debug("<h1>antialias = false</h1>");
shouldBeNonNull("gl = wtu.create3DContext('example', {antialias: false})")
var formats = [ gl.RGBA ];
var formatNames = [ "RGBA" ];
runAllIterations();
debug("<h1>antialias = true</h1>");
shouldBeNonNull("gl = wtu.create3DContext('example2', {antialias: true})")
runAllIterations();

function runAllIterations() {
    shouldBeNonNull("program = wtu.setupProgram(gl, ['vshader', 'fshader'], ['pos', 'colorIn'])");

    for (var i = 0; i < formats.length; ++i) {
        var format = formats[i];

        debug("Testing format = " + formatNames[i] + " and type = UNSIGNED_BYTE");
        runTestIteration(format, gl.UNSIGNED_BYTE, 1, 1, 2);
        runTestIteration(format, gl.UNSIGNED_BYTE, 2, 1, 2);
        runTestIteration(format, gl.UNSIGNED_BYTE, 4, 1, 2);
        runTestIteration(format, gl.UNSIGNED_BYTE, 8, 1, 2);
        runTestIteration(format, gl.UNSIGNED_BYTE, 4, 2, 2);
        runTestIteration(format, gl.UNSIGNED_BYTE, 8, 2, 2);
        runTestIteration(format, gl.UNSIGNED_BYTE, 4, 3, 2);
        runTestIteration(format, gl.UNSIGNED_BYTE, 8, 3, 2);
        runTestIteration(format, gl.UNSIGNED_BYTE, 4, 4, 2);
        runTestIteration(format, gl.UNSIGNED_BYTE, 8, 4, 2);
        runTestIteration(format, gl.UNSIGNED_BYTE, 8, 5, 1);
        runTestIteration(format, gl.UNSIGNED_BYTE, 4, 5, 2);
        runTestIteration(format, gl.UNSIGNED_BYTE, 8, 5, 2);
        runTestIteration(format, gl.UNSIGNED_BYTE, 8, 6, 2);
        runTestIteration(format, gl.UNSIGNED_BYTE, 8, 7, 2);
        runTestIteration(format, gl.UNSIGNED_BYTE, 8, 8, 2);
        runTestIteration(format, gl.UNSIGNED_BYTE, 1, 0, 0);
        runTestIteration(format, gl.UNSIGNED_BYTE, 2, 0, 0);
        runTestIteration(format, gl.UNSIGNED_BYTE, 4, 0, 0);
        runTestIteration(format, gl.UNSIGNED_BYTE, 8, 0, 0);
        runTestIteration(format, gl.UNSIGNED_BYTE, 1, -1, 1);
        runTestIteration(format, gl.UNSIGNED_BYTE, 2, 1, -1);
        runTestIteration(format, gl.UNSIGNED_BYTE, 4, 0, -1);
        runTestIteration(format, gl.UNSIGNED_BYTE, 8, -1, -1);
    }
}
var successfullyParsed = true;

</script>
<script src="../../js/js-test-post.js"></script>
</body>
</html>
