renderdiff: add magnification glass of rendering (#9265)
- Show magnifying glass when diffing between two images - Add checkbox to enable or disable feature
This commit is contained in:
@@ -53,10 +53,27 @@ class ExpandedPassedResult extends LitElement {
|
||||
render() {
|
||||
if (this.content) {
|
||||
const url = getGoldenUrl(this.content);
|
||||
return html`<tiff-viewer fileurl="${url}"></tiff-viewer>`;
|
||||
return html`
|
||||
<div>
|
||||
<div style="margin-bottom: 10px;">
|
||||
<label>
|
||||
<input type="checkbox" id="magnifierToggle" checked @change="${this._onMagnifierToggle}">
|
||||
Enable Magnifier
|
||||
</label>
|
||||
</div>
|
||||
<tiff-viewer fileurl="${url}" magnifier-enabled id="passedViewer"></tiff-viewer>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
return html``;
|
||||
}
|
||||
|
||||
_onMagnifierToggle(ev) {
|
||||
const viewer = this.shadowRoot.getElementById('passedViewer');
|
||||
if (viewer) {
|
||||
viewer.magnifierEnabled = ev.target.checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
customElements.define('expanded-passed-result', ExpandedPassedResult);
|
||||
|
||||
@@ -97,6 +114,7 @@ class ExpandedComparisonResult extends LitElement {
|
||||
leftViewType: { type: String },
|
||||
rightViewType: { type: String },
|
||||
disableDropdowns: { type: Boolean },
|
||||
magnifierEnabled: { type: Boolean },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -116,6 +134,12 @@ class ExpandedComparisonResult extends LitElement {
|
||||
margin-top: 22px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.diff-container {
|
||||
position: relative;
|
||||
}
|
||||
.viewer-container {
|
||||
position: relative;
|
||||
}
|
||||
.selector {
|
||||
margin: 8px 0;
|
||||
}
|
||||
@@ -132,6 +156,10 @@ class ExpandedComparisonResult extends LitElement {
|
||||
this.leftViewType = 'golden';
|
||||
this.rightViewType = 'golden';
|
||||
this.disableDropdowns = false;
|
||||
this.magnifierEnabled = true;
|
||||
this.diffCanvasRect = null;
|
||||
this.originalDiffImageData = null;
|
||||
this.globalMousePosition = null;
|
||||
|
||||
this.addEventListener(
|
||||
'image-loaded',
|
||||
@@ -156,7 +184,9 @@ class ExpandedComparisonResult extends LitElement {
|
||||
<div>${current.name}</div>
|
||||
<tiff-viewer id="viewer-${name}" class="viewer"
|
||||
name="${current.name}"
|
||||
fileurl="${url}"></tiff-viewer>
|
||||
fileurl="${url}"
|
||||
?magnifier-enabled="${this.magnifierEnabled}"
|
||||
disable-mouse-handlers></tiff-viewer>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -233,6 +263,8 @@ class ExpandedComparisonResult extends LitElement {
|
||||
if (diff.result == RES_DIFFERENT_PIXELS) {
|
||||
this.diffResult = diff;
|
||||
this.showDiff = true;
|
||||
// Reset original diff data when new diff is computed
|
||||
this.originalDiffImageData = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,6 +295,228 @@ class ExpandedComparisonResult extends LitElement {
|
||||
// Create the ImageData from the modified copy.
|
||||
const imgData = new ImageData(diffImgCopy, diffResult.dim.width, diffResult.dim.height);
|
||||
diffCtx.putImageData(imgData, 0, 0);
|
||||
|
||||
// Store both original and current diff image data for magnifier
|
||||
if (!this.originalDiffImageData) {
|
||||
this.originalDiffImageData = new ImageData(diffResult.diffImg.slice(), diffResult.dim.width, diffResult.dim.height);
|
||||
}
|
||||
this.currentDiffImageData = imgData;
|
||||
}
|
||||
|
||||
_onDiffMouseEnter(event) {
|
||||
if (!this.showDiff || !this.currentDiffImageData) return;
|
||||
this.diffCanvasRect = event.target.getBoundingClientRect();
|
||||
}
|
||||
|
||||
_onGlobalMouseLeave(event) {
|
||||
// Hide all magnifiers when mouse leaves the viewer container
|
||||
const leftViewer = this.shadowRoot.querySelector('#viewer-left');
|
||||
const rightViewer = this.shadowRoot.querySelector('#viewer-right');
|
||||
const diffMagnifier = this.shadowRoot.getElementById('diffMagnifier');
|
||||
|
||||
if (leftViewer) {
|
||||
const leftMagnifier = leftViewer.shadowRoot?.getElementById('magnifier');
|
||||
if (leftMagnifier) leftMagnifier.hide();
|
||||
}
|
||||
|
||||
if (rightViewer) {
|
||||
const rightMagnifier = rightViewer.shadowRoot?.getElementById('magnifier');
|
||||
if (rightMagnifier) rightMagnifier.hide();
|
||||
}
|
||||
|
||||
if (diffMagnifier) diffMagnifier.hide();
|
||||
}
|
||||
|
||||
_onGlobalMouseMove(event) {
|
||||
this.globalMousePosition = { clientX: event.clientX, clientY: event.clientY };
|
||||
|
||||
// Update all three magnifiers
|
||||
this._updateLeftMagnifier(event);
|
||||
this._updateRightMagnifier(event);
|
||||
this._updateDiffMagnifier(event);
|
||||
}
|
||||
|
||||
_updateLeftMagnifier(event) {
|
||||
if (!this.magnifierEnabled) return;
|
||||
const leftViewer = this.shadowRoot.querySelector('#viewer-left');
|
||||
if (!leftViewer) return;
|
||||
|
||||
const canvas = leftViewer.shadowRoot?.querySelector('canvas');
|
||||
if (!canvas) return;
|
||||
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const leftMagnifier = leftViewer.shadowRoot?.getElementById('magnifier');
|
||||
|
||||
if (!leftMagnifier) return;
|
||||
|
||||
// Get image data from the viewer
|
||||
const imageData = leftViewer.imageData;
|
||||
if (!imageData) {
|
||||
leftMagnifier.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if mouse is over any of the three views (left, right, or diff)
|
||||
if (!this._isMouseOverAnyView(event)) {
|
||||
leftMagnifier.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate the equivalent position on the left image
|
||||
const { imageX, imageY, mouseX, mouseY } = this._calculateEquivalentPosition(event, rect, imageData);
|
||||
|
||||
// Position magnifier relative to the canvas within the left viewer
|
||||
leftMagnifier.updateMagnifier(imageData, imageX, imageY, mouseX, mouseY, rect);
|
||||
}
|
||||
|
||||
_updateRightMagnifier(event) {
|
||||
if (!this.magnifierEnabled) return;
|
||||
const rightViewer = this.shadowRoot.querySelector('#viewer-right');
|
||||
if (!rightViewer) return;
|
||||
|
||||
const canvas = rightViewer.shadowRoot?.querySelector('canvas');
|
||||
if (!canvas) return;
|
||||
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const rightMagnifier = rightViewer.shadowRoot?.getElementById('magnifier');
|
||||
|
||||
if (!rightMagnifier) return;
|
||||
|
||||
// Get image data from the viewer
|
||||
const imageData = rightViewer.imageData;
|
||||
if (!imageData) {
|
||||
rightMagnifier.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if mouse is over any of the three views (left, right, or diff)
|
||||
if (!this._isMouseOverAnyView(event)) {
|
||||
rightMagnifier.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate the equivalent position on the right image
|
||||
const { imageX, imageY, mouseX, mouseY } = this._calculateEquivalentPosition(event, rect, imageData);
|
||||
|
||||
// Position magnifier relative to the canvas within the right viewer
|
||||
rightMagnifier.updateMagnifier(imageData, imageX, imageY, mouseX, mouseY, rect);
|
||||
}
|
||||
|
||||
_updateDiffMagnifier(event) {
|
||||
if (!this.magnifierEnabled || !this.showDiff || !this.currentDiffImageData || !this.diffCanvasRect) return;
|
||||
|
||||
const rect = this.diffCanvasRect;
|
||||
const diffMagnifier = this.shadowRoot.getElementById('diffMagnifier');
|
||||
|
||||
if (!diffMagnifier) return;
|
||||
|
||||
// Check if mouse is over any of the three views (left, right, or diff)
|
||||
if (!this._isMouseOverAnyView(event)) {
|
||||
diffMagnifier.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate the equivalent position on the diff image
|
||||
const { imageX, imageY, mouseX, mouseY } = this._calculateEquivalentPosition(event, rect, this.currentDiffImageData);
|
||||
|
||||
// Position magnifier relative to the diff canvas
|
||||
diffMagnifier.updateMagnifier(this.currentDiffImageData, imageX, imageY, mouseX, mouseY, rect, 8, this.originalDiffImageData);
|
||||
}
|
||||
|
||||
_isMouseOverElement(event, rect) {
|
||||
return event.clientX >= rect.left &&
|
||||
event.clientX <= rect.right &&
|
||||
event.clientY >= rect.top &&
|
||||
event.clientY <= rect.bottom;
|
||||
}
|
||||
|
||||
_isMouseOverAnyView(event) {
|
||||
// Check if mouse is over left viewer
|
||||
const leftViewer = this.shadowRoot.querySelector('#viewer-left');
|
||||
if (leftViewer) {
|
||||
const leftCanvas = leftViewer.shadowRoot?.querySelector('canvas');
|
||||
if (leftCanvas && this._isMouseOverElement(event, leftCanvas.getBoundingClientRect())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if mouse is over right viewer
|
||||
const rightViewer = this.shadowRoot.querySelector('#viewer-right');
|
||||
if (rightViewer) {
|
||||
const rightCanvas = rightViewer.shadowRoot?.querySelector('canvas');
|
||||
if (rightCanvas && this._isMouseOverElement(event, rightCanvas.getBoundingClientRect())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if mouse is over diff canvas
|
||||
if (this.showDiff && this.diffCanvasRect) {
|
||||
if (this._isMouseOverElement(event, this.diffCanvasRect)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
_calculateEquivalentPosition(event, targetRect, targetImageData) {
|
||||
// Find which view the mouse is actually over and calculate the equivalent position
|
||||
const leftViewer = this.shadowRoot.querySelector('#viewer-left');
|
||||
const rightViewer = this.shadowRoot.querySelector('#viewer-right');
|
||||
|
||||
let sourceRect = null;
|
||||
let sourceImageData = null;
|
||||
|
||||
// Determine source view
|
||||
if (leftViewer) {
|
||||
const leftCanvas = leftViewer.shadowRoot?.querySelector('canvas');
|
||||
if (leftCanvas && this._isMouseOverElement(event, leftCanvas.getBoundingClientRect())) {
|
||||
sourceRect = leftCanvas.getBoundingClientRect();
|
||||
sourceImageData = leftViewer.imageData;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sourceRect && rightViewer) {
|
||||
const rightCanvas = rightViewer.shadowRoot?.querySelector('canvas');
|
||||
if (rightCanvas && this._isMouseOverElement(event, rightCanvas.getBoundingClientRect())) {
|
||||
sourceRect = rightCanvas.getBoundingClientRect();
|
||||
sourceImageData = rightViewer.imageData;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sourceRect && this.showDiff && this.diffCanvasRect) {
|
||||
if (this._isMouseOverElement(event, this.diffCanvasRect)) {
|
||||
sourceRect = this.diffCanvasRect;
|
||||
sourceImageData = this.currentDiffImageData;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sourceRect || !sourceImageData) {
|
||||
// Fallback to target rect
|
||||
sourceRect = targetRect;
|
||||
sourceImageData = targetImageData;
|
||||
}
|
||||
|
||||
// Calculate relative position in source
|
||||
const sourceMouseX = event.clientX - sourceRect.left;
|
||||
const sourceMouseY = event.clientY - sourceRect.top;
|
||||
const sourceScaleX = sourceImageData.width / sourceRect.width;
|
||||
const sourceScaleY = sourceImageData.height / sourceRect.height;
|
||||
const sourceImageX = Math.floor(sourceMouseX * sourceScaleX);
|
||||
const sourceImageY = Math.floor(sourceMouseY * sourceScaleY);
|
||||
|
||||
// Convert to target coordinates
|
||||
const targetScaleX = targetImageData.width / targetRect.width;
|
||||
const targetScaleY = targetImageData.height / targetRect.height;
|
||||
const targetMouseX = sourceImageX / targetScaleX;
|
||||
const targetMouseY = sourceImageY / targetScaleY;
|
||||
|
||||
return {
|
||||
imageX: sourceImageX,
|
||||
imageY: sourceImageY,
|
||||
mouseX: targetMouseX,
|
||||
mouseY: targetMouseY
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -274,22 +528,34 @@ class ExpandedComparisonResult extends LitElement {
|
||||
const v1 = this._viewer("left", this.tests, this.left, this.leftViewType);
|
||||
const v2 = this._viewer("right", this.tests, this.right, this.rightViewType);
|
||||
const onMultiplierChange = (ev) => {
|
||||
const multiplierValue = this.shadowRoot.querySelector('#multiplierValue');
|
||||
multiplierValue.textContent = ev.target.value;
|
||||
this._updateDiffCanvas(this.diffResult, ev.target.value);
|
||||
};
|
||||
const onMagnifierToggle = (ev) => {
|
||||
this.magnifierEnabled = ev.target.checked;
|
||||
};
|
||||
return html`
|
||||
<div class="main-container">
|
||||
<div class="viewer-container">
|
||||
<div class="viewer-container" @mousemove="${this._onGlobalMouseMove}" @mouseleave="${this._onGlobalMouseLeave}">
|
||||
${v1}
|
||||
<div style="flex: 1; ${diffStyle}">
|
||||
<canvas id="diffCanvas"></canvas>
|
||||
<div style="flex: 1; ${diffStyle}" class="diff-container">
|
||||
<canvas id="diffCanvas" @mouseenter="${this._onDiffMouseEnter}"></canvas>
|
||||
<image-magnifier id="diffMagnifier"></image-magnifier>
|
||||
</div>
|
||||
${v2}
|
||||
</div>
|
||||
<div class="control" style="${diffStyle}">
|
||||
<div>
|
||||
<div>Difference Multiplier</div>
|
||||
<div>Difference Multiplier: <span id="multiplierValue">1</span></div>
|
||||
<input type="range" min="1" max="100" value="1" id="diffMultiplier" @input=${onMultiplierChange}>
|
||||
</div>
|
||||
<div style="margin-top: 10px;">
|
||||
<label>
|
||||
<input type="checkbox" .checked="${this.magnifierEnabled}" @change=${onMagnifierToggle}>
|
||||
Enable Magnifier
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user