Skip to content

Commit 0bf11eb

Browse files
committed
Comparer: pilot pink-dot grid overlay for pixel-level diffs
The image area is conceptually divided into a fixed-size grid in display pixels. Each cell that contains at least one differing pixel between the two source panes gets a small pink dot overlay. Because the cell size is fixed in display pixels, zooming in implicitly subdivides the source region per cell — at maximum zoom the dot resolves to a single differing source pixel. Per-row red diff lines in PosInfoView are removed, and the left timeline is hidden when neither pane is a video; selecting frames is still available for video sources.
1 parent 82ef8e0 commit 0bf11eb

3 files changed

Lines changed: 101 additions & 28 deletions

File tree

Comparer/ComparerView.cpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,97 @@ void CComparerView::OnDraw(CDC *pDC)
242242
}
243243
}
244244

245+
DrawDiffOverlay(&memDC, pDoc, pane);
246+
245247
pDC->BitBlt(0, mRcControls.bottom, mWCanvas, mHCanvas, &memDC, 0, 0, SRCCOPY);
246248

247249
mProcessing = false;
248250
}
249251

252+
// Pilot: draw a pink dot in every grid cell that contains at least one pixel
253+
// that differs from the reference pane. Cell size is fixed in display pixels,
254+
// so zooming in implicitly subdivides the source region each cell covers —
255+
// at maximum zoom the dot resolves to an individual differing pixel.
256+
void CComparerView::DrawDiffOverlay(CDC *pDC, CComparerDoc *pDoc, ComparerPane *pane)
257+
{
258+
if (!pane || !pane->isAvail() || !pane->rgbBuf)
259+
return;
260+
261+
// Find any other available pane to compare against (pilot assumes 2 panes).
262+
ComparerPane *other = nullptr;
263+
for (int i = 0; i < CComparerDoc::IMG_VIEW_MAX; i++) {
264+
ComparerPane *p = &pDoc->mPane[i];
265+
if (p == pane)
266+
continue;
267+
if (p->isAvail() && p->rgbBuf) {
268+
other = p;
269+
break;
270+
}
271+
}
272+
if (!other || pDoc->mW <= 0 || pDoc->mH <= 0)
273+
return;
274+
275+
const int rowBytes = ROUNDUP_DWORD(pDoc->mW) * QIMG_DST_RGB_BYTES;
276+
const BYTE *bufA = pane->rgbBuf;
277+
const BYTE *bufB = other->rgbBuf;
278+
279+
const int cellPx = 24; // grid cell size in display pixels
280+
const int dotR = 3; // dot radius
281+
const COLORREF kPink = RGB(0xff, 0x3d, 0x8a);
282+
283+
CBrush dotBrush(kPink);
284+
CBrush *prevBrush = pDC->SelectObject(&dotBrush);
285+
HGDIOBJ prevPen = pDC->SelectObject(::GetStockObject(NULL_PEN));
286+
287+
const float invN = (pDoc->mN > 0.0f) ? (1.0f / pDoc->mN) : 1.0f;
288+
289+
for (int cy = 0; cy < mHCanvas; cy += cellPx) {
290+
int cellBot = cy + cellPx;
291+
if (cellBot > mHCanvas) cellBot = mHCanvas;
292+
293+
// Map canvas y range -> source y range.
294+
int sy0 = (int)floorf((cy - mYDst) * invN);
295+
int sy1 = (int)ceilf ((cellBot - mYDst) * invN);
296+
if (sy0 < 0) sy0 = 0;
297+
if (sy1 > pDoc->mH) sy1 = pDoc->mH;
298+
if (sy0 >= sy1) continue;
299+
300+
for (int cx = 0; cx < mWCanvas; cx += cellPx) {
301+
int cellRight = cx + cellPx;
302+
if (cellRight > mWCanvas) cellRight = mWCanvas;
303+
304+
int sx0 = (int)floorf((cx - mXDst) * invN);
305+
int sx1 = (int)ceilf ((cellRight - mXDst) * invN);
306+
if (sx0 < 0) sx0 = 0;
307+
if (sx1 > pDoc->mW) sx1 = pDoc->mW;
308+
if (sx0 >= sx1) continue;
309+
310+
// Scan the source rectangle for any differing byte; early exit.
311+
bool hasDiff = false;
312+
const int byteStart = sx0 * QIMG_DST_RGB_BYTES;
313+
const int byteLen = (sx1 - sx0) * QIMG_DST_RGB_BYTES;
314+
for (int sy = sy0; sy < sy1; sy++) {
315+
const BYTE *ra = bufA + sy * rowBytes + byteStart;
316+
const BYTE *rb = bufB + sy * rowBytes + byteStart;
317+
if (memcmp(ra, rb, byteLen) != 0) {
318+
hasDiff = true;
319+
break;
320+
}
321+
}
322+
323+
if (hasDiff) {
324+
int dotX = (cx + cellRight) / 2;
325+
int dotY = (cy + cellBot) / 2;
326+
pDC->Ellipse(dotX - dotR, dotY - dotR,
327+
dotX + dotR + 1, dotY + dotR + 1);
328+
}
329+
}
330+
}
331+
332+
pDC->SelectObject(prevBrush);
333+
pDC->SelectObject(prevPen);
334+
}
335+
250336
void CComparerView::DrawEmptyPane(CDC *pDC, CComparerDoc *pDoc)
251337
{
252338
CRect canvas(0, mRcControls.bottom, mWClient, mHClient);

Comparer/ComparerView.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class CComparerView : public CScrollView
5757
void ScaleNearestNeighbor(CComparerDoc *pDoc, BYTE *src, BYTE *dst, int sDst,
5858
q1::GridInfo &gi);
5959
void DrawEmptyPane(CDC *pDC, CComparerDoc *pDoc);
60+
void DrawDiffOverlay(CDC *pDC, CComparerDoc *pDoc, ComparerPane *pane);
6061

6162
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
6263
afx_msg void OnDropFiles(HDROP hDropInfo);

Comparer/PosInfoView.cpp

Lines changed: 14 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -59,21 +59,6 @@ void CPosInfoView::OnInitialUpdate()
5959
CScrollView::OnInitialUpdate();
6060
}
6161

62-
static void DrawDiffPosLines(CDC *pDC, CRect *frameRect, bool *flags, int n)
63-
{
64-
int top = frameRect->top + 1;
65-
int left = frameRect->left + 1;
66-
int right = frameRect->right - 1;
67-
68-
for (int i = 0; i < n; i++) {
69-
if (flags[i] == false)
70-
continue;
71-
72-
pDC->MoveTo(left, top + i);
73-
pDC->LineTo(right, top + i);
74-
}
75-
}
76-
7762
void CPosInfoView::DrawEachRect(CDC* pDC,
7863
ComparerPane *pane,
7964
CRect *frameRect,
@@ -98,17 +83,9 @@ void CPosInfoView::DrawEachRect(CDC* pDC,
9883
pDC->FillSolidRect(frameRect, frameColor);
9984
pDC->Draw3dRect(frameRect, Q1UI_COLOR_SURFACE, Q1UI_COLOR_SURFACE);
10085

101-
if (parseDone) {
102-
CPen *prev = pDC->SelectObject(mDiffPen);
103-
IFrmCmpStrategy *frmCmpStrategy = pDoc->mFrmCmpStrategy;
104-
list<RLC> diffRLC[QPLANES];
105-
106-
if (mFileScanThread->copyDiffRLC(i, diffRLC)) {
107-
frmCmpStrategy->FlagTotalDiffLine(diffRLC, mDiffFlags, mPosLinesPerFrame);
108-
DrawDiffPosLines(pDC, frameRect, mDiffFlags, mPosLinesPerFrame);
109-
}
110-
pDC->SelectObject(prev);
111-
}
86+
// Per-row red diff lines have been replaced by the pink grid overlay in
87+
// ComparerView. Pilot keeps the frame tiles for video-frame selection but
88+
// no longer draws the per-row indicators.
11289

11390
const COLORREF curIdColor = Q1UI_COLOR_ACCENT;
11491
COLORREF preColor;
@@ -183,7 +160,13 @@ void CPosInfoView::OnDraw(CDC* pDC)
183160

184161
memDC.FillSolidRect(CRect(0, 0, mWClient, h), Q1UI_COLOR_APP_BG);
185162

186-
if (!paneL->isAvail() && !paneR->isAvail()) {
163+
// Pilot: hide the timeline entirely when neither pane is a video. The pink
164+
// grid overlay in ComparerView already conveys diff information; the
165+
// timeline is only useful for picking a frame in a video source.
166+
bool hasVideo = (paneL->isAvail() && paneL->frames > 1)
167+
|| (paneR->isAvail() && paneR->frames > 1);
168+
169+
if (!paneL->isAvail() && !paneR->isAvail() || !hasVideo) {
187170
LOGFONT lf;
188171
mPosNumFont.GetLogFont(&lf);
189172
::lstrcpy(lf.lfFaceName, Q1UI_FONT_TEXT);
@@ -195,7 +178,10 @@ void CPosInfoView::OnDraw(CDC* pDC)
195178
memDC.SetBkMode(TRANSPARENT);
196179
memDC.SetTextColor(Q1UI_COLOR_TEXT_MUTED);
197180
CRect msgRect(0, 0, mWClient, h);
198-
memDC.DrawText(_T("Timeline"), &msgRect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
181+
LPCTSTR msg = (paneL->isAvail() || paneR->isAvail())
182+
? _T("Timeline\n(video only)") : _T("Timeline");
183+
memDC.DrawText(msg, &msgRect,
184+
DT_CENTER | DT_VCENTER | DT_WORDBREAK | DT_NOCLIP);
199185
memDC.SelectObject(prevFont);
200186
goto OnDrawExit;
201187
}

0 commit comments

Comments
 (0)