Files
ycode/scripts/compare-chat-header-pixels.py
T

55 lines
3.7 KiB
Python

"""Compare fixed REA captures with the one calibrated YCode header fixture mode."""
from pathlib import Path
from hashlib import sha256
import json
from PIL import Image, ImageChops
ROOT = Path(__file__).resolve().parents[1]
OUTPUT = ROOT / '.tmp/chat-header-native-swiftshader-20261016'
report = json.loads((OUTPUT / 'report.json').read_text(encoding='utf-8'))
rendering = report['rendering']
assert rendering['mode'] == 'visible-swiftshader' and rendering['visible']
assert rendering['gpu']['auxAttributes']['displayType'] == 'ANGLE_SWIFTSHADER'
assert rendering['gpu']['auxAttributes']['glImplementationParts'] == '(gl=egl-angle,angle=swiftshader)'
assert rendering['gpuFeatures']['gpu_compositing'] == 'disabled_software'
regions = [
('title', 'rea-chat-header-final-20261016/workbuddy.png', 'collapsed.png', (308, 50, 388, 74), []),
('search-button', 'rea-chat-header-final-20261016/workbuddy.png', 'collapsed.png', (1215, 46, 1248, 78), []),
('share-button', 'rea-chat-header-final-20261016/workbuddy.png', 'collapsed.png', (1251, 46, 1284, 78), []),
('panel-button', 'rea-chat-header-final-20261016/workbuddy.png', 'collapsed.png', (1287, 46, 1320, 78), []),
('header', 'rea-chat-header-final-20261016/workbuddy.png', 'collapsed.png', (285, 30, 1336, 94), []),
# Empty query: no dynamic counter. Exclude only the 3px caret column at the input start.
('search', 'rea-chat-header-title-20261016/workbuddy.png', 'search.png', (947, 46, 1248, 78), [(977, 50, 980, 74)]),
('edit-selected-title', 'rea-chat-header-title-20261016/frame-001.png', 'edit.png', (308, 50, 512, 74), []),
]
results = []
for name, reference, candidate, box, masks in regions:
source_path, own_path = ROOT / '.tmp' / reference, OUTPUT / candidate
a, b = Image.open(source_path).convert('RGB'), Image.open(own_path).convert('RGB')
assert a.size == b.size == (1336, 901)
diff = ImageChops.difference(a.crop(box), b.crop(box))
compared, changed, total, maximum = 0, 0, 0, 0
differences = []
for y in range(box[1], box[3]):
for x in range(box[0], box[2]):
if any(l <= x < r and t <= y < bt for l, t, r, bt in masks):
continue
delta = diff.getpixel((x - box[0], y - box[1]))
compared += 1
if any(delta):
changed += 1
differences.append({'x': x, 'y': y, 'source': a.getpixel((x, y)), 'candidate': b.getpixel((x, y))})
total += sum(delta)
maximum = max(maximum, *delta)
diff.save(OUTPUT / (name + '-diff.png'))
results.append({'name': name, 'source': reference, 'candidate': candidate, 'box': box, 'masks': masks,
'sourceSha256': sha256(source_path.read_bytes()).hexdigest(), 'candidateSha256': sha256(own_path.read_bytes()).hexdigest(),
'comparedPixels': compared, 'differentPixels': changed, 'maxChannelDifference': maximum,
'meanAbsoluteChannelDifference': total / (compared * 3), 'differentPixelSamples': differences[:60]})
summary = {'mode': rendering['mode'], 'displayType': rendering['gpu']['auxAttributes']['displayType'],
'gpuCompositing': rendering['gpuFeatures']['gpu_compositing'], 'channelTolerance': 0,
'regions': results, 'limitations': ['Fixed Windows/DPR1/1336x901 component fixture only. Production integration, tooltips, prompt-list states, compact/Mac/dark are outside these pixel results.',
'Remaining different pixels are preserved and reported; no tolerance or reference-pixel patch was applied.']}
(OUTPUT / 'pixels.json').write_text(json.dumps(summary, ensure_ascii=False, indent=2), encoding='utf-8')
print(json.dumps([{'name': r['name'], 'pixels': r['comparedPixels'], 'different': r['differentPixels'], 'max': r['maxChannelDifference']} for r in results]))