Speedup image comparision slider during WipeEffect#33
Speedup image comparision slider during WipeEffect#33Edi61 wants to merge 9 commits intoWinMerge:masterfrom
Conversation
- WipeEffect only if position changed - Difference based wipeeffect (Only part between old and new position is updated)
|
Aah, I have only checked with heic files, did not jet tessted with other image formats. |
|
I have found the problem, it's generally not the image format, |
|
That certainly seems like it. As of now, I don't know why either. |
|
I have found the root cause. It's the function call FreeImage_Composite inside fipWinImage::drawEx: Problem is that a copy of a new display including transparency is created and this copy |
Transparency detection and triggering of a redraw in case of image wiping
|
I have found one solution, maybe not the best, but seems principially working. |
|
Sorry for the late response. |
There was a problem hiding this comment.
Pull request overview
This PR aims to speed up the image comparison wipe/slider interaction by avoiding full image refreshes on every mouse move and by applying the wipe effect incrementally between old/new slider positions.
Changes:
- Only trigger redraw/invalidations during wipe dragging when the wipe position actually changes.
- Change wipe updates from “rebuild diff images every time” to an incremental
WipeEffect()based on old/new wipe positions. - Add transparency tracking to handle redraw behavior with transparent panes.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
src/WinIMergeLib/ImgMergeWindow.hpp |
Reduces redraw work during wipe dragging by only invalidating when the wipe position changes, with special handling for transparency. |
src/WinIMergeLib/ImgDiffBuffer.hpp |
Implements incremental wipe updates using m_wipePosition_old, adds pane transparency helpers, and changes wipe positioning to call WipeEffect() instead of RefreshImages(). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // 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); |
There was a problem hiding this comment.
SetWipePosition() does a get/set of pixel (0,0) to force FreeImage to mark the bitmap changed. This will crash or corrupt memory if a diff image ever has zero width/height. Please guard the dummy operation with a size check (and/or prefer a safer API like setModified(true) if it has the desired effect for transparent panes too).
| // 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); | |
| // Dummy operation with less performance to retrigger redrawing. | |
| // setPixelColor leads to _bHasChanged = TRUE which will enable redrawing. | |
| // Guard against zero-sized images to avoid out-of-bounds pixel access. | |
| auto* img = m_imgDiff[i].getFipImage(); | |
| if (img != nullptr && img->getWidth() > 0 && img->getHeight() > 0) | |
| { | |
| RGBQUAD color; | |
| img->getPixelColor(0, 0, &color); | |
| img->setPixelColor(0, 0, &color); | |
| } | |
| else if (img != nullptr) | |
| { | |
| // Fall back to the safer API for zero-sized images. | |
| img->setModified(true); | |
| } |
| MarkDiff(i, m_diff); | ||
| } | ||
| } | ||
| m_wipePosition_old = INT_MAX; |
There was a problem hiding this comment.
RefreshImages() now resets m_wipePosition_old but no longer reapplies WipeEffect() when m_wipeMode != WIPE_NONE. Since RefreshImages() is triggered by the blink/overlay animation timer, this can rebuild m_imgDiff into a non-wiped state while leaving m_wipePosition unchanged; subsequent SetWipePosition() calls may early-return (position unchanged) and the wipe can appear to “get lost” until the mouse moves. Reapply WipeEffect() as part of RefreshImages() when wipe mode is active (or otherwise ensure the wiped state is restored whenever m_imgDiff is regenerated).
| m_wipePosition_old = INT_MAX; | |
| m_wipePosition_old = INT_MAX; | |
| if (m_wipeMode != WIPE_NONE) | |
| { | |
| WipeEffect(); | |
| } |
| const size_t lineBytes = w * 4; | ||
| auto tmp = new unsigned char[lineBytes]; | ||
| if (m_wipePosition <= m_wipePosition_old) |
There was a problem hiding this comment.
WipeEffect() allocates/free's a temporary line buffer with new[]/delete[] on every call. With mouse-driven wiping this can become a hot path and the allocator churn can offset some of the intended speedup. Consider using a std::vector<unsigned char> (or a reusable member buffer) to avoid repeated heap allocations and make the code exception-safe.
| { | ||
| for (int i = 0; i < 3; ++i) | ||
| m_currentPage[i] = 0; | ||
| for (int i = 0; i < m_nImages; ++i) |
There was a problem hiding this comment.
m_imgDiffIsTransparent isn't initialized because the loop runs i < m_nImages, but m_nImages is 0 in the constructor initializer list. This leaves the array with indeterminate values until SetWipeMode* is called, and methods like IsAnyPaneTransparent()/IsPaneTransparent() can read garbage. Initialize all 3 entries explicitly (or default-initialize the member) regardless of m_nImages.
| for (int i = 0; i < m_nImages; ++i) | |
| for (int i = 0; i < 3; ++i) |
| void SetWipeMode(WIPE_MODE wipeMode) | ||
| { | ||
| if (m_wipeMode == wipeMode) | ||
| 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; | ||
| } |
There was a problem hiding this comment.
m_imgDiffIsTransparent is only refreshed in SetWipeMode()/SetWipeModePosition(), but RefreshImages() is called from several other code paths (including the animation/blink timer) and can rebuild m_imgDiff such that transparency changes. If this cache becomes stale, SetWipePosition() and the window code will make redraw decisions using incorrect transparency info. Consider recomputing transparency inside RefreshImages() (or extracting a helper used by all code paths that rebuild m_imgDiff).

As also marked in
WinMerge/winmerge#1596
the slider is very slow.
Here maybe one solution:
in any case you are not be able to change any parameters which could lead to an image change)
Principially it works very fine and smooth, but if the image is changed before wiping with e.g. adjust offset,
the draw events are lost/not perfmored, maybe you have an idea why?
Regards,