|
16 | 16 | #include <QViewerCmn.h> |
17 | 17 | #include <QImageStr.h> |
18 | 18 |
|
| 19 | +#include <gdiplus.h> |
| 20 | + |
19 | 21 | // CComparerView |
20 | 22 |
|
21 | 23 | CComparerView::CComparerView() |
@@ -242,11 +244,104 @@ void CComparerView::OnDraw(CDC *pDC) |
242 | 244 | } |
243 | 245 | } |
244 | 246 |
|
| 247 | + DrawDiffOverlay(&memDC, pDoc, pane); |
| 248 | + |
245 | 249 | pDC->BitBlt(0, mRcControls.bottom, mWCanvas, mHCanvas, &memDC, 0, 0, SRCCOPY); |
246 | 250 |
|
247 | 251 | mProcessing = false; |
248 | 252 | } |
249 | 253 |
|
| 254 | +// Pilot: draw a semi-transparent pink cell outline + center dot in every |
| 255 | +// grid cell that contains at least one pixel that differs from the reference |
| 256 | +// pane. Cell size is fixed in display pixels, so zooming in implicitly |
| 257 | +// subdivides the source region each cell covers — at maximum zoom each dot |
| 258 | +// resolves to a single differing source pixel. |
| 259 | +void CComparerView::DrawDiffOverlay(CDC *pDC, CComparerDoc *pDoc, ComparerPane *pane) |
| 260 | +{ |
| 261 | + if (!pDoc->mDiffOverlay) |
| 262 | + return; |
| 263 | + if (!pane || !pane->isAvail() || !pane->rgbBuf) |
| 264 | + return; |
| 265 | + |
| 266 | + // Find any other available pane to compare against (pilot assumes 2 panes). |
| 267 | + ComparerPane *other = nullptr; |
| 268 | + for (int i = 0; i < CComparerDoc::IMG_VIEW_MAX; i++) { |
| 269 | + ComparerPane *p = &pDoc->mPane[i]; |
| 270 | + if (p == pane) |
| 271 | + continue; |
| 272 | + if (p->isAvail() && p->rgbBuf) { |
| 273 | + other = p; |
| 274 | + break; |
| 275 | + } |
| 276 | + } |
| 277 | + if (!other || pDoc->mW <= 0 || pDoc->mH <= 0) |
| 278 | + return; |
| 279 | + |
| 280 | + const int rowBytes = ROUNDUP_DWORD(pDoc->mW) * QIMG_DST_RGB_BYTES; |
| 281 | + const BYTE *bufA = pane->rgbBuf; |
| 282 | + const BYTE *bufB = other->rgbBuf; |
| 283 | + |
| 284 | + const int cellPx = 48; // grid cell size in display pixels |
| 285 | + const int dotR = 3; // center dot radius |
| 286 | + |
| 287 | + Gdiplus::Graphics g(pDC->m_hDC); |
| 288 | + g.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias); |
| 289 | + g.SetCompositingMode(Gdiplus::CompositingModeSourceOver); |
| 290 | + |
| 291 | + const Gdiplus::Color cellColor(200, 0xff, 0x3d, 0x8a); // ~78% alpha pink |
| 292 | + const Gdiplus::Color dotColor (235, 0xff, 0x3d, 0x8a); // ~92% alpha pink |
| 293 | + Gdiplus::Pen cellPen(cellColor, 1.0f); |
| 294 | + cellPen.SetAlignment(Gdiplus::PenAlignmentInset); |
| 295 | + Gdiplus::SolidBrush dotBrush(dotColor); |
| 296 | + |
| 297 | + const float invN = (pDoc->mN > 0.0f) ? (1.0f / pDoc->mN) : 1.0f; |
| 298 | + |
| 299 | + for (int cy = 0; cy < mHCanvas; cy += cellPx) { |
| 300 | + int cellBot = cy + cellPx; |
| 301 | + if (cellBot > mHCanvas) cellBot = mHCanvas; |
| 302 | + |
| 303 | + int sy0 = (int)floorf((cy - mYDst) * invN); |
| 304 | + int sy1 = (int)ceilf ((cellBot - mYDst) * invN); |
| 305 | + if (sy0 < 0) sy0 = 0; |
| 306 | + if (sy1 > pDoc->mH) sy1 = pDoc->mH; |
| 307 | + if (sy0 >= sy1) continue; |
| 308 | + |
| 309 | + for (int cx = 0; cx < mWCanvas; cx += cellPx) { |
| 310 | + int cellRight = cx + cellPx; |
| 311 | + if (cellRight > mWCanvas) cellRight = mWCanvas; |
| 312 | + |
| 313 | + int sx0 = (int)floorf((cx - mXDst) * invN); |
| 314 | + int sx1 = (int)ceilf ((cellRight - mXDst) * invN); |
| 315 | + if (sx0 < 0) sx0 = 0; |
| 316 | + if (sx1 > pDoc->mW) sx1 = pDoc->mW; |
| 317 | + if (sx0 >= sx1) continue; |
| 318 | + |
| 319 | + bool hasDiff = false; |
| 320 | + const int byteStart = sx0 * QIMG_DST_RGB_BYTES; |
| 321 | + const int byteLen = (sx1 - sx0) * QIMG_DST_RGB_BYTES; |
| 322 | + for (int sy = sy0; sy < sy1; sy++) { |
| 323 | + const BYTE *ra = bufA + sy * rowBytes + byteStart; |
| 324 | + const BYTE *rb = bufB + sy * rowBytes + byteStart; |
| 325 | + if (memcmp(ra, rb, byteLen) != 0) { |
| 326 | + hasDiff = true; |
| 327 | + break; |
| 328 | + } |
| 329 | + } |
| 330 | + |
| 331 | + if (hasDiff) { |
| 332 | + g.DrawRectangle(&cellPen, |
| 333 | + (float)cx, (float)cy, |
| 334 | + (float)(cellRight - cx - 1), (float)(cellBot - cy - 1)); |
| 335 | + |
| 336 | + float dotX = (cx + cellRight) * 0.5f; |
| 337 | + float dotY = (cy + cellBot) * 0.5f; |
| 338 | + g.FillEllipse(&dotBrush, |
| 339 | + dotX - dotR, dotY - dotR, 2.0f * dotR, 2.0f * dotR); |
| 340 | + } |
| 341 | + } |
| 342 | + } |
| 343 | +} |
| 344 | + |
250 | 345 | void CComparerView::DrawEmptyPane(CDC *pDC, CComparerDoc *pDoc) |
251 | 346 | { |
252 | 347 | CRect canvas(0, mRcControls.bottom, mWClient, mHClient); |
@@ -678,6 +773,9 @@ void CComparerView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) |
678 | 773 | pDoc->mInterpol = !pDoc->mInterpol; |
679 | 774 | Invalidate(FALSE); |
680 | 775 | break; |
| 776 | + case 'D': |
| 777 | + pDoc->mDiffOverlay = !pDoc->mDiffOverlay; |
| 778 | + break; |
681 | 779 | } |
682 | 780 |
|
683 | 781 | // Most shortcuts affect shared document/view state, so refresh once at the end. |
|
0 commit comments