tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

tcuImageCompare.js (35650B)


      1 /*-------------------------------------------------------------------------
      2 * drawElements Quality Program OpenGL ES Utilities
      3 * ------------------------------------------------
      4 *
      5 * Copyright 2014 The Android Open Source Project
      6 *
      7 * Licensed under the Apache License, Version 2.0 (the "License");
      8 * you may not use this file except in compliance with the License.
      9 * You may obtain a copy of the License at
     10 *
     11 *      http://www.apache.org/licenses/LICENSE-2.0
     12 *
     13 * Unless required by applicable law or agreed to in writing, software
     14 * distributed under the License is distributed on an "AS IS" BASIS,
     15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16 * See the License for the specific language governing permissions and
     17 * limitations under the License.
     18 *
     19 */
     20 
     21 'use strict';
     22 goog.provide('framework.common.tcuImageCompare');
     23 goog.require('framework.common.tcuBilinearImageCompare');
     24 goog.require('framework.common.tcuFloat');
     25 goog.require('framework.common.tcuFuzzyImageCompare');
     26 goog.require('framework.common.tcuLogImage');
     27 goog.require('framework.common.tcuRGBA');
     28 goog.require('framework.common.tcuSurface');
     29 goog.require('framework.common.tcuTexture');
     30 goog.require('framework.common.tcuTextureUtil');
     31 goog.require('framework.delibs.debase.deMath');
     32 
     33 goog.scope(function() {
     34 
     35 var tcuImageCompare = framework.common.tcuImageCompare;
     36 var tcuSurface = framework.common.tcuSurface;
     37 var deMath = framework.delibs.debase.deMath;
     38 var tcuTexture = framework.common.tcuTexture;
     39 var tcuTextureUtil = framework.common.tcuTextureUtil;
     40 var tcuFloat = framework.common.tcuFloat;
     41 var tcuFuzzyImageCompare = framework.common.tcuFuzzyImageCompare;
     42 var tcuBilinearImageCompare = framework.common.tcuBilinearImageCompare;
     43 var tcuRGBA = framework.common.tcuRGBA;
     44 var tcuLogImage = framework.common.tcuLogImage;
     45 
     46 /**
     47 * @enum
     48 */
     49 tcuImageCompare.CompareLogMode = {
     50    EVERYTHING: 0,
     51    RESULT: 1,
     52    ON_ERROR: 2
     53 };
     54 
     55 /**
     56 * @param {framework.common.tcuTexture.ConstPixelBufferAccess} result
     57 * @param {framework.common.tcuTexture.ConstPixelBufferAccess} reference
     58 * @param {framework.common.tcuTexture.ConstPixelBufferAccess=} diff
     59 */
     60 tcuImageCompare.displayImages = function(result, reference, diff) {
     61        var limits = tcuImageCompare.computeScaleAndBias(reference, result);
     62        tcuLogImage.logImage('Result', '', result, limits.scale, limits.bias);
     63        tcuLogImage.logImage('Reference', '', reference, limits.scale, limits.bias);
     64        if (diff)
     65            tcuLogImage.logImage('Error', 'error mask', diff);
     66 };
     67 
     68 /**
     69 * @param {tcuTexture.ConstPixelBufferAccess} reference
     70 * @param {tcuTexture.ConstPixelBufferAccess} result
     71 * @return {{scale: Array<number>, bias: Array<number>}}
     72 */
     73 tcuImageCompare.computeScaleAndBias = function(reference, result) {
     74    var minVal = [];
     75    var maxVal = [];
     76    var scale = [];
     77    var bias = [];
     78 
     79    var eps = 0.0001;
     80    var referenceRange = tcuTextureUtil.estimatePixelValueRange(reference);
     81    var resultRange = tcuTextureUtil.estimatePixelValueRange(result);
     82 
     83    minVal[0] = Math.min(referenceRange[0][0], resultRange[0][0]);
     84    minVal[1] = Math.min(referenceRange[0][1], resultRange[0][1]);
     85    minVal[2] = Math.min(referenceRange[0][2], resultRange[0][2]);
     86    minVal[3] = Math.min(referenceRange[0][3], resultRange[0][3]);
     87 
     88    maxVal[0] = Math.max(referenceRange[1][0], resultRange[1][0]);
     89    maxVal[1] = Math.max(referenceRange[1][1], resultRange[1][1]);
     90    maxVal[2] = Math.max(referenceRange[1][2], resultRange[1][2]);
     91    maxVal[3] = Math.max(referenceRange[1][3], resultRange[1][3]);
     92 
     93    for (var c = 0; c < 4; c++) {
     94        if (maxVal[c] - minVal[c] < eps) {
     95            scale[c] = (maxVal[c] < eps) ? 1 : (1 / maxVal[c]);
     96            bias[c] = (c == 3) ? (1 - maxVal[c] * scale[c]) : (0 - minVal[c] * scale[c]);
     97        } else {
     98            scale[c] = 1 / (maxVal[c] - minVal[c]);
     99            bias[c] = 0 - minVal[c] * scale[c];
    100        }
    101    }
    102    return {
    103        scale: scale,
    104        bias: bias
    105    };
    106 };
    107 
    108 /**
    109 * \brief Per-pixel threshold-based comparison
    110 *
    111 * This compare computes per-pixel differences between result and reference
    112 * image. Comparison fails if any pixels exceed the given threshold value.
    113 *
    114 * This comparison can be used for integer- and fixed-point texture formats.
    115 * Difference is computed in integer space.
    116 *
    117 * On failure error image is generated that shows where the failing pixels
    118 * are.
    119 *
    120 * @param {string} imageSetName Name for image set when logging results
    121 * @param {string} imageSetDesc Description for image set
    122 * @param {tcuTexture.ConstPixelBufferAccess} reference Reference image
    123 * @param {tcuTexture.ConstPixelBufferAccess} result Result image
    124 * @param {Array<number>} threshold Maximum allowed difference
    125 * @param {tcuImageCompare.CompareLogMode=} logMode
    126 * @param {Array< Array<number> >} skipPixels pixels that are skipped comparison
    127 * @return {boolean} true if comparison passes, false otherwise
    128 */
    129 tcuImageCompare.intThresholdCompare = function(imageSetName, imageSetDesc, reference, result, threshold, logMode, skipPixels) {
    130    var width = reference.getWidth();
    131    var height = reference.getHeight();
    132    var depth = reference.getDepth();
    133    var errorMask = new tcuSurface.Surface(width, height);
    134 
    135    var maxDiff = [0, 0, 0, 0];
    136    // var pixelBias = [0, 0, 0, 0]; // Vec4 // TODO: check, only used in computeScaleAndBias, which is not included
    137    // var pixelScale = [1, 1, 1, 1]; // Vec4 // TODO: check, only used in computeScaleAndBias
    138 
    139    assertMsgOptions(result.getWidth() == width && result.getHeight() == height && result.getDepth() == depth,
    140        'Reference and result images have different dimensions', false, true);
    141 
    142    for (var z = 0; z < depth; z++) {
    143        for (var y = 0; y < height; y++) {
    144            for (var x = 0; x < width; x++) {
    145                if (skipPixels && skipPixels.length > 0) {
    146                    var skip = false;
    147                    for (var ii = 0; ii < skipPixels.length; ++ii) {
    148                        var refZ = (skipPixels[ii].length > 2 ? skipPixels[ii][2] : 0);
    149                        if (x == skipPixels[ii][0] && y == skipPixels[ii][1] && z == refZ) {
    150                            skip = true;
    151                            break;
    152                        }
    153                    }
    154                    if (skip)
    155                        continue;
    156                }
    157                var refPix = reference.getPixelInt(x, y, z);
    158                var cmpPix = result.getPixelInt(x, y, z);
    159 
    160                var diff = deMath.absDiff(refPix, cmpPix);
    161                var isOk = deMath.boolAll(deMath.lessThanEqual(diff, threshold));
    162 
    163                maxDiff = deMath.max(maxDiff, diff);
    164                var color = [0, 255, 0, 255];
    165                if (!isOk)
    166                    color = [255, 0, 0, 255];
    167                errorMask.setPixel(x, y, color);
    168            }
    169        }
    170    }
    171 
    172    var compareOk = deMath.boolAll(deMath.lessThanEqual(maxDiff, threshold));
    173 
    174    if (!compareOk) {
    175        debug('Image comparison failed: max difference = ' + maxDiff + ', threshold = ' + threshold);
    176        tcuImageCompare.displayImages(result, reference, errorMask.getAccess());
    177    }
    178 
    179    return compareOk;
    180 };
    181 
    182 /**
    183 * \brief Per-pixel threshold-based deviation-ignoring comparison
    184 *
    185 * This compare computes per-pixel differences between result and reference
    186 * image. Pixel fails the test if there is no pixel matching the given
    187 * threshold value in the search volume. Comparison fails if the number of
    188 * failing pixels exceeds the given limit.
    189 *
    190 * If the search volume contains out-of-bounds pixels, comparison can be set
    191 * to either ignore these pixels in search or to accept any pixel that has
    192 * out-of-bounds pixels in its search volume.
    193 *
    194 * This comparison can be used for integer- and fixed-point texture formats.
    195 * Difference is computed in integer space.
    196 *
    197 * On failure error image is generated that shows where the failing pixels
    198 * are.
    199 *
    200 * @param {string} imageSetName Name for image set when logging results
    201 * @param {string} imageSetDesc Description for image set
    202 * @param {tcuTexture.ConstPixelBufferAccess} reference Reference image
    203 * @param {tcuTexture.ConstPixelBufferAccess} result Result image
    204 * @param {Array<number>} threshold Maximum allowed difference
    205 * @param {Array<number>} maxPositionDeviation Maximum allowed distance in the search volume.
    206 * @param {boolean} acceptOutOfBoundsAsAnyValue Accept any pixel in the boundary region
    207 * @param {number} maxAllowedFailingPixels Maximum number of failing pixels
    208 * @return {boolean} true if comparison passes, false otherwise
    209 */
    210 tcuImageCompare.intThresholdPositionDeviationErrorThresholdCompare = function(
    211    imageSetName, imageSetDesc, reference, result, threshold, maxPositionDeviation, acceptOutOfBoundsAsAnyValue, maxAllowedFailingPixels) {
    212    /** @type {number} */ var width = reference.getWidth();
    213    /** @type {number} */ var height = reference.getHeight();
    214    /** @type {number} */ var depth = reference.getDepth();
    215    /** @type {tcuSurface.Surface} */ var errorMask = new tcuSurface.Surface(width, height);
    216    /** @type {number} */ var numFailingPixels = tcuImageCompare.findNumPositionDeviationFailingPixels(errorMask.getAccess(), reference, result, threshold, maxPositionDeviation, acceptOutOfBoundsAsAnyValue);
    217    var compareOk = numFailingPixels <= maxAllowedFailingPixels;
    218    /** @type {Array<number>} */ var pixelBias = [0.0, 0.0, 0.0, 0.0];
    219    /** @type {Array<number>} */ var pixelScale = [1.0, 1.0, 1.0, 1.0];
    220 
    221    if (!compareOk) {
    222        debug('Position deviation error threshold image comparison failed: failed pixels = ' + numFailingPixels + ', threshold = ' + threshold);
    223        tcuImageCompare.displayImages(result, reference, errorMask.getAccess());
    224    } else
    225        tcuLogImage.logImage('Result', '', result);
    226 
    227    /*if (!compareOk) {
    228        // All formats except normalized unsigned fixed point ones need remapping in order to fit into unorm channels in logged images.
    229        if (tcuTexture.getTextureChannelClass(reference.getFormat().type) != tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT ||
    230        tcuTexture.getTextureChannelClass(result.getFormat().type) != tcuTexture.TextureChannelClass.UNSIGNED_FIXED_POINT) {
    231            computeScaleAndBias(reference, result, pixelScale, pixelBias);
    232            log << TestLog::Message << "Result and reference images are normalized with formula p * " << pixelScale << " + " << pixelBias << TestLog::EndMessage;
    233        }
    234 
    235        if (!compareOk)
    236            log << TestLog::Message
    237                << "Image comparison failed:\n"
    238                << "\tallowed position deviation = " << maxPositionDeviation << "\n"
    239                << "\tcolor threshold = " << threshold
    240                << TestLog::EndMessage;
    241        log << TestLog::Message << "Number of failing pixels = " << numFailingPixels << ", max allowed = " << maxAllowedFailingPixels << TestLog::EndMessage;
    242 
    243        log << TestLog::ImageSet(imageSetName, imageSetDesc)
    244            << TestLog::Image("Result", "Result", result, pixelScale, pixelBias)
    245            << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias)
    246            << TestLog::Image("ErrorMask", "Error mask", errorMask)
    247            << TestLog::EndImageSet;
    248    } else if (logMode == COMPARE_LOG_RESULT) {
    249        if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
    250            computePixelScaleBias(result, pixelScale, pixelBias);
    251 
    252        log << TestLog::ImageSet(imageSetName, imageSetDesc)
    253            << TestLog::Image("Result", "Result", result, pixelScale, pixelBias)
    254            << TestLog::EndImageSet;
    255    }*/
    256 
    257    return compareOk;
    258 };
    259 
    260 /**
    261 * tcuImageCompare.floatUlpThresholdCompare
    262 * @param {string} imageSetName
    263 * @param {string} imageSetDesc
    264 * @param {tcuTexture.ConstPixelBufferAccess} reference
    265 * @param {tcuTexture.ConstPixelBufferAccess} result
    266 * @param {Array<number>} threshold - previously used as an Uint32Array
    267 * @return {boolean}
    268 */
    269 tcuImageCompare.floatUlpThresholdCompare = function(imageSetName, imageSetDesc, reference, result, threshold) {
    270    /** @type {number} */ var width = reference.getWidth();
    271    /** @type {number} */ var height = reference.getHeight();
    272    /** @type {number} */ var depth = reference.getDepth();
    273    /** @type {tcuSurface.Surface} */ var errorMask = new tcuSurface.Surface(width, height);
    274 
    275    /** @type {Array<number>} */ var maxDiff = [0, 0, 0, 0]; // UVec4
    276    // var pixelBias = [0, 0, 0, 0]; // Vec4
    277    // var pixelScale = [1, 1, 1, 1]; // Vec4
    278 
    279    assertMsgOptions(result.getWidth() == width && result.getHeight() == height && result.getDepth() == depth,
    280            'Reference and result images have different dimensions', false, true);
    281 
    282    for (var z = 0; z < depth; z++) {
    283        for (var y = 0; y < height; y++) {
    284            for (var x = 0; x < width; x++) {
    285                /** @type {ArrayBuffer} */ var arrayBufferRef = new ArrayBuffer(4 * 4);
    286                /** @type {ArrayBuffer} */ var arrayBufferCmp = new ArrayBuffer(4 * 4);
    287 
    288                /** @type {Array<number>} */ var refPix = reference.getPixel(x, y, z); // getPixel returns a Vec4 pixel color
    289 
    290                /** @type {Array<number>} */ var cmpPix = result.getPixel(x, y, z); // getPixel returns a Vec4 pixel color
    291 
    292                /** @type {Uint32Array} */ var refBits = new Uint32Array(arrayBufferRef); // UVec4
    293                /** @type {Uint32Array} */ var cmpBits = new Uint32Array(arrayBufferCmp); // UVec4
    294 
    295                // Instead of memcpy(), which is the way to do float->uint32 reinterpretation in C++
    296                for (var i = 0; i < refPix.length; i++) {
    297                    refBits[i] = tcuFloat.convertFloat32Inline(refPix[i], tcuFloat.description32);
    298                    cmpBits[i] = tcuFloat.convertFloat32Inline(cmpPix[i], tcuFloat.description32);
    299                }
    300 
    301                /** @type {Array<number>} */ var diff = deMath.absDiff(refBits, cmpBits); // UVec4
    302                /** @type {boolean} */ var isOk = deMath.boolAll(deMath.lessThanEqual(diff, threshold));
    303 
    304                maxDiff = deMath.max(maxDiff, diff);
    305 
    306                errorMask.setPixel(x, y, isOk ? [0, 255, 0, 255] : [255, 0, 0, 255]);
    307            }
    308        }
    309    }
    310 
    311    /** @type {boolean} */ var compareOk = deMath.boolAll(deMath.lessThanEqual(maxDiff, threshold));
    312 
    313    if (!compareOk) {
    314        debug('Image comparison failed: max difference = ' + maxDiff + ', threshold = ' + threshold);
    315        tcuImageCompare.displayImages(result, reference, errorMask.getAccess());
    316    }
    317 
    318    /*if (!compareOk || logMode == COMPARE_LOG_EVERYTHING) {
    319        // All formats except normalized unsigned fixed point ones need remapping in order to fit into unorm channels in logged images.
    320        if (tcu::getTextureChannelClass(reference.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT ||
    321            tcu::getTextureChannelClass(result.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT) {
    322            computeScaleAndBias(reference, result, pixelScale, pixelBias);
    323            log << TestLog::Message << "Result and reference images are normalized with formula p * " << pixelScale << " + " << pixelBias << TestLog::EndMessage;
    324        }
    325 
    326        if (!compareOk)
    327            log << TestLog::Message << "Image comparison failed: max difference = " << maxDiff << ", threshold = " << threshold << TestLog::EndMessage;
    328 
    329        log << TestLog::ImageSet(imageSetName, imageSetDesc)
    330            << TestLog::Image("Result", "Result", result, pixelScale, pixelBias)
    331            << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias)
    332            << TestLog::Image("ErrorMask", "Error mask", errorMask)
    333            << TestLog::EndImageSet;
    334    } else if (logMode == COMPARE_LOG_RESULT) {
    335        if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
    336            computePixelScaleBias(result, pixelScale, pixelBias);
    337 
    338        log << TestLog::ImageSet(imageSetName, imageSetDesc)
    339            << TestLog::Image("Result", "Result", result, pixelScale, pixelBias)
    340            << TestLog::EndImageSet;
    341    }*/
    342 
    343    return compareOk;
    344 };
    345 
    346 /**
    347 * tcuImageCompare.floatThresholdCompare
    348 * @param {string} imageSetName
    349 * @param {string} imageSetDesc
    350 * @param {tcuTexture.ConstPixelBufferAccess} reference
    351 * @param {tcuTexture.ConstPixelBufferAccess} result
    352 * @param {Array<number>} threshold
    353 * @return {boolean}
    354 */
    355 tcuImageCompare.floatThresholdCompare = function(imageSetName, imageSetDesc, reference, result, threshold) {
    356    /** @type {number} */ var width = reference.getWidth();
    357    /** @type {number} */ var height = reference.getHeight();
    358    /** @type {number} */ var depth = reference.getDepth();
    359    /** @type {tcuSurface.Surface} */ var errorMask = new tcuSurface.Surface(width, height);
    360 
    361    /** @type {Array<number>} */ var maxDiff = [0, 0, 0, 0]; // Vec4
    362    // var pixelBias = [0, 0, 0, 0]; // Vec4
    363    // var pixelScale = [1, 1, 1, 1]; // Vec4
    364 
    365    assertMsgOptions(result.getWidth() == width && result.getHeight() == height && result.getDepth() == depth,
    366            'Reference and result images have different dimensions', false, true);
    367 
    368    for (var z = 0; z < depth; z++) {
    369        for (var y = 0; y < height; y++) {
    370            for (var x = 0; x < width; x++) {
    371                var refPix = reference.getPixel(x, y, z); // Vec4
    372                var cmpPix = result.getPixel(x, y, z); // Vec4
    373 
    374                /** @type {Array<number>} */ var diff = deMath.absDiff(refPix, cmpPix); // Vec4
    375                /** @type {boolean} */ var isOk = deMath.boolAll(deMath.lessThanEqual(diff, threshold));
    376 
    377                maxDiff = deMath.max(maxDiff, diff);
    378 
    379                errorMask.setPixel(x, y, isOk ? [0, 255, 0, 255] : [255, 0, 0, 255]);
    380            }
    381        }
    382    }
    383 
    384    /** @type {boolean} */ var compareOk = deMath.boolAll(deMath.lessThanEqual(maxDiff, threshold));
    385 
    386    if (!compareOk) {
    387        debug('Image comparison failed: max difference = ' + maxDiff + ', threshold = ' + threshold);
    388        tcuImageCompare.displayImages(result, reference, errorMask.getAccess());
    389    }
    390 
    391    /*if (!compareOk || logMode == COMPARE_LOG_EVERYTHING) {
    392        // All formats except normalized unsigned fixed point ones need remapping in order to fit into unorm channels in logged images.
    393        if (tcu::getTextureChannelClass(reference.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT ||
    394            tcu::getTextureChannelClass(result.getFormat().type) != tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT) {
    395            computeScaleAndBias(reference, result, pixelScale, pixelBias);
    396            log << TestLog::Message << "Result and reference images are normalized with formula p * " << pixelScale << " + " << pixelBias << TestLog::EndMessage;
    397        }
    398 
    399        if (!compareOk)
    400            log << TestLog::Message << "Image comparison failed: max difference = " << maxDiff << ", threshold = " << threshold << TestLog::EndMessage;
    401 
    402        log << TestLog::ImageSet(imageSetName, imageSetDesc)
    403            << TestLog::Image("Result", "Result", result, pixelScale, pixelBias)
    404            << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias)
    405            << TestLog::Image("ErrorMask", "Error mask", errorMask)
    406            << TestLog::EndImageSet;
    407    } else if (logMode == COMPARE_LOG_RESULT) {
    408        if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
    409            computePixelScaleBias(result, pixelScale, pixelBias);
    410 
    411        log << TestLog::ImageSet(imageSetName, imageSetDesc)
    412            << TestLog::Image("Result", "Result", result, pixelScale, pixelBias)
    413            << TestLog::EndImageSet;
    414    }*/
    415 
    416    return compareOk;
    417 };
    418 
    419 /**
    420 * \brief Per-pixel threshold-based comparison
    421 *
    422 * This compare computes per-pixel differences between result and reference
    423 * image. Comparison fails if any pixels exceed the given threshold value.
    424 *
    425 * On failure error image is generated that shows where the failing pixels
    426 * are.
    427 *
    428 * @param {string} imageSetName Name for image set when logging results
    429 * @param {string} imageSetDesc Description for image set
    430 * @param {tcuSurface.Surface} reference Reference image
    431 * @param {tcuSurface.Surface} result Result image
    432 * @param {Array<number>} threshold Maximum allowed difference
    433 * @param {tcuImageCompare.CompareLogMode=} logMode
    434 * @param {Array< Array<number> >} skipPixels pixels that are skipped comparison
    435 * @return {boolean} true if comparison passes, false otherwise
    436 */
    437 tcuImageCompare.pixelThresholdCompare = function(imageSetName, imageSetDesc, reference, result, threshold, logMode, skipPixels) {
    438    return tcuImageCompare.intThresholdCompare(imageSetName, imageSetDesc, reference.getAccess(), result.getAccess(), threshold, logMode, skipPixels);
    439 };
    440 
    441 /**
    442 * @param {tcuTexture.PixelBufferAccess} errorMask
    443 * @param {tcuTexture.ConstPixelBufferAccess} reference
    444 * @param {tcuTexture.ConstPixelBufferAccess} result
    445 * @param {Array<number>} threshold
    446 * @param {Array<number>} maxPositionDeviation
    447 * @param {boolean} acceptOutOfBoundsAsAnyValue
    448 * @return {number}
    449 */
    450 tcuImageCompare.findNumPositionDeviationFailingPixels = function(errorMask, reference, result, threshold, maxPositionDeviation, acceptOutOfBoundsAsAnyValue) {
    451    /** @type {number} */ var width = reference.getWidth();
    452    /** @type {number} */ var height = reference.getHeight();
    453    /** @type {number} */ var depth = reference.getDepth();
    454    /** @type {number} */ var numFailingPixels = 0;
    455 
    456    checkMessage(result.getWidth() == width && result.getHeight() == height && result.getDepth() == depth, 'Surfaces have different dimensions');
    457 
    458    for (var z = 0; z < depth; z++) {
    459        for (var y = 0; y < height; y++) {
    460            for (var x = 0; x < width; x++) {
    461                /** @type {Array<number>} */ var refPix = reference.getPixelInt(x, y, z);
    462                /** @type {Array<number>} */ var cmpPix = result.getPixelInt(x, y, z);
    463 
    464                // Exact match
    465                /** @type {Array<number>} */ var diff = deMath.absDiff(refPix, cmpPix);
    466                /** @type {boolean} */ var isOk = deMath.boolAll(deMath.lessThanEqual(diff, threshold));
    467 
    468                if (isOk) {
    469                    errorMask.setPixel([0, 0xff, 0, 0xff], x, y, z);
    470                    continue;
    471                }
    472 
    473                // Accept over the image bounds pixels since they could be anything
    474 
    475                if (acceptOutOfBoundsAsAnyValue &&
    476                    (x < maxPositionDeviation[0] || x + maxPositionDeviation[0] >= width ||
    477                     y < maxPositionDeviation[1] || y + maxPositionDeviation[1] >= height ||
    478                     z < maxPositionDeviation[2] || z + maxPositionDeviation[2] >= depth)) {
    479                    errorMask.setPixel([0, 0xff, 0, 0xff], x, y, z);
    480                    continue;
    481                }
    482 
    483                // Find matching pixels for both result and reference pixel
    484 
    485                var pixelFoundForReference = false;
    486                var pixelFoundForResult = false;
    487 
    488                // Find deviated result pixel for reference
    489 
    490                for (var sz = Math.max(0, z - maxPositionDeviation[2]); sz <= Math.min(depth - 1, z + maxPositionDeviation[2]) && !pixelFoundForReference; ++sz)
    491                for (var sy = Math.max(0, y - maxPositionDeviation[1]); sy <= Math.min(height - 1, y + maxPositionDeviation[1]) && !pixelFoundForReference; ++sy)
    492                for (var sx = Math.max(0, x - maxPositionDeviation[0]); sx <= Math.min(width - 1, x + maxPositionDeviation[0]) && !pixelFoundForReference; ++sx) {
    493                    /** @type {Array<number>} */ var deviatedCmpPix = result.getPixelInt(sx, sy, sz);
    494                    diff = deMath.absDiff(refPix, deviatedCmpPix);
    495                    isOk = deMath.boolAll(deMath.lessThanEqual(diff, threshold));
    496 
    497                    pixelFoundForReference |= isOk;
    498                }
    499 
    500                // Find deviated reference pixel for result
    501 
    502                for (var sz = Math.max(0, z - maxPositionDeviation[2]); sz <= Math.min(depth - 1, z + maxPositionDeviation[2]) && !pixelFoundForResult; ++sz)
    503                for (var sy = Math.max(0, y - maxPositionDeviation[1]); sy <= Math.min(height - 1, y + maxPositionDeviation[1]) && !pixelFoundForResult; ++sy)
    504                for (var sx = Math.max(0, x - maxPositionDeviation[0]); sx <= Math.min(width - 1, x + maxPositionDeviation[0]) && !pixelFoundForResult; ++sx) {
    505                    /** @type {Array<number>} */ var deviatedRefPix = reference.getPixelInt(sx, sy, sz);
    506                    diff = deMath.absDiff(cmpPix, deviatedRefPix);
    507                    isOk = deMath.boolAll(deMath.lessThanEqual(diff, threshold));
    508 
    509                    pixelFoundForResult |= isOk;
    510                }
    511 
    512                if (pixelFoundForReference && pixelFoundForResult)
    513                    errorMask.setPixel([0, 0xff, 0, 0xff], x, y, z);
    514                else {
    515                    errorMask.setPixel([0xff, 0, 0, 0xff], x, y, z);
    516                    ++numFailingPixels;
    517                }
    518            }
    519        }
    520    }
    521 
    522    return numFailingPixels;
    523 };
    524 
    525 /**
    526  * tcuImageCompare.fuzzyCompare
    527  * @param {string} imageSetName
    528  * @param {string} imageSetDesc
    529  * @param {tcuTexture.ConstPixelBufferAccess} reference
    530  * @param {tcuTexture.ConstPixelBufferAccess} result
    531  * @param {number} threshold
    532  * @param {tcuImageCompare.CompareLogMode=} logMode
    533  * @return {boolean}
    534  */
    535 tcuImageCompare.fuzzyCompare = function(imageSetName, imageSetDesc, reference, result, threshold, logMode) {
    536    /** @type {tcuFuzzyImageCompare.FuzzyCompareParams} */ var params = new tcuFuzzyImageCompare.FuzzyCompareParams(); // Use defaults.
    537    /** @type {tcuTexture.TextureLevel} */ var errorMask = new tcuTexture.TextureLevel(
    538                                                                new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGB,
    539                                                                              tcuTexture.ChannelType.UNORM_INT8),
    540                                                                reference.getWidth(),
    541                                                                reference.getHeight()
    542                                                           );
    543    /** @type {number} */ var difference = tcuFuzzyImageCompare.fuzzyCompare(
    544                                                                params,
    545                                                                reference,
    546                                                                result,
    547                                                                tcuTexture.PixelBufferAccess.newFromTextureLevel(errorMask)
    548                                                               );
    549    /** @type {boolean} */ var isOk = difference <= threshold;
    550    /** @type {Array<number>} */ var pixelBias = [0.0, 0.0, 0.0, 0.0];
    551    /** @type {Array<number>} */ var pixelScale = [1.0, 1.0, 1.0, 1.0];
    552 
    553    if (!isOk) {
    554        debug('Fuzzy image comparison failed: difference = ' + difference + ', threshold = ' + threshold);
    555        tcuImageCompare.displayImages(result, reference, errorMask.getAccess());
    556    }
    557 
    558    /*
    559    if (!isOk || logMode == COMPARE_LOG_EVERYTHING) {
    560        // Generate more accurate error mask.
    561        params.maxSampleSkip = 0;
    562        tcuImageCompare.fuzzyCompare(params, reference, result, errorMask.getAccess());
    563 
    564        if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8) && reference.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
    565            computeScaleAndBias(reference, result, pixelScale, pixelBias);
    566 
    567        if (!isOk)
    568            log << TestLog::Message << "Image comparison failed: difference = " << difference << ", threshold = " << threshold << TestLog::EndMessage;
    569 
    570        log << TestLog::ImageSet(imageSetName, imageSetDesc)
    571            << TestLog::Image("Result", "Result", result, pixelScale, pixelBias)
    572            << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias)
    573            << TestLog::Image("ErrorMask", "Error mask", errorMask)
    574            << TestLog::EndImageSet;
    575    } else if (logMode == COMPARE_LOG_RESULT) {
    576        if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
    577            computePixelScaleBias(result, pixelScale, pixelBias);
    578 
    579        log << TestLog::ImageSet(imageSetName, imageSetDesc)
    580            << TestLog::Image("Result", "Result", result, pixelScale, pixelBias)
    581            << TestLog::EndImageSet;
    582    }
    583    */
    584    return isOk;
    585 };
    586 
    587 tcuImageCompare.unitTest = function() {
    588    var width = 128;
    589    var height = 128;
    590 
    591    var weirdLevel = new tcuTexture.TextureLevel(new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RG, tcuTexture.ChannelType.SNORM_INT32), width, height);
    592    var access = weirdLevel.getAccess();
    593    access.clear([0.1, 0.5, 0, 0]);
    594    access.clear([0.11, 0.52, 0, 0], [0, width], [0, height / 2]);
    595    access.clear([0.12, 0.52, 0, 0], [0, width], [height / 2, height / 2 + height / 8]);
    596    var limits = tcuTextureUtil.computePixelScaleBias(access);
    597    debug('Scale: ' + limits.scale);
    598    debug('Bias: ' + limits.bias);
    599    tcuLogImage.logImage('Weird', 'weird format without scaling', access);
    600    tcuLogImage.logImage('Weird', 'weird format', access, limits.scale, limits.bias);
    601 
    602    var srcLevel = new tcuTexture.TextureLevel(new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8), width, height);
    603    var dstLevel = new tcuTexture.TextureLevel(new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8), width, height);
    604    var src = srcLevel.getAccess();
    605    var dst = dstLevel.getAccess();
    606 
    607    src.clear();
    608    dst.clear();
    609 
    610    for (var i = 0; i < width - 1; i++) {
    611        for (var j = 0; j < height - 1; j++) {
    612            src.setPixelInt([i, j, 90, 255], i, j);
    613            dst.setPixelInt([i, j, 90, 255], i + 1, j + 1);
    614        }
    615    }
    616 
    617    debug('Src format: ' + src.getFormat());
    618    debug('Destination: ' + dst);
    619    debug(src);
    620    tcuLogImage.logImage('Source', 'Source image', src);
    621 
    622    if (!tcuImageCompare.fuzzyCompare('compare', 'compare similar images', src, dst, 0.05))
    623        throw new Error('Compare should return true');
    624 
    625    src.clear();
    626    dst.clear();
    627 
    628    for (var i = 0; i < width - 2; i++) {
    629        for (var j = 0; j < height - 2; j++) {
    630            src.setPixelInt([i, j, 90, 255], i, j);
    631            dst.setPixelInt([i, j, 90, 255], i + 2, j + 2);
    632        }
    633    }
    634 
    635    if (tcuImageCompare.fuzzyCompare('compare', 'compare different images', src, dst, 0.05))
    636        throw new Error('Compare should return false');
    637 
    638    debug('Passed');
    639 };
    640 
    641 tcuImageCompare.unitTest2 = function() {
    642    var width = 128;
    643    var height = 128;
    644    var srcLevel = new tcuTexture.TextureLevel(new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8), width, height);
    645    var dstLevel = new tcuTexture.TextureLevel(new tcuTexture.TextureFormat(tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8), width, height);
    646    var src = srcLevel.getAccess();
    647    var dst = dstLevel.getAccess();
    648    var threshold = tcuRGBA.newRGBAComponents(1, 1, 1, 1);
    649    debug('Threshold: ' + threshold);
    650 
    651    src.clear();
    652    dst.clear();
    653 
    654    for (var i = 0; i < width - 1; i++) {
    655        for (var j = 0; j < height - 1; j++) {
    656            src.setPixelInt([i, j, 90, 255], i, j);
    657            dst.setPixelInt([i, j, 90, 255], i, j);
    658        }
    659    }
    660    if (!tcuImageCompare.bilinearCompare('compare', 'compare similar images', src, dst, threshold))
    661        throw new Error('Compare should return true');
    662    debug('bilinear compare the same images passed');
    663 
    664    src.clear();
    665    dst.clear();
    666 
    667    for (var i = 0; i < width - 1; i++) {
    668        for (var j = 0; j < height - 1; j++) {
    669            src.setPixelInt([i, j, 90, 255], i, j);
    670            dst.setPixelInt([i, j + 1, 90, 255], i, j + 1);
    671        }
    672    }
    673    if (!tcuImageCompare.bilinearCompare('compare', 'compare similar images', src, dst, threshold))
    674        throw new Error('Compare should return true');
    675    debug('bilinear compare very similar images passed');
    676 
    677    src.clear();
    678    dst.clear();
    679 
    680    for (var i = 0; i < width - 2; i++) {
    681        for (var j = 0; j < height - 2; j++) {
    682            src.setPixelInt([i, j, 90, 255], i, j);
    683            // dst.setPixelInt([i, j, 90, 255], i + 2, j + 2);
    684        }
    685    }
    686 
    687    if (tcuImageCompare.bilinearCompare('compare', 'compare different images', src, dst, threshold))
    688        throw new Error('Compare should return false');
    689 
    690    debug('bilinear compare very different images passed');
    691 };
    692 
    693 /**
    694 * Bilinear image comparison
    695 * On failure error image is generated that shows where the failing pixels
    696 * are.
    697 * Currently supports only RGBA, UNORM_INT8 formats
    698 *
    699 * @param {string} imageSetName Name for image set when logging results
    700 * @param {string} imageSetDesc Description for image set
    701 * @param {tcuTexture.ConstPixelBufferAccess} reference Reference image
    702 * @param {tcuTexture.ConstPixelBufferAccess} result Result image
    703 * @param {tcuRGBA.RGBA} threshold Maximum local difference
    704 * @param {tcuImageCompare.CompareLogMode=} logMode Logging mode
    705 * @return {boolean} if comparison passes, false otherwise
    706 */
    707 tcuImageCompare.bilinearCompare = function(imageSetName, imageSetDesc, reference, result, threshold, logMode) {
    708    /** @type {tcuTexture.TextureLevel} */
    709    var errorMask = new tcuTexture.TextureLevel(
    710        new tcuTexture.TextureFormat(
    711            tcuTexture.ChannelOrder.RGB,
    712            tcuTexture.ChannelType.UNORM_INT8),
    713        reference.getWidth(),
    714        reference.getHeight());
    715 
    716    /** @type {boolean} */
    717    var isOk = tcuBilinearImageCompare.bilinearCompare(
    718        reference,
    719        result,
    720        tcuTexture.PixelBufferAccess.newFromTextureLevel(errorMask),
    721        threshold);
    722 
    723    if (!isOk) {
    724        debug('Image comparison failed: threshold = ' + threshold);
    725        tcuImageCompare.displayImages(result, reference, errorMask.getAccess());
    726    }
    727 
    728    // /* @type {Array<number>} */ var pixelBias = [0.0, 0.0, 0.0, 0.0];
    729    // /* @type {Array<number>} */ var pixelScale = [1.0, 1.0, 1.0, 1.0];
    730    // if (!isOk || logMode == COMPARE_LOG_EVERYTHING)
    731    // {
    732    //     if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8) && reference.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
    733    //         computeScaleAndBias(reference, result, pixelScale, pixelBias);
    734    //
    735    //     if (!isOk)
    736    //         log << TestLog::Message << "Image comparison failed, threshold = " << threshold << TestLog::EndMessage;
    737    //
    738    //     log << TestLog::ImageSet(imageSetName, imageSetDesc)
    739    //         << TestLog::Image("Result", "Result", result, pixelScale, pixelBias)
    740    //         << TestLog::Image("Reference", "Reference", reference, pixelScale, pixelBias)
    741    //         << TestLog::Image("ErrorMask", "Error mask", errorMask)
    742    //         << TestLog::EndImageSet;
    743    // }
    744    // else if (logMode == COMPARE_LOG_RESULT)
    745    // {
    746    //     if (result.getFormat() != TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8))
    747    //         computePixelScaleBias(result, pixelScale, pixelBias);
    748    //
    749    //     log << TestLog::ImageSet(imageSetName, imageSetDesc)
    750    //         << TestLog::Image("Result", "Result", result, pixelScale, pixelBias)
    751    //         << TestLog::EndImageSet;
    752    // }
    753 
    754    return isOk;
    755 };
    756 
    757 });