| /*------------------------------------------------------------------------- |
| * drawElements Quality Program OpenGL ES Utilities |
| * ------------------------------------------------ |
| * |
| * Copyright 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| 'use strict'; |
| goog.provide('framework.common.tcuBilinearImageCompare'); |
| goog.require('framework.common.tcuRGBA'); |
| goog.require('framework.common.tcuTexture'); |
| goog.require('framework.delibs.debase.deMath'); |
| |
| goog.scope(function() { |
| |
| var tcuBilinearImageCompare = framework.common.tcuBilinearImageCompare; |
| var deMath = framework.delibs.debase.deMath; |
| var tcuTexture = framework.common.tcuTexture; |
| var tcuRGBA = framework.common.tcuRGBA; |
| |
| var DE_ASSERT = function(x) { |
| if (!x) |
| throw new Error('Assert failed'); |
| }; |
| |
| // for bilinear interpolation |
| /** @const {number} */ tcuBilinearImageCompare.NUM_SUBPIXEL_BITS = 8; |
| |
| // Algorithm assumes that colors are packed to 32-bit values as dictated by |
| // tcu::RGBA::*_SHIFT values. |
| |
| function UintRGBA8_R(color) { |
| return (color >> 24) & 0xff; |
| } |
| function UintRGBA8_G(color) { |
| return (color >> 16) & 0xff; |
| } |
| function UintRGBA8_B(color) { |
| return (color >> 8) & 0xff; |
| } |
| function UintRGBA8_A(color) { |
| return color & 0xff; |
| } |
| |
| /** |
| * @param {number} fx1 deUint32 |
| * @param {number} fy1 deUint32 |
| * @param {number} p00 deUint8 |
| * @param {number} p01 deUint8 |
| * @param {number} p10 deUint8 |
| * @param {number} p11 deUint8 |
| * @return {number} deUint8 |
| */ |
| tcuBilinearImageCompare.interpolateChannel = function(fx1, fy1, p00, p01, p10, p11) { |
| /** @const {number} */ var fx0 = (1 << tcuBilinearImageCompare.NUM_SUBPIXEL_BITS) - fx1; |
| /** @const {number} */ var fy0 = (1 << tcuBilinearImageCompare.NUM_SUBPIXEL_BITS) - fy1; |
| /** @const {number} */ |
| var half = 1 << (tcuBilinearImageCompare.NUM_SUBPIXEL_BITS * 2 - 1); |
| /** @const {number} */ var sum = |
| (fx0 * fy0 * p00) + |
| (fx1 * fy0 * p10) + |
| (fx0 * fy1 * p01) + |
| (fx1 * fy1 * p11); |
| /** @const {number} */ |
| var rounded = (sum + half) >> (tcuBilinearImageCompare.NUM_SUBPIXEL_BITS * 2); |
| |
| DE_ASSERT(deMath.deInRange32(rounded, 0, 0xff)); |
| return rounded; |
| }; |
| |
| tcuBilinearImageCompare.compareUintRGBA8Threshold = function(a, b, thr) { |
| if (a == b) |
| return true; |
| |
| return (Math.abs(UintRGBA8_R(a) - UintRGBA8_R(b)) <= thr.getRed() && |
| Math.abs(UintRGBA8_G(a) - UintRGBA8_G(b)) <= thr.getGreen() && |
| Math.abs(UintRGBA8_B(a) - UintRGBA8_B(b)) <= thr.getBlue() && |
| Math.abs(UintRGBA8_A(a) - UintRGBA8_A(b)) <= thr.getAlpha()); |
| }; |
| |
| /** |
| * @param {tcuTexture.RGBA8View} view |
| * @param {number} u |
| * @param {number} v |
| * @return {number} |
| */ |
| tcuBilinearImageCompare.bilinearSampleUintRGBA8 = function(view, u, v) { |
| /** @type {number} */ var x0 = u >> tcuBilinearImageCompare.NUM_SUBPIXEL_BITS; |
| /** @type {number} */ var y0 = v >> tcuBilinearImageCompare.NUM_SUBPIXEL_BITS; |
| /** @type {number} */ var x1 = x0 + 1; |
| /** @type {number} */ var y1 = y0 + 1; |
| |
| DE_ASSERT(x1 < view.getWidth()); |
| DE_ASSERT(y1 < view.getHeight()); |
| |
| /** @type {number} */ var fx1 = u - (x0 << tcuBilinearImageCompare.NUM_SUBPIXEL_BITS); |
| /** @type {number} */ var fy1 = v - (y0 << tcuBilinearImageCompare.NUM_SUBPIXEL_BITS); |
| |
| /** @type {Array<number>} */ var channelsP00 = view.readUintRGBA8(x0, y0); |
| /** @type {Array<number>} */ var channelsP10 = view.readUintRGBA8(x1, y0); |
| /** @type {Array<number>} */ var channelsP01 = view.readUintRGBA8(x0, y1); |
| /** @type {Array<number>} */ var channelsP11 = view.readUintRGBA8(x1, y1); |
| |
| /** @type {number} */ var res = 0; |
| |
| res = (tcuBilinearImageCompare.interpolateChannel(fx1, fy1, UintRGBA8_R(channelsP00), |
| UintRGBA8_R(channelsP01), UintRGBA8_R(channelsP10), UintRGBA8_R(channelsP11)) & 0xff) << 24; |
| res += (tcuBilinearImageCompare.interpolateChannel(fx1, fy1, UintRGBA8_G(channelsP00), |
| UintRGBA8_G(channelsP01), UintRGBA8_G(channelsP10), UintRGBA8_G(channelsP11)) & 0xff) << 16; |
| res += (tcuBilinearImageCompare.interpolateChannel(fx1, fy1, UintRGBA8_B(channelsP00), |
| UintRGBA8_B(channelsP01), UintRGBA8_B(channelsP10), UintRGBA8_B(channelsP11)) & 0xff) << 8; |
| res += tcuBilinearImageCompare.interpolateChannel(fx1, fy1, UintRGBA8_A(channelsP00), |
| UintRGBA8_A(channelsP01), UintRGBA8_A(channelsP10), UintRGBA8_A(channelsP11)) & 0xff; |
| |
| return res; |
| }; |
| |
| /** |
| * @param {tcuTexture.RGBA8View} reference |
| * @param {tcuTexture.RGBA8View} result |
| * @param {tcuRGBA.RGBA} threshold |
| * @param {number} x |
| * @param {number} y |
| * @return {boolean} |
| */ |
| tcuBilinearImageCompare.comparePixelRGBA8 = function(reference, result, threshold, x, y) { |
| /** @const {tcuRGBA.RGBA} */ var resPix = result.readUintRGBA8(x, y); |
| |
| // Step 1: Compare result pixel to 3x3 neighborhood pixels in reference. |
| /** @const {number} */ var x0 = Math.max(x - 1, 0); |
| /** @const {number} */ var x1 = x; |
| /** @const {number} */ |
| var x2 = Math.min(x + 1, reference.getWidth() - 1); |
| /** @const {number} */ var y0 = Math.max(y - 1, 0); |
| /** @const {number} */ var y1 = y; |
| /** @const {number} */ |
| var y2 = Math.min(y + 1, reference.getHeight() - 1); |
| |
| //tcuBilinearImageCompare.readRGBA8List (reference, x0, y0, x2, y2); |
| |
| if (tcuBilinearImageCompare.compareUintRGBA8Threshold(resPix, reference.readUintRGBA8(x1, y1), threshold) || |
| tcuBilinearImageCompare.compareUintRGBA8Threshold(resPix, reference.readUintRGBA8(x0, y1), threshold) || |
| tcuBilinearImageCompare.compareUintRGBA8Threshold(resPix, reference.readUintRGBA8(x2, y1), threshold) || |
| tcuBilinearImageCompare.compareUintRGBA8Threshold(resPix, reference.readUintRGBA8(x0, y0), threshold) || |
| tcuBilinearImageCompare.compareUintRGBA8Threshold(resPix, reference.readUintRGBA8(x1, y0), threshold) || |
| tcuBilinearImageCompare.compareUintRGBA8Threshold(resPix, reference.readUintRGBA8(x2, y0), threshold) || |
| tcuBilinearImageCompare.compareUintRGBA8Threshold(resPix, reference.readUintRGBA8(x0, y2), threshold) || |
| tcuBilinearImageCompare.compareUintRGBA8Threshold(resPix, reference.readUintRGBA8(x1, y2), threshold) || |
| tcuBilinearImageCompare.compareUintRGBA8Threshold(resPix, reference.readUintRGBA8(x2, y2), threshold)) |
| return true; |
| |
| // Step 2: Compare using bilinear sampling. |
| // \todo [pyry] Optimize sample positions! |
| /** @const {Array<Array<number>>} */ var s_offsets = [ |
| [226, 186], |
| [335, 235], |
| [279, 334], |
| [178, 272], |
| [112, 202], |
| [306, 117], |
| [396, 299], |
| [206, 382], |
| [146, 96], |
| [423, 155], |
| [361, 412], |
| [84, 339], |
| [48, 130], |
| [367, 43], |
| [455, 367], |
| [105, 439], |
| [83, 46], |
| [217, 24], |
| [461, 71], |
| [450, 459], |
| [239, 469], |
| [67, 267], |
| [459, 255], |
| [13, 416], |
| [10, 192], |
| [141, 502], |
| [503, 304], |
| [380, 506] |
| ]; |
| |
| for (var sampleNdx = 0; sampleNdx < s_offsets.length; sampleNdx++) { |
| /** @const {number} */ |
| var u = ((x - 1) << tcuBilinearImageCompare.NUM_SUBPIXEL_BITS) + s_offsets[sampleNdx][0]; |
| /** @const {number} */ |
| var v = ((y - 1) << tcuBilinearImageCompare.NUM_SUBPIXEL_BITS) + s_offsets[sampleNdx][1]; |
| |
| if (!deMath.deInBounds32(u, 0, (reference.getWidth() - 1) << tcuBilinearImageCompare.NUM_SUBPIXEL_BITS) || |
| !deMath.deInBounds32(v, 0, (reference.getHeight() - 1) << tcuBilinearImageCompare.NUM_SUBPIXEL_BITS)) |
| continue; |
| |
| if (tcuBilinearImageCompare.compareUintRGBA8Threshold(resPix, tcuBilinearImageCompare.bilinearSampleUintRGBA8(reference, u, v), threshold)) |
| return true; |
| } |
| |
| return false; |
| }; |
| |
| /** |
| * @param {tcuTexture.RGBA8View} reference |
| * @param {tcuTexture.RGBA8View} result |
| * @param {tcuTexture.PixelBufferAccess} errorMask |
| * @param {tcuRGBA.RGBA} threshold |
| * @return {boolean} |
| */ |
| tcuBilinearImageCompare.bilinearCompareRGBA8 = function(reference, result, errorMask, threshold) { |
| DE_ASSERT(reference.getFormat().isEqual(new tcuTexture.TextureFormat( |
| tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8))); |
| DE_ASSERT(result.getFormat().isEqual(new tcuTexture.TextureFormat( |
| tcuTexture.ChannelOrder.RGBA, tcuTexture.ChannelType.UNORM_INT8))); |
| |
| // Clear error mask first to green (faster this way). |
| errorMask.clear([0.0, 1.0, 0.0, 1.0]); |
| |
| /** @type {boolean} */ var allOk = true; |
| |
| for (var y = 0; y < reference.getHeight(); y++) { |
| for (var x = 0; x < reference.getWidth(); x++) { |
| if (!tcuBilinearImageCompare.comparePixelRGBA8(reference, result, threshold, x, y) && |
| !tcuBilinearImageCompare.comparePixelRGBA8(result, reference, threshold, x, y)) { |
| allOk = false; |
| errorMask.setPixel([1.0, 0.0, 0.0, 1.0], x, y); |
| } |
| } |
| } |
| |
| return allOk; |
| }; |
| |
| /** |
| * @param {tcuTexture.ConstPixelBufferAccess} reference |
| * @param {tcuTexture.ConstPixelBufferAccess} result |
| * @param {tcuTexture.PixelBufferAccess} errorMask |
| * @param {tcuRGBA.RGBA} threshold |
| * @return {boolean} |
| */ |
| tcuBilinearImageCompare.bilinearCompare = function(reference, result, errorMask, threshold) { |
| assertMsgOptions(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight() && result.getDepth() == reference.getDepth(), |
| 'Reference and result images have different dimensions', false, true); |
| |
| assertMsgOptions(errorMask.getWidth() == reference.getWidth() && errorMask.getHeight() == reference.getHeight() && errorMask.getDepth() == reference.getDepth(), |
| 'Reference and error mask images have different dimensions', false, true); |
| |
| /** @type {boolean} */ var isEqual = reference.getFormat().isEqual( |
| new tcuTexture.TextureFormat( |
| tcuTexture.ChannelOrder.RGBA, |
| tcuTexture.ChannelType.UNORM_INT8)); |
| if (isEqual) { |
| /** @type {tcuTexture.RGBA8View} */ var refView = new tcuTexture.RGBA8View(reference); |
| /** @type {tcuTexture.RGBA8View} */ var resView = new tcuTexture.RGBA8View(result); |
| return tcuBilinearImageCompare.bilinearCompareRGBA8(refView, resView, errorMask, threshold); |
| } else |
| throw new Error('Unsupported format for bilinear comparison'); |
| }; |
| |
| }); |