1616#include < QViewerCmn.h>
1717#include < QImageStr.h>
1818
19+ #include < gdiplus.h>
20+
1921// CComparerView
2022
2123CComparerView::CComparerView ()
@@ -27,6 +29,7 @@ CComparerView::CComparerView()
2729, mHCanvas(0 )
2830, mIsClicked(false )
2931, mProcessing(false )
32+ , mShowHelp(false )
3033, mRgbBufSize(0 )
3134, mRgbBuf(NULL )
3235{
@@ -242,11 +245,161 @@ void CComparerView::OnDraw(CDC *pDC)
242245 }
243246 }
244247
248+ DrawDiffOverlay (&memDC, pDoc, pane);
249+
250+ if (mShowHelp )
251+ DrawHelpMenu (&memDC);
252+
245253 pDC->BitBlt (0 , mRcControls .bottom , mWCanvas , mHCanvas , &memDC, 0 , 0 , SRCCOPY);
246254
247255 mProcessing = false ;
248256}
249257
258+ void CComparerView::ToggleHelp ()
259+ {
260+ mShowHelp = !mShowHelp ;
261+ Invalidate (FALSE );
262+ }
263+
264+ void CComparerView::DrawHelpMenu (CDC *pDC)
265+ {
266+ const int W_HELP = 460 ;
267+ const int H_HELP = 280 ;
268+ const int W_MARGIN = 18 ;
269+ const int H_MARGIN = 14 ;
270+ const int X_HELP = (mWCanvas - W_HELP) / 2 ;
271+ const int Y_HELP = (mHCanvas - H_HELP) / 2 ;
272+ CRect bgRect (X_HELP, Y_HELP, X_HELP + W_HELP, Y_HELP + H_HELP);
273+ pDC->FillSolidRect (bgRect, Q1UI_COLOR_SURFACE);
274+ CPen borderPen (PS_SOLID, 1 , Q1UI_COLOR_BORDER);
275+ CPen *prevPen = pDC->SelectObject (&borderPen);
276+ pDC->SelectStockObject (NULL_BRUSH);
277+ pDC->Rectangle (bgRect);
278+ pDC->SelectObject (prevPen);
279+
280+ CRect manualRect (bgRect.left + W_MARGIN, bgRect.top + H_MARGIN,
281+ bgRect.right - W_MARGIN, bgRect.bottom - H_MARGIN);
282+ LOGFONT lf;
283+ CFont manualFont;
284+ mDefPixelTextFont .GetLogFont (&lf);
285+ lf.lfHeight = 14 ;
286+ lf.lfWeight = FW_NORMAL;
287+ manualFont.CreateFontIndirect (&lf);
288+ pDC->SetBkMode (TRANSPARENT);
289+ CFont *prevFont = pDC->SelectObject (&manualFont);
290+ pDC->SetTextColor (Q1UI_COLOR_TEXT);
291+ CString manual (
292+ " Comparer shortcuts\n "
293+ " \n "
294+ " ? Show or hide this panel\n "
295+ " Drag && Drop Open a source into a pane\n "
296+ " Mouse Wheel Zoom in or out; high zoom shows pixel values\n "
297+ " Left/Right Previous or next video frame\n "
298+ " Space Play or pause\n "
299+ " H Toggle hex pixel values\n "
300+ " I Interpolate pixels\n "
301+ " D Toggle pink diff overlay (grid + dots)\n "
302+ " Click timeline Pick a video frame (left/right pane)\n "
303+ );
304+ pDC->DrawText (manual, &manualRect, DT_LEFT | DT_TOP);
305+ pDC->SelectObject (prevFont);
306+ }
307+
308+ // Pilot: draw a semi-transparent pink cell outline + center dot in every
309+ // grid cell that contains at least one pixel that differs from the reference
310+ // pane. Cell size is fixed in display pixels, so zooming in implicitly
311+ // subdivides the source region each cell covers — at maximum zoom each dot
312+ // resolves to a single differing source pixel.
313+ void CComparerView::DrawDiffOverlay (CDC *pDC, CComparerDoc *pDoc, ComparerPane *pane)
314+ {
315+ if (!pDoc->mDiffOverlay )
316+ return ;
317+ // At high zoom the per-pixel value labels already convey the diff
318+ // information; the grid and dots would just clutter the view.
319+ if (pDoc->mN > ZOOM_TEXT_START)
320+ return ;
321+ if (!pane || !pane->isAvail () || !pane->rgbBuf )
322+ return ;
323+
324+ // Find any other available pane to compare against (pilot assumes 2 panes).
325+ ComparerPane *other = nullptr ;
326+ for (int i = 0 ; i < CComparerDoc::IMG_VIEW_MAX; i++) {
327+ ComparerPane *p = &pDoc->mPane [i];
328+ if (p == pane)
329+ continue ;
330+ if (p->isAvail () && p->rgbBuf ) {
331+ other = p;
332+ break ;
333+ }
334+ }
335+ if (!other || pDoc->mW <= 0 || pDoc->mH <= 0 )
336+ return ;
337+
338+ const int rowBytes = ROUNDUP_DWORD (pDoc->mW ) * QIMG_DST_RGB_BYTES;
339+ const BYTE *bufA = pane->rgbBuf ;
340+ const BYTE *bufB = other->rgbBuf ;
341+
342+ const int cellPx = 48 ; // grid cell size in display pixels
343+ const int dotR = 3 ; // center dot radius
344+
345+ Gdiplus::Graphics g (pDC->m_hDC );
346+ g.SetSmoothingMode (Gdiplus::SmoothingModeAntiAlias);
347+ g.SetCompositingMode (Gdiplus::CompositingModeSourceOver);
348+
349+ const Gdiplus::Color cellColor (200 , 0xff , 0x3d , 0x8a ); // ~78% alpha pink
350+ const Gdiplus::Color dotColor (235 , 0xff , 0x3d , 0x8a ); // ~92% alpha pink
351+ Gdiplus::Pen cellPen (cellColor, 1 .0f );
352+ cellPen.SetAlignment (Gdiplus::PenAlignmentInset);
353+ Gdiplus::SolidBrush dotBrush (dotColor);
354+
355+ const float invN = (pDoc->mN > 0 .0f ) ? (1 .0f / pDoc->mN ) : 1 .0f ;
356+
357+ for (int cy = 0 ; cy < mHCanvas ; cy += cellPx) {
358+ int cellBot = cy + cellPx;
359+ if (cellBot > mHCanvas ) cellBot = mHCanvas ;
360+
361+ int sy0 = (int )floorf ((cy - mYDst ) * invN);
362+ int sy1 = (int )ceilf ((cellBot - mYDst ) * invN);
363+ if (sy0 < 0 ) sy0 = 0 ;
364+ if (sy1 > pDoc->mH ) sy1 = pDoc->mH ;
365+ if (sy0 >= sy1) continue ;
366+
367+ for (int cx = 0 ; cx < mWCanvas ; cx += cellPx) {
368+ int cellRight = cx + cellPx;
369+ if (cellRight > mWCanvas ) cellRight = mWCanvas ;
370+
371+ int sx0 = (int )floorf ((cx - mXDst ) * invN);
372+ int sx1 = (int )ceilf ((cellRight - mXDst ) * invN);
373+ if (sx0 < 0 ) sx0 = 0 ;
374+ if (sx1 > pDoc->mW ) sx1 = pDoc->mW ;
375+ if (sx0 >= sx1) continue ;
376+
377+ bool hasDiff = false ;
378+ const int byteStart = sx0 * QIMG_DST_RGB_BYTES;
379+ const int byteLen = (sx1 - sx0) * QIMG_DST_RGB_BYTES;
380+ for (int sy = sy0; sy < sy1; sy++) {
381+ const BYTE *ra = bufA + sy * rowBytes + byteStart;
382+ const BYTE *rb = bufB + sy * rowBytes + byteStart;
383+ if (memcmp (ra, rb, byteLen) != 0 ) {
384+ hasDiff = true ;
385+ break ;
386+ }
387+ }
388+
389+ if (hasDiff) {
390+ g.DrawRectangle (&cellPen,
391+ (float )cx, (float )cy,
392+ (float )(cellRight - cx - 1 ), (float )(cellBot - cy - 1 ));
393+
394+ float dotX = (cx + cellRight) * 0 .5f ;
395+ float dotY = (cy + cellBot) * 0 .5f ;
396+ g.FillEllipse (&dotBrush,
397+ dotX - dotR, dotY - dotR, 2 .0f * dotR, 2 .0f * dotR);
398+ }
399+ }
400+ }
401+ }
402+
250403void CComparerView::DrawEmptyPane (CDC *pDC, CComparerDoc *pDoc)
251404{
252405 CRect canvas (0 , mRcControls .bottom , mWClient , mHClient );
@@ -678,6 +831,12 @@ void CComparerView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
678831 pDoc->mInterpol = !pDoc->mInterpol ;
679832 Invalidate (FALSE );
680833 break ;
834+ case ' D' :
835+ pDoc->mDiffOverlay = !pDoc->mDiffOverlay ;
836+ break ;
837+ case VK_OEM_2: // '?' / '/' on US keyboards.
838+ ToggleHelp ();
839+ break ;
681840 }
682841
683842 // Most shortcuts affect shared document/view state, so refresh once at the end.
0 commit comments