@syngrisi/syngrisi
Version:
Syngrisi - Visual Testing Tool
168 lines (148 loc) • 7.28 kB
text/typescript
/* eslint-disable no-console */
import { fabric } from 'fabric';
import { MainView } from '../Canvas/mainView';
export interface IGroup {
minX: number
maxX: number
minY: number
maxY: number
imageData: any,
members: { x: number, y: number }[],
}
function getDiffImageData(image: any) {
const canvas = document.createElement('canvas');
canvas.width = image.width;
canvas.height = image.height;
const ctx = canvas.getContext('2d');
ctx!.drawImage(image, 0, 0);
const imgData = ctx!.getImageData(0, 0, image.width, image.height);
return imgData;
}
export function highlightDiff(mainView: MainView, highlightsGroups: IGroup[] | null, imageData: any)
: Promise<{ groups: IGroup[], diffImageData: any }> {
return new Promise((resolve) => {
// remove highlights
mainView.canvas.getObjects()
.filter((x) => x.name === 'highlight')
.forEach((x) => mainView.canvas.remove(x));
// get image data
const urlData = mainView.diffImage.toDataURL({});
const img = new Image();
img.src = urlData;
img.onload = () => {
console.time('get_image_data');
const diffImageData = imageData || getDiffImageData(img);
console.timeEnd('get_image_data');
console.time('process_data');
const createNewGroup = (x: number, y: number) => ({
minX: x,
maxX: x,
minY: y,
maxY: y,
members: [{ x, y }],
});
// get pixel color string from flat array of image data e.g.: '255,0,255,255'(rgba)
// eslint-disable-next-line max-len
const getPixel = (x: number, y: number, curCount: number): string => diffImageData.data.slice(curCount, curCount + 4).join();
const diffColor = '255,0,255,255';
const groups: IGroup[] = highlightsGroups || [];
console.time('group formation');
if (groups.length < 1) {
let cursorCount = 0;
for (let y = 0; y < diffImageData.height; y += 1) {
for (let x = 0; x < diffImageData.width; x += 1) {
// console.log(getPixel(x, y, cursorCount), diffColor);
if (getPixel(x, y, cursorCount) === diffColor) {
const suitableGroup = groups.find((group) => x >= group.minX - 1
&& x <= group.maxX + 1
&& y >= group.minY
&& y <= group.maxY + 1);
if (!suitableGroup) {
groups.push(createNewGroup(x, y));
} else if (
getPixel(x - 1, y, cursorCount) === diffColor // left
|| getPixel(x - 1, y - 1, cursorCount) === diffColor // top left
|| getPixel(x, y - 1, cursorCount) === diffColor // top
|| getPixel(x + 1, y - 1, cursorCount) === diffColor // top right
|| getPixel(x + 1, y, cursorCount) === diffColor // right
|| getPixel(x + 1, y + 1, cursorCount) === diffColor // bottom right
|| getPixel(x, y + 1, cursorCount) === diffColor // bottom
|| getPixel(x - 1, y + 1, cursorCount) === diffColor // bottom left
) {
suitableGroup.members.push({ x, y });
if (x < suitableGroup.minX) suitableGroup.minX = x;
if (x > suitableGroup.maxX) suitableGroup.maxX = x;
if (y > suitableGroup.maxY) suitableGroup.maxY = y;
}
}
cursorCount += 4;
}
}
}
console.timeEnd('group formation');
console.time('group handling');
// eslint-disable-next-line no-restricted-syntax
for (const group of groups) {
// console.log(group);
const top = group.minY + (group.maxY - group.minY) / 2;
const left = group.minX + (group.maxX - group.minX) / 2;
const circle = new fabric.Circle({
name: 'highlight',
originX: 'center',
originY: 'center',
left,
top,
// radius: 5,
data: { group },
fill: '#D6336C',
opacity: 0.3,
strokeWidth: 0,
selectable: false,
});
mainView.canvas.add(circle);
}
console.timeEnd('group handling');
console.timeEnd('process_data');
const highlightRemoving = () => {
mainView.canvas.getObjects()
.filter((x) => x.name === 'highlight')
.forEach((x) => mainView.canvas.remove(x));
};
// highlights animation
setTimeout(() => {
mainView.canvas.getObjects()
.filter((x) => x.name === 'highlight')
.forEach((circle) => {
const suitableRadius = Math.max(
circle.data.group.maxX - circle.data.group.minX,
circle.data.group.maxY - circle.data.group.minY,
);
const radius = ((suitableRadius > 25 ? suitableRadius : 25) + Math.floor(Math.random() * 10))
/ mainView.canvas.getZoom();
circle.animate('opacity', '0.5', {
onChange: mainView.canvas.renderAll.bind(mainView.canvas),
duration: 500,
});
circle.animate('radius', String(radius), {
onChange: mainView.canvas.renderAll.bind(mainView.canvas),
duration: 500,
onComplete: () => {
circle.animate('radius', '0.00', {
onChange: mainView.canvas.renderAll.bind(mainView.canvas),
// slowHighlight - for testing purposes
duration: window?.slowHighlight ? 15000 : 700,
onComplete: highlightRemoving,
});
circle.animate('opacity', '0.30', {
onChange: mainView.canvas.renderAll.bind(mainView.canvas),
duration: window?.slowHighlight ? 3000 : 700,
});
},
});
});
mainView.canvas.renderAll();
}, 0);
return resolve({ groups, diffImageData });
};
});
}