From 5c25eefe2690412e61e07627b62cf0546a51cd59 Mon Sep 17 00:00:00 2001 From: Edi61 <84682915+Edi61@users.noreply.github.com> Date: Tue, 9 Apr 2024 22:20:11 +0200 Subject: [PATCH 1/8] Speedup Wipeffect - WipeEffect only if position changed - Difference based wipeeffect (Only part between old and new position is updated) --- src/WinIMergeLib/ImgDiffBuffer.hpp | 118 ++++++++++++++++++++++------- 1 file changed, 90 insertions(+), 28 deletions(-) diff --git a/src/WinIMergeLib/ImgDiffBuffer.hpp b/src/WinIMergeLib/ImgDiffBuffer.hpp index 0f04b9e..3f4c582 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)) @@ -777,7 +778,7 @@ class CImgDiffBuffer if (m_wipePosition == pos) return; m_wipePosition = pos; - RefreshImages(); + WipeEffect(); } void SetWipeModePosition(WIPE_MODE wipeMode, int pos) @@ -1134,10 +1135,6 @@ class CImgDiffBuffer (this->*func)(1, 2); } } - if (m_wipeMode != WIPE_NONE) - { - WipeEffect(); - } if (m_showDifferences) { bool showDiff = true; @@ -1157,6 +1154,7 @@ class CImgDiffBuffer MarkDiff(i, m_diff); } } + m_wipePosition_old = INT_MAX; } bool OpenImages(int nImages, const wchar_t * const filename[3]) @@ -1878,49 +1876,112 @@ 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) { + if (m_wipePosition >= h) + m_wipePosition = h; + if (m_wipePosition_old == INT_MAX) + m_wipePosition_old = h; auto tmp = new unsigned char[w * 4]; - for (unsigned y = m_wipePosition; y < h; ++y) + 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, w * 4); + memcpy(scanline, scanline2, w * 4); + memcpy(scanline2, tmp, w * 4); + } + } + } + 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, w * 4); + memcpy(scanline, scanline2, w * 4); + memcpy(scanline2, tmp, w * 4); + } } } + delete[] tmp; } else if (m_wipeMode = WIPE_HORIZONTAL) { + if (m_wipePosition >= w) + m_wipePosition = w; + if (m_wipePosition_old == INT_MAX) + m_wipePosition_old = w; 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) + { + scanline = m_imgDiff[pane].scanLine(y); + scanline2 = m_imgDiff[pane + 1].scanLine(y); + } + else + { + scanline = m_imgDiff[m_nImages - 2 - pane].scanLine(y); + scanline2 = m_imgDiff[m_nImages - 1 - pane].scanLine(y); + } + + 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]; + for (unsigned x = m_wipePosition; x < m_wipePosition_old; ++x) + { + 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]; + } + } + else + { + for (unsigned x = m_wipePosition_old; x < m_wipePosition; ++x) + { + 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]; + } } } } } + m_wipePosition_old = m_wipePosition; } void CopyPreprocessedImageToDiffImage(int dst) @@ -2257,6 +2318,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; From b06068e178ae928f90ddea5eaa7411c67da8d17d Mon Sep 17 00:00:00 2001 From: Edi61 <84682915+Edi61@users.noreply.github.com> Date: Sat, 20 Apr 2024 23:43:39 +0200 Subject: [PATCH 2/8] Transparency detection + redraw Transparency detection and triggering of a redraw in case of image wiping --- src/WinIMergeLib/ImgDiffBuffer.hpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/WinIMergeLib/ImgDiffBuffer.hpp b/src/WinIMergeLib/ImgDiffBuffer.hpp index 3f4c582..004eac6 100644 --- a/src/WinIMergeLib/ImgDiffBuffer.hpp +++ b/src/WinIMergeLib/ImgDiffBuffer.hpp @@ -520,6 +520,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() @@ -766,6 +770,11 @@ 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; + } } int GetWipePosition() const @@ -778,9 +787,21 @@ class CImgDiffBuffer if (m_wipePosition == pos) return; m_wipePosition = pos; + for (int i = 0; i < m_nImages; ++i) + { + if (m_imgDiffIsTransparent[i] == true) + { + // 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); + } + } WipeEffect(); } + void SetWipeModePosition(WIPE_MODE wipeMode, int pos) { if (m_wipeMode == wipeMode && m_wipePosition == pos) @@ -788,6 +809,13 @@ 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; + } + if (m_wipeMode != WIPE_NONE) + WipeEffect(); } bool GetShowDifferences() const @@ -2337,4 +2365,5 @@ class CImgDiffBuffer std::vector m_lineDiffInfos; bool m_temporarilyTransformed; DIFF_ALGORITHM m_diffAlgorithm; + bool m_imgDiffIsTransparent[3]; }; From b3b552780a2d25de56cfedef09ced91501ccf82d Mon Sep 17 00:00:00 2001 From: Edi61 <84682915+Edi61@users.noreply.github.com> Date: Fri, 9 Jan 2026 15:04:26 +0300 Subject: [PATCH 3/8] Speed up transparent images and redrawing without gaps --- src/WinIMergeLib/ImgDiffBuffer.hpp | 8 ++++++++ src/WinIMergeLib/ImgMergeWindow.hpp | 18 ++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/WinIMergeLib/ImgDiffBuffer.hpp b/src/WinIMergeLib/ImgDiffBuffer.hpp index 004eac6..27f51a9 100644 --- a/src/WinIMergeLib/ImgDiffBuffer.hpp +++ b/src/WinIMergeLib/ImgDiffBuffer.hpp @@ -543,6 +543,14 @@ class CImgDiffBuffer return m_nImages; } + bool IsAnyPaneTransparent() const + { + for (int i = 0; i < m_nImages; ++i) + if (m_imgDiffIsTransparent[i]) + return true; + return 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); diff --git a/src/WinIMergeLib/ImgMergeWindow.hpp b/src/WinIMergeLib/ImgMergeWindow.hpp index 42caa34..2f65d21 100644 --- a/src/WinIMergeLib/ImgMergeWindow.hpp +++ b/src/WinIMergeLib/ImgMergeWindow.hpp @@ -1747,15 +1747,29 @@ 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(); + // Only redraw if position changed AND pane has transparency + if (m_buffer.GetWipePosition() != oldWipePos && m_buffer.IsAnyPaneTransparent()) { + for (int i = 0; i < m_nImages; ++i) + RedrawWindow(m_imgWindow[i].GetHWND(), NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOERASE); + } else { + 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(); + // Only redraw if position changed AND pane has transparency + if (m_buffer.GetWipePosition() != oldWipePos && m_buffer.IsAnyPaneTransparent()) { + for (int i = 0; i < m_nImages; ++i) + RedrawWindow(m_imgWindow[i].GetHWND(), NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOERASE); + } else { + Invalidate(); + } } else if (m_draggingModeCurrent == DRAGGING_MODE::RECTANGLE_SELECT) { From 346c76e4191faf5e4d346886e37a2eb91128fa25 Mon Sep 17 00:00:00 2001 From: Edi61 <84682915+Edi61@users.noreply.github.com> Date: Fri, 9 Jan 2026 16:44:43 +0300 Subject: [PATCH 4/8] WipeEffect: Some source code optimisations --- src/WinIMergeLib/ImgDiffBuffer.hpp | 51 ++++++++++-------------------- 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/src/WinIMergeLib/ImgDiffBuffer.hpp b/src/WinIMergeLib/ImgDiffBuffer.hpp index ce4288d..5c05a0c 100644 --- a/src/WinIMergeLib/ImgDiffBuffer.hpp +++ b/src/WinIMergeLib/ImgDiffBuffer.hpp @@ -1941,7 +1941,8 @@ class CImgDiffBuffer m_wipePosition = h; if (m_wipePosition_old == INT_MAX) m_wipePosition_old = h; - auto tmp = new unsigned char[w * 4]; + const size_t lineBytes = w * 4; + auto tmp = new unsigned char[lineBytes]; if (m_wipePosition <= m_wipePosition_old) { for (unsigned y = m_wipePosition; y < m_wipePosition_old; ++y) @@ -1950,9 +1951,9 @@ class CImgDiffBuffer { 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); + memcpy(tmp, scanline, lineBytes); + memcpy(scanline, scanline2, lineBytes); + memcpy(scanline2, tmp, lineBytes); } } } @@ -1964,16 +1965,16 @@ class CImgDiffBuffer { 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); + 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; @@ -2000,38 +2001,20 @@ class CImgDiffBuffer { for (unsigned x = m_wipePosition; x < m_wipePosition_old; ++x) { - 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]; + std::swap(scanline[x * 4 + 0], scanline2[x * 4 + 0]); + std::swap(scanline[x * 4 + 1], scanline2[x * 4 + 1]); + std::swap(scanline[x * 4 + 2], scanline2[x * 4 + 2]); + std::swap(scanline[x * 4 + 3], scanline2[x * 4 + 3]); } } else { for (unsigned x = m_wipePosition_old; x < m_wipePosition; ++x) { - 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]; + std::swap(scanline[x * 4 + 0], scanline2[x * 4 + 0]); + std::swap(scanline[x * 4 + 1], scanline2[x * 4 + 1]); + std::swap(scanline[x * 4 + 2], scanline2[x * 4 + 2]); + std::swap(scanline[x * 4 + 3], scanline2[x * 4 + 3]); } } } From 0f1e4a8c3383a635d1626edb93536ea7388a0c55 Mon Sep 17 00:00:00 2001 From: Edi61 <84682915+Edi61@users.noreply.github.com> Date: Fri, 9 Jan 2026 18:52:48 +0300 Subject: [PATCH 5/8] Wipe Redrawing: Some fixes related to transparecy check --- src/WinIMergeLib/ImgDiffBuffer.hpp | 11 ++++++----- src/WinIMergeLib/ImgMergeWindow.hpp | 20 ++++++++++++-------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/WinIMergeLib/ImgDiffBuffer.hpp b/src/WinIMergeLib/ImgDiffBuffer.hpp index 5c05a0c..209c8d4 100644 --- a/src/WinIMergeLib/ImgDiffBuffer.hpp +++ b/src/WinIMergeLib/ImgDiffBuffer.hpp @@ -546,12 +546,9 @@ class CImgDiffBuffer return m_nImages; } - bool IsAnyPaneTransparent() const + bool IsPaneTransparent(int pane) const { - for (int i = 0; i < m_nImages; ++i) - if (m_imgDiffIsTransparent[i]) - return true; - return false; + return (pane >= 0 && pane < m_nImages) ? m_imgDiffIsTransparent[pane] : false; } Image::Color GetPixelColor(int pane, int x, int y) const @@ -785,6 +782,8 @@ class CImgDiffBuffer { if (m_imgDiff[i].getFipImage()->isTransparent()) m_imgDiffIsTransparent[i] = true; + else + m_imgDiffIsTransparent[i] = false; } } @@ -824,6 +823,8 @@ class CImgDiffBuffer { if (m_imgDiff[i].getFipImage()->isTransparent()) m_imgDiffIsTransparent[i] = true; + else + m_imgDiffIsTransparent[i] = false; } if (m_wipeMode != WIPE_NONE) WipeEffect(); diff --git a/src/WinIMergeLib/ImgMergeWindow.hpp b/src/WinIMergeLib/ImgMergeWindow.hpp index 95b034a..7bec076 100644 --- a/src/WinIMergeLib/ImgMergeWindow.hpp +++ b/src/WinIMergeLib/ImgMergeWindow.hpp @@ -1818,11 +1818,13 @@ class CImgMergeWindow : public IImgMergeWindow int oldWipePos = m_buffer.GetWipePosition(); imgWindow.SetRectangleSelection(0, pt.y, m_buffer.GetImageWidth(evt.pane), pt.y); m_buffer.SetWipePosition(pt.y); - // Only redraw if position changed AND pane has transparency - if (m_buffer.GetWipePosition() != oldWipePos && m_buffer.IsAnyPaneTransparent()) { + if (m_buffer.GetWipePosition() != oldWipePos) + { for (int i = 0; i < m_nImages; ++i) - RedrawWindow(m_imgWindow[i].GetHWND(), NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOERASE); - } else { + { + if (m_buffer.IsPaneTransparent(i)) + RedrawWindow(m_imgWindow[i].GetHWND(), NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOERASE); + } Invalidate(); } } @@ -1831,11 +1833,13 @@ class CImgMergeWindow : public IImgMergeWindow int oldWipePos = m_buffer.GetWipePosition(); imgWindow.SetRectangleSelection(pt.x, 0, pt.x, m_buffer.GetImageHeight(evt.pane)); m_buffer.SetWipePosition(pt.x); - // Only redraw if position changed AND pane has transparency - if (m_buffer.GetWipePosition() != oldWipePos && m_buffer.IsAnyPaneTransparent()) { + if (m_buffer.GetWipePosition() != oldWipePos) + { for (int i = 0; i < m_nImages; ++i) - RedrawWindow(m_imgWindow[i].GetHWND(), NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOERASE); - } else { + { + if (m_buffer.IsPaneTransparent(i)) + RedrawWindow(m_imgWindow[i].GetHWND(), NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOERASE); + } Invalidate(); } } From e0f92b56105be233a89ded7dee53b911b059b734 Mon Sep 17 00:00:00 2001 From: Edi61 <84682915+Edi61@users.noreply.github.com> Date: Fri, 9 Jan 2026 20:48:38 +0300 Subject: [PATCH 6/8] Fixed: Mixed transparent and not transparante images --- src/WinIMergeLib/ImgDiffBuffer.hpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/WinIMergeLib/ImgDiffBuffer.hpp b/src/WinIMergeLib/ImgDiffBuffer.hpp index 209c8d4..c2bcdff 100644 --- a/src/WinIMergeLib/ImgDiffBuffer.hpp +++ b/src/WinIMergeLib/ImgDiffBuffer.hpp @@ -546,6 +546,14 @@ 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; @@ -797,9 +805,10 @@ class CImgDiffBuffer if (m_wipePosition == pos) return; m_wipePosition = pos; + bool anyTransparent = IsAnyPaneTransparent(); for (int i = 0; i < m_nImages; ++i) { - if (m_imgDiffIsTransparent[i] == true) + if (m_imgDiffIsTransparent[i]) { // Dummy operation with less performance to retrigger redrawing // setPixelColor leads to _bHasChanged = TRUE which will enable redrawing @@ -807,6 +816,13 @@ class CImgDiffBuffer 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(); } From 6444735fbe2e9ef918a143e7874f0885bf4e1f35 Mon Sep 17 00:00:00 2001 From: Edi61 <84682915+Edi61@users.noreply.github.com> Date: Fri, 9 Jan 2026 21:03:48 +0300 Subject: [PATCH 7/8] Speed up, mixed transparancy mode --- src/WinIMergeLib/ImgMergeWindow.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/WinIMergeLib/ImgMergeWindow.hpp b/src/WinIMergeLib/ImgMergeWindow.hpp index 7bec076..a15103a 100644 --- a/src/WinIMergeLib/ImgMergeWindow.hpp +++ b/src/WinIMergeLib/ImgMergeWindow.hpp @@ -1820,9 +1820,10 @@ class CImgMergeWindow : public IImgMergeWindow m_buffer.SetWipePosition(pt.y); if (m_buffer.GetWipePosition() != oldWipePos) { + bool anyTransparent = m_buffer.IsAnyPaneTransparent(); for (int i = 0; i < m_nImages; ++i) { - if (m_buffer.IsPaneTransparent(i)) + if (m_buffer.IsPaneTransparent(i) || (anyTransparent && m_nImages >= 3)) RedrawWindow(m_imgWindow[i].GetHWND(), NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOERASE); } Invalidate(); @@ -1835,9 +1836,10 @@ class CImgMergeWindow : public IImgMergeWindow m_buffer.SetWipePosition(pt.x); if (m_buffer.GetWipePosition() != oldWipePos) { + bool anyTransparent = m_buffer.IsAnyPaneTransparent(); for (int i = 0; i < m_nImages; ++i) { - if (m_buffer.IsPaneTransparent(i)) + if (m_buffer.IsPaneTransparent(i) || (anyTransparent && m_nImages >= 3)) RedrawWindow(m_imgWindow[i].GetHWND(), NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW | RDW_NOERASE); } Invalidate(); From e68793c31d98a1d78009c430e53b209636a96ad4 Mon Sep 17 00:00:00 2001 From: Edi61 <84682915+Edi61@users.noreply.github.com> Date: Fri, 9 Jan 2026 21:15:26 +0300 Subject: [PATCH 8/8] Speedup horizontal wiping --- src/WinIMergeLib/ImgDiffBuffer.hpp | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/WinIMergeLib/ImgDiffBuffer.hpp b/src/WinIMergeLib/ImgDiffBuffer.hpp index c2bcdff..4fcf760 100644 --- a/src/WinIMergeLib/ImgDiffBuffer.hpp +++ b/src/WinIMergeLib/ImgDiffBuffer.hpp @@ -1997,6 +1997,10 @@ class CImgDiffBuffer 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) @@ -2007,31 +2011,22 @@ class CImgDiffBuffer { scanline = m_imgDiff[pane].scanLine(y); scanline2 = m_imgDiff[pane + 1].scanLine(y); - } - else - { - scanline = m_imgDiff[m_nImages - 2 - pane].scanLine(y); - scanline2 = m_imgDiff[m_nImages - 1 - pane].scanLine(y); - } - - if (m_wipePosition <= m_wipePosition_old) - { for (unsigned x = m_wipePosition; x < m_wipePosition_old; ++x) { - std::swap(scanline[x * 4 + 0], scanline2[x * 4 + 0]); - std::swap(scanline[x * 4 + 1], scanline2[x * 4 + 1]); - std::swap(scanline[x * 4 + 2], scanline2[x * 4 + 2]); - std::swap(scanline[x * 4 + 3], scanline2[x * 4 + 3]); + 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) { - std::swap(scanline[x * 4 + 0], scanline2[x * 4 + 0]); - std::swap(scanline[x * 4 + 1], scanline2[x * 4 + 1]); - std::swap(scanline[x * 4 + 2], scanline2[x * 4 + 2]); - std::swap(scanline[x * 4 + 3], scanline2[x * 4 + 3]); + memcpy(tmp, scanline + x * pixelBytes, pixelBytes); + memcpy(scanline + x * pixelBytes, scanline2 + x * pixelBytes, pixelBytes); + memcpy(scanline2 + x * pixelBytes, tmp, pixelBytes); } } }