init commit
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.blendWithWhite = blendWithWhite;
|
||||
exports.colorDeltaE94 = colorDeltaE94;
|
||||
exports.rgb2gray = rgb2gray;
|
||||
exports.srgb2xyz = srgb2xyz;
|
||||
exports.xyz2lab = xyz2lab;
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
function blendWithWhite(c, a) {
|
||||
return 255 + (c - 255) * a;
|
||||
}
|
||||
function rgb2gray(r, g, b) {
|
||||
// NOTE: this is the exact integer formula from SSIM.js.
|
||||
// See https://github.com/obartra/ssim/blob/ca8e3c6a6ff5f4f2e232239e0c3d91806f3c97d5/src/matlab/rgb2gray.ts#L56
|
||||
return 77 * r + 150 * g + 29 * b + 128 >> 8;
|
||||
}
|
||||
|
||||
// Percieved color difference defined by CIE94.
|
||||
// See https://en.wikipedia.org/wiki/Color_difference#CIE94
|
||||
//
|
||||
// The result of 1.0 is a "just-noticiable difference".
|
||||
//
|
||||
// Other results interpretation (taken from http://zschuessler.github.io/DeltaE/learn/):
|
||||
// < 1.0 Not perceptible by human eyes.
|
||||
// 1-2 Perceptible through close observation.
|
||||
// 2-10 Perceptible at a glance.
|
||||
// 11-49 Colors are more similar than opposite
|
||||
// 100 Colors are exact opposite
|
||||
function colorDeltaE94(rgb1, rgb2) {
|
||||
const [l1, a1, b1] = xyz2lab(srgb2xyz(rgb1));
|
||||
const [l2, a2, b2] = xyz2lab(srgb2xyz(rgb2));
|
||||
const deltaL = l1 - l2;
|
||||
const deltaA = a1 - a2;
|
||||
const deltaB = b1 - b2;
|
||||
const c1 = Math.sqrt(a1 ** 2 + b1 ** 2);
|
||||
const c2 = Math.sqrt(a2 ** 2 + b2 ** 2);
|
||||
const deltaC = c1 - c2;
|
||||
let deltaH = deltaA ** 2 + deltaB ** 2 - deltaC ** 2;
|
||||
deltaH = deltaH < 0 ? 0 : Math.sqrt(deltaH);
|
||||
// The k1, k2, kL, kC, kH values for "graphic arts" applications.
|
||||
// See https://en.wikipedia.org/wiki/Color_difference#CIE94
|
||||
const k1 = 0.045;
|
||||
const k2 = 0.015;
|
||||
const kL = 1;
|
||||
const kC = 1;
|
||||
const kH = 1;
|
||||
const sC = 1.0 + k1 * c1;
|
||||
const sH = 1.0 + k2 * c1;
|
||||
const sL = 1;
|
||||
return Math.sqrt((deltaL / sL / kL) ** 2 + (deltaC / sC / kC) ** 2 + (deltaH / sH / kH) ** 2);
|
||||
}
|
||||
|
||||
// sRGB -> 1-normalized XYZ (i.e. Y ∈ [0, 1]) with D65 illuminant
|
||||
// See https://en.wikipedia.org/wiki/SRGB#From_sRGB_to_CIE_XYZ
|
||||
function srgb2xyz(rgb) {
|
||||
let r = rgb[0] / 255;
|
||||
let g = rgb[1] / 255;
|
||||
let b = rgb[2] / 255;
|
||||
r = r > 0.04045 ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92;
|
||||
g = g > 0.04045 ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92;
|
||||
b = b > 0.04045 ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92;
|
||||
return [r * 0.4124 + g * 0.3576 + b * 0.1805, r * 0.2126 + g * 0.7152 + b * 0.0722, r * 0.0193 + g * 0.1192 + b * 0.9505];
|
||||
}
|
||||
const sigma_pow2 = 6 * 6 / 29 / 29;
|
||||
const sigma_pow3 = 6 * 6 * 6 / 29 / 29 / 29;
|
||||
|
||||
// 1-normalized CIE XYZ with D65 to L*a*b*
|
||||
// See https://en.wikipedia.org/wiki/CIELAB_color_space#From_CIEXYZ_to_CIELAB
|
||||
function xyz2lab(xyz) {
|
||||
const x = xyz[0] / 0.950489;
|
||||
const y = xyz[1];
|
||||
const z = xyz[2] / 1.088840;
|
||||
const fx = x > sigma_pow3 ? x ** (1 / 3) : x / 3 / sigma_pow2 + 4 / 29;
|
||||
const fy = y > sigma_pow3 ? y ** (1 / 3) : y / 3 / sigma_pow2 + 4 / 29;
|
||||
const fz = z > sigma_pow3 ? z ** (1 / 3) : z / 3 / sigma_pow2 + 4 / 29;
|
||||
const l = 116 * fy - 16;
|
||||
const a = 500 * (fx - fy);
|
||||
const b = 200 * (fy - fz);
|
||||
return [l, a, b];
|
||||
}
|
||||
109
bin/pac/tools/.playwright/package/lib/image_tools/compare.js
Normal file
109
bin/pac/tools/.playwright/package/lib/image_tools/compare.js
Normal file
@@ -0,0 +1,109 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.compare = compare;
|
||||
var _colorUtils = require("./colorUtils");
|
||||
var _imageChannel = require("./imageChannel");
|
||||
var _stats = require("./stats");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
const SSIM_WINDOW_RADIUS = 15;
|
||||
const VARIANCE_WINDOW_RADIUS = 1;
|
||||
function drawPixel(width, data, x, y, r, g, b) {
|
||||
const idx = (y * width + x) * 4;
|
||||
data[idx + 0] = r;
|
||||
data[idx + 1] = g;
|
||||
data[idx + 2] = b;
|
||||
data[idx + 3] = 255;
|
||||
}
|
||||
function compare(actual, expected, diff, width, height, options = {}) {
|
||||
const {
|
||||
maxColorDeltaE94 = 1.0
|
||||
} = options;
|
||||
const paddingSize = Math.max(VARIANCE_WINDOW_RADIUS, SSIM_WINDOW_RADIUS);
|
||||
const paddingColorEven = [255, 0, 255];
|
||||
const paddingColorOdd = [0, 255, 0];
|
||||
const [r1, g1, b1] = _imageChannel.ImageChannel.intoRGB(width, height, expected, {
|
||||
paddingSize,
|
||||
paddingColorEven,
|
||||
paddingColorOdd
|
||||
});
|
||||
const [r2, g2, b2] = _imageChannel.ImageChannel.intoRGB(width, height, actual, {
|
||||
paddingSize,
|
||||
paddingColorEven,
|
||||
paddingColorOdd
|
||||
});
|
||||
const noop = (x, y) => {};
|
||||
const drawRedPixel = diff ? (x, y) => drawPixel(width, diff, x - paddingSize, y - paddingSize, 255, 0, 0) : noop;
|
||||
const drawYellowPixel = diff ? (x, y) => drawPixel(width, diff, x - paddingSize, y - paddingSize, 255, 255, 0) : noop;
|
||||
const drawGrayPixel = diff ? (x, y) => {
|
||||
const gray = (0, _colorUtils.rgb2gray)(r1.get(x, y), g1.get(x, y), b1.get(x, y));
|
||||
const value = (0, _colorUtils.blendWithWhite)(gray, 0.1);
|
||||
drawPixel(width, diff, x - paddingSize, y - paddingSize, value, value, value);
|
||||
} : noop;
|
||||
let fastR, fastG, fastB;
|
||||
let diffCount = 0;
|
||||
for (let y = paddingSize; y < r1.height - paddingSize; ++y) {
|
||||
for (let x = paddingSize; x < r1.width - paddingSize; ++x) {
|
||||
// Fast-path: equal pixels.
|
||||
if (r1.get(x, y) === r2.get(x, y) && g1.get(x, y) === g2.get(x, y) && b1.get(x, y) === b2.get(x, y)) {
|
||||
drawGrayPixel(x, y);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compare pixel colors using the dE94 color difference formulae.
|
||||
// The dE94 is normalized so that the value of 1.0 is the "just-noticeable-difference".
|
||||
// Color difference below 1.0 is not noticeable to a human eye, so we can disregard it.
|
||||
// See https://en.wikipedia.org/wiki/Color_difference
|
||||
const delta = (0, _colorUtils.colorDeltaE94)([r1.get(x, y), g1.get(x, y), b1.get(x, y)], [r2.get(x, y), g2.get(x, y), b2.get(x, y)]);
|
||||
if (delta <= maxColorDeltaE94) {
|
||||
drawGrayPixel(x, y);
|
||||
continue;
|
||||
}
|
||||
|
||||
// if this pixel is a part of a flood fill of a 3x3 square then it cannot be
|
||||
// anti-aliasing pixel so it must be a pixel difference.
|
||||
if (!fastR || !fastG || !fastB) {
|
||||
fastR = new _stats.FastStats(r1, r2);
|
||||
fastG = new _stats.FastStats(g1, g2);
|
||||
fastB = new _stats.FastStats(b1, b2);
|
||||
}
|
||||
const [varX1, varY1] = r1.boundXY(x - VARIANCE_WINDOW_RADIUS, y - VARIANCE_WINDOW_RADIUS);
|
||||
const [varX2, varY2] = r1.boundXY(x + VARIANCE_WINDOW_RADIUS, y + VARIANCE_WINDOW_RADIUS);
|
||||
const var1 = fastR.varianceC1(varX1, varY1, varX2, varY2) + fastG.varianceC1(varX1, varY1, varX2, varY2) + fastB.varianceC1(varX1, varY1, varX2, varY2);
|
||||
const var2 = fastR.varianceC2(varX1, varY1, varX2, varY2) + fastG.varianceC2(varX1, varY1, varX2, varY2) + fastB.varianceC2(varX1, varY1, varX2, varY2);
|
||||
if (var1 === 0 && var2 === 0) {
|
||||
drawRedPixel(x, y);
|
||||
++diffCount;
|
||||
continue;
|
||||
}
|
||||
const [ssimX1, ssimY1] = r1.boundXY(x - SSIM_WINDOW_RADIUS, y - SSIM_WINDOW_RADIUS);
|
||||
const [ssimX2, ssimY2] = r1.boundXY(x + SSIM_WINDOW_RADIUS, y + SSIM_WINDOW_RADIUS);
|
||||
const ssimRGB = ((0, _stats.ssim)(fastR, ssimX1, ssimY1, ssimX2, ssimY2) + (0, _stats.ssim)(fastG, ssimX1, ssimY1, ssimX2, ssimY2) + (0, _stats.ssim)(fastB, ssimX1, ssimY1, ssimX2, ssimY2)) / 3.0;
|
||||
const isAntialiassed = ssimRGB >= 0.99;
|
||||
if (isAntialiassed) {
|
||||
drawYellowPixel(x, y);
|
||||
} else {
|
||||
drawRedPixel(x, y);
|
||||
++diffCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
return diffCount;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.ImageChannel = void 0;
|
||||
var _colorUtils = require("./colorUtils");
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
class ImageChannel {
|
||||
static intoRGB(width, height, data, options = {}) {
|
||||
const {
|
||||
paddingSize = 0,
|
||||
paddingColorOdd = [255, 0, 255],
|
||||
paddingColorEven = [0, 255, 0]
|
||||
} = options;
|
||||
const newWidth = width + 2 * paddingSize;
|
||||
const newHeight = height + 2 * paddingSize;
|
||||
const r = new Uint8Array(newWidth * newHeight);
|
||||
const g = new Uint8Array(newWidth * newHeight);
|
||||
const b = new Uint8Array(newWidth * newHeight);
|
||||
for (let y = 0; y < newHeight; ++y) {
|
||||
for (let x = 0; x < newWidth; ++x) {
|
||||
const index = y * newWidth + x;
|
||||
if (y >= paddingSize && y < newHeight - paddingSize && x >= paddingSize && x < newWidth - paddingSize) {
|
||||
const offset = ((y - paddingSize) * width + (x - paddingSize)) * 4;
|
||||
const alpha = data[offset + 3] === 255 ? 1 : data[offset + 3] / 255;
|
||||
r[index] = (0, _colorUtils.blendWithWhite)(data[offset], alpha);
|
||||
g[index] = (0, _colorUtils.blendWithWhite)(data[offset + 1], alpha);
|
||||
b[index] = (0, _colorUtils.blendWithWhite)(data[offset + 2], alpha);
|
||||
} else {
|
||||
const color = (y + x) % 2 === 0 ? paddingColorEven : paddingColorOdd;
|
||||
r[index] = color[0];
|
||||
g[index] = color[1];
|
||||
b[index] = color[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
return [new ImageChannel(newWidth, newHeight, r), new ImageChannel(newWidth, newHeight, g), new ImageChannel(newWidth, newHeight, b)];
|
||||
}
|
||||
constructor(width, height, data) {
|
||||
this.data = void 0;
|
||||
this.width = void 0;
|
||||
this.height = void 0;
|
||||
this.data = data;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
get(x, y) {
|
||||
return this.data[y * this.width + x];
|
||||
}
|
||||
boundXY(x, y) {
|
||||
return [Math.min(Math.max(x, 0), this.width - 1), Math.min(Math.max(y, 0), this.height - 1)];
|
||||
}
|
||||
}
|
||||
exports.ImageChannel = ImageChannel;
|
||||
102
bin/pac/tools/.playwright/package/lib/image_tools/stats.js
Normal file
102
bin/pac/tools/.playwright/package/lib/image_tools/stats.js
Normal file
@@ -0,0 +1,102 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.FastStats = void 0;
|
||||
exports.ssim = ssim;
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// Image channel has a 8-bit depth.
|
||||
const DYNAMIC_RANGE = 2 ** 8 - 1;
|
||||
function ssim(stats, x1, y1, x2, y2) {
|
||||
const mean1 = stats.meanC1(x1, y1, x2, y2);
|
||||
const mean2 = stats.meanC2(x1, y1, x2, y2);
|
||||
const var1 = stats.varianceC1(x1, y1, x2, y2);
|
||||
const var2 = stats.varianceC2(x1, y1, x2, y2);
|
||||
const cov = stats.covariance(x1, y1, x2, y2);
|
||||
const c1 = (0.01 * DYNAMIC_RANGE) ** 2;
|
||||
const c2 = (0.03 * DYNAMIC_RANGE) ** 2;
|
||||
return (2 * mean1 * mean2 + c1) * (2 * cov + c2) / (mean1 ** 2 + mean2 ** 2 + c1) / (var1 + var2 + c2);
|
||||
}
|
||||
class FastStats {
|
||||
constructor(c1, c2) {
|
||||
this.c1 = void 0;
|
||||
this.c2 = void 0;
|
||||
this._partialSumC1 = void 0;
|
||||
this._partialSumC2 = void 0;
|
||||
this._partialSumMult = void 0;
|
||||
this._partialSumSq1 = void 0;
|
||||
this._partialSumSq2 = void 0;
|
||||
this.c1 = c1;
|
||||
this.c2 = c2;
|
||||
const {
|
||||
width,
|
||||
height
|
||||
} = c1;
|
||||
this._partialSumC1 = new Array(width * height);
|
||||
this._partialSumC2 = new Array(width * height);
|
||||
this._partialSumSq1 = new Array(width * height);
|
||||
this._partialSumSq2 = new Array(width * height);
|
||||
this._partialSumMult = new Array(width * height);
|
||||
const recalc = (mx, idx, initial, x, y) => {
|
||||
mx[idx] = initial;
|
||||
if (y > 0) mx[idx] += mx[(y - 1) * width + x];
|
||||
if (x > 0) mx[idx] += mx[y * width + x - 1];
|
||||
if (x > 0 && y > 0) mx[idx] -= mx[(y - 1) * width + x - 1];
|
||||
};
|
||||
for (let y = 0; y < height; ++y) {
|
||||
for (let x = 0; x < width; ++x) {
|
||||
const idx = y * width + x;
|
||||
recalc(this._partialSumC1, idx, this.c1.data[idx], x, y);
|
||||
recalc(this._partialSumC2, idx, this.c2.data[idx], x, y);
|
||||
recalc(this._partialSumSq1, idx, this.c1.data[idx] * this.c1.data[idx], x, y);
|
||||
recalc(this._partialSumSq2, idx, this.c2.data[idx] * this.c2.data[idx], x, y);
|
||||
recalc(this._partialSumMult, idx, this.c1.data[idx] * this.c2.data[idx], x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
_sum(partialSum, x1, y1, x2, y2) {
|
||||
const width = this.c1.width;
|
||||
let result = partialSum[y2 * width + x2];
|
||||
if (y1 > 0) result -= partialSum[(y1 - 1) * width + x2];
|
||||
if (x1 > 0) result -= partialSum[y2 * width + x1 - 1];
|
||||
if (x1 > 0 && y1 > 0) result += partialSum[(y1 - 1) * width + x1 - 1];
|
||||
return result;
|
||||
}
|
||||
meanC1(x1, y1, x2, y2) {
|
||||
const N = (y2 - y1 + 1) * (x2 - x1 + 1);
|
||||
return this._sum(this._partialSumC1, x1, y1, x2, y2) / N;
|
||||
}
|
||||
meanC2(x1, y1, x2, y2) {
|
||||
const N = (y2 - y1 + 1) * (x2 - x1 + 1);
|
||||
return this._sum(this._partialSumC2, x1, y1, x2, y2) / N;
|
||||
}
|
||||
varianceC1(x1, y1, x2, y2) {
|
||||
const N = (y2 - y1 + 1) * (x2 - x1 + 1);
|
||||
return (this._sum(this._partialSumSq1, x1, y1, x2, y2) - this._sum(this._partialSumC1, x1, y1, x2, y2) ** 2 / N) / N;
|
||||
}
|
||||
varianceC2(x1, y1, x2, y2) {
|
||||
const N = (y2 - y1 + 1) * (x2 - x1 + 1);
|
||||
return (this._sum(this._partialSumSq2, x1, y1, x2, y2) - this._sum(this._partialSumC2, x1, y1, x2, y2) ** 2 / N) / N;
|
||||
}
|
||||
covariance(x1, y1, x2, y2) {
|
||||
const N = (y2 - y1 + 1) * (x2 - x1 + 1);
|
||||
return (this._sum(this._partialSumMult, x1, y1, x2, y2) - this._sum(this._partialSumC1, x1, y1, x2, y2) * this._sum(this._partialSumC2, x1, y1, x2, y2) / N) / N;
|
||||
}
|
||||
}
|
||||
exports.FastStats = FastStats;
|
||||
Reference in New Issue
Block a user