diff --git a/src/WinIMergeLib/ImgDiffBuffer.hpp b/src/WinIMergeLib/ImgDiffBuffer.hpp index a32d93c..4fcf760 100644 --- a/src/WinIMergeLib/ImgDiffBuffer.hpp +++ b/src/WinIMergeLib/ImgDiffBuffer.hpp @@ -502,6 +502,7 @@ class CImgDiffBuffer , m_overlayAlpha(0.3) , m_wipeMode(WIPE_NONE) , m_wipePosition(0) + , m_wipePosition_old(INT_MAX) , m_diffBlockSize(8) , m_selDiffColor(Image::Rgb(0xff, 0x40, 0x40)) , m_selDiffDeletedColor(Image::Rgb(0xf0, 0xc0, 0xc0)) @@ -522,6 +523,10 @@ class CImgDiffBuffer { for (int i = 0; i < 3; ++i) m_currentPage[i] = 0; + for (int i = 0; i < m_nImages; ++i) + { + m_imgDiffIsTransparent[i] = false; + } } virtual ~CImgDiffBuffer() @@ -541,6 +546,19 @@ class CImgDiffBuffer return m_nImages; } + bool IsAnyPaneTransparent() const + { + for (int i = 0; i < m_nImages; ++i) + if (m_imgDiffIsTransparent[i]) + return true; + return false; + } + + bool IsPaneTransparent(int pane) const + { + return (pane >= 0 && pane < m_nImages) ? m_imgDiffIsTransparent[pane] : false; + } + Image::Color GetPixelColor(int pane, int x, int y) const { return m_imgPreprocessed[pane].pixel(x - m_offset[pane].x, y - m_offset[pane].y); @@ -768,6 +786,13 @@ class CImgDiffBuffer return; m_wipeMode = wipeMode; RefreshImages(); + for (int i = 0; i < m_nImages; ++i) + { + if (m_imgDiff[i].getFipImage()->isTransparent()) + m_imgDiffIsTransparent[i] = true; + else + m_imgDiffIsTransparent[i] = false; + } } int GetWipePosition() const @@ -780,9 +805,29 @@ class CImgDiffBuffer if (m_wipePosition == pos) return; m_wipePosition = pos; - RefreshImages(); + bool anyTransparent = IsAnyPaneTransparent(); + for (int i = 0; i < m_nImages; ++i) + { + if (m_imgDiffIsTransparent[i]) + { + // Dummy operation with less performance to retrigger redrawing + // setPixelColor leads to _bHasChanged = TRUE which will enable redrawing + RGBQUAD color; + m_imgDiff[i].getFipImage()->getPixelColor(0, 0, &color); + m_imgDiff[i].getFipImage()->setPixelColor(0, 0, &color); + } + else if (anyTransparent) + { + // In mixed transparency scenarios (at least one transparent pane), + // non-transparent images must also be marked as modified so FreeImage + // rebuilds their internal DIB cache. + m_imgDiff[i].getFipImage()->setModified(true); + } + } + WipeEffect(); } + void SetWipeModePosition(WIPE_MODE wipeMode, int pos) { if (m_wipeMode == wipeMode && m_wipePosition == pos) @@ -790,6 +835,15 @@ class CImgDiffBuffer m_wipeMode = wipeMode; m_wipePosition = pos; RefreshImages(); + for (int i = 0; i < m_nImages; ++i) + { + if (m_imgDiff[i].getFipImage()->isTransparent()) + m_imgDiffIsTransparent[i] = true; + else + m_imgDiffIsTransparent[i] = false; + } + if (m_wipeMode != WIPE_NONE) + WipeEffect(); } bool GetShowDifferences() const @@ -1137,10 +1191,6 @@ class CImgDiffBuffer (this->*func)(1, 2); } } - if (m_wipeMode != WIPE_NONE) - { - WipeEffect(); - } if (m_showDifferences) { bool showDiff = true; @@ -1160,6 +1210,7 @@ class CImgDiffBuffer MarkDiff(i, m_diff); } } + m_wipePosition_old = INT_MAX; } bool OpenImages(int nImages, const wchar_t * const filename[3]) @@ -1898,49 +1949,90 @@ class CImgDiffBuffer const unsigned w = m_imgDiff[0].width(); const unsigned h = m_imgDiff[0].height(); + if (m_wipePosition <= 0) + m_wipePosition = 0; + if (m_wipeMode == WIPE_VERTICAL) { - auto tmp = new unsigned char[w * 4]; - for (unsigned y = m_wipePosition; y < h; ++y) + if (m_wipePosition >= h) + m_wipePosition = h; + if (m_wipePosition_old == INT_MAX) + m_wipePosition_old = h; + const size_t lineBytes = w * 4; + auto tmp = new unsigned char[lineBytes]; + if (m_wipePosition <= m_wipePosition_old) { - for (int pane = 0; pane < m_nImages - 1; ++pane) + for (unsigned y = m_wipePosition; y < m_wipePosition_old; ++y) + { + for (int pane = 0; pane < m_nImages - 1; ++pane) + { + unsigned char* scanline = m_imgDiff[pane].scanLine(y); + unsigned char* scanline2 = m_imgDiff[pane + 1].scanLine(y); + memcpy(tmp, scanline, lineBytes); + memcpy(scanline, scanline2, lineBytes); + memcpy(scanline2, tmp, lineBytes); + } + } + } + else + { + for (unsigned y = m_wipePosition_old; y < m_wipePosition; ++y) { - unsigned char *scanline = m_imgDiff[pane].scanLine(y); - unsigned char *scanline2 = m_imgDiff[pane + 1].scanLine(y); - memcpy(tmp, scanline, w * 4); - memcpy(scanline, scanline2, w * 4); - memcpy(scanline2, tmp, w * 4); + for (int pane = m_nImages - 1; pane > 0; --pane) + { + unsigned char* scanline = m_imgDiff[pane].scanLine(y); + unsigned char* scanline2 = m_imgDiff[pane - 1].scanLine(y); + memcpy(tmp, scanline, lineBytes); + memcpy(scanline, scanline2, lineBytes); + memcpy(scanline2, tmp, lineBytes); + } } } + delete[] tmp; } - else if (m_wipeMode = WIPE_HORIZONTAL) + else if (m_wipeMode == WIPE_HORIZONTAL) { + if (m_wipePosition >= w) + m_wipePosition = w; + if (m_wipePosition_old == INT_MAX) + m_wipePosition_old = w; + + const size_t pixelBytes = 4; + unsigned char tmp[4]; + for (unsigned y = 0; y < h; ++y) { for (int pane = 0; pane < m_nImages - 1; ++pane) { - unsigned char *scanline = m_imgDiff[pane].scanLine(y); - unsigned char *scanline2 = m_imgDiff[pane + 1].scanLine(y); - for (unsigned x = m_wipePosition; x < w; ++x) + unsigned char* scanline; + unsigned char* scanline2; + if (m_wipePosition <= m_wipePosition_old) { - unsigned char tmp[4]; - tmp[0] = scanline[x * 4 + 0]; - tmp[1] = scanline[x * 4 + 1]; - tmp[2] = scanline[x * 4 + 2]; - tmp[3] = scanline[x * 4 + 3]; - scanline[x * 4 + 0] = scanline2[x * 4 + 0]; - scanline[x * 4 + 1] = scanline2[x * 4 + 1]; - scanline[x * 4 + 2] = scanline2[x * 4 + 2]; - scanline[x * 4 + 3] = scanline2[x * 4 + 3]; - scanline2[x * 4 + 0] = tmp[0]; - scanline2[x * 4 + 1] = tmp[1]; - scanline2[x * 4 + 2] = tmp[2]; - scanline2[x * 4 + 3] = tmp[3]; + scanline = m_imgDiff[pane].scanLine(y); + scanline2 = m_imgDiff[pane + 1].scanLine(y); + for (unsigned x = m_wipePosition; x < m_wipePosition_old; ++x) + { + memcpy(tmp, scanline + x * pixelBytes, pixelBytes); + memcpy(scanline + x * pixelBytes, scanline2 + x * pixelBytes, pixelBytes); + memcpy(scanline2 + x * pixelBytes, tmp, pixelBytes); + } + } + else + { + scanline = m_imgDiff[m_nImages - 2 - pane].scanLine(y); + scanline2 = m_imgDiff[m_nImages - 1 - pane].scanLine(y); + for (unsigned x = m_wipePosition_old; x < m_wipePosition; ++x) + { + memcpy(tmp, scanline + x * pixelBytes, pixelBytes); + memcpy(scanline + x * pixelBytes, scanline2 + x * pixelBytes, pixelBytes); + memcpy(scanline2 + x * pixelBytes, tmp, pixelBytes); + } } } } } + m_wipePosition_old = m_wipePosition; } void CopyPreprocessedImageToDiffImage(int dst) @@ -2277,6 +2369,7 @@ class CImgDiffBuffer double m_overlayAlpha; WIPE_MODE m_wipeMode; int m_wipePosition; + int m_wipePosition_old; unsigned m_diffBlockSize; Image::Color m_selDiffColor; Image::Color m_selDiffDeletedColor; @@ -2298,4 +2391,5 @@ class CImgDiffBuffer int m_blinkInterval; int m_overlayAnimationInterval; int m_lastErrorCode; + bool m_imgDiffIsTransparent[3]; }; diff --git a/src/WinIMergeLib/ImgMergeWindow.hpp b/src/WinIMergeLib/ImgMergeWindow.hpp index 54876bf..a15103a 100644 --- a/src/WinIMergeLib/ImgMergeWindow.hpp +++ b/src/WinIMergeLib/ImgMergeWindow.hpp @@ -1815,15 +1815,35 @@ class CImgMergeWindow : public IImgMergeWindow } else if (m_draggingModeCurrent == DRAGGING_MODE::VERTICAL_WIPE) { + int oldWipePos = m_buffer.GetWipePosition(); imgWindow.SetRectangleSelection(0, pt.y, m_buffer.GetImageWidth(evt.pane), pt.y); m_buffer.SetWipePosition(pt.y); - Invalidate(); + if (m_buffer.GetWipePosition() != oldWipePos) + { + bool anyTransparent = m_buffer.IsAnyPaneTransparent(); + for (int i = 0; i < m_nImages; ++i) + { + if (m_buffer.IsPaneTransparent(i) || (anyTransparent && m_nImages >= 3)) + RedrawWindow(m_imgWindow[i].GetHWND(), NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOERASE); + } + Invalidate(); + } } else if (m_draggingModeCurrent == DRAGGING_MODE::HORIZONTAL_WIPE) { + int oldWipePos = m_buffer.GetWipePosition(); imgWindow.SetRectangleSelection(pt.x, 0, pt.x, m_buffer.GetImageHeight(evt.pane)); m_buffer.SetWipePosition(pt.x); - Invalidate(); + if (m_buffer.GetWipePosition() != oldWipePos) + { + bool anyTransparent = m_buffer.IsAnyPaneTransparent(); + for (int i = 0; i < m_nImages; ++i) + { + if (m_buffer.IsPaneTransparent(i) || (anyTransparent && m_nImages >= 3)) + RedrawWindow(m_imgWindow[i].GetHWND(), NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOERASE); + } + Invalidate(); + } } else if (m_draggingModeCurrent == DRAGGING_MODE::RECTANGLE_SELECT) {