From 335200df0ac285c7c0ded49f459bec1b82624bf1 Mon Sep 17 00:00:00 2001 From: sdottaka Date: Fri, 3 Oct 2014 22:20:21 +0900 Subject: [PATCH 01/16] CImgMergeWindow.hpp: Move non-GUI code to CImgMergeBuffer.hpp --- src/CImgMergeBuffer.hpp | 1509 ++++++++++++++++++++++++++++++++++++++ src/CImgMergeWindow.hpp | 1336 ++++----------------------------- src/WinIMergeLib.vcxproj | 1 + src/WinIMergeLib.vcxproj.filters | 7 +- 4 files changed, 1643 insertions(+), 1210 deletions(-) create mode 100644 src/CImgMergeBuffer.hpp diff --git a/src/CImgMergeBuffer.hpp b/src/CImgMergeBuffer.hpp new file mode 100644 index 000000000..a381bcc31 --- /dev/null +++ b/src/CImgMergeBuffer.hpp @@ -0,0 +1,1509 @@ +///////////////////////////////////////////////////////////////////////////// +// License (GPLv2+): +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +///////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "FreeImagePlus.h" +#include "WinIMergeLib.h" +#include +#include +#include +#include +#include +#include + +template struct Point +{ + Point(T x, T y): x(x), y(y) {} + T x, y; +}; + +template struct Size +{ + Size(T cx, T cy): cx(cx), cy(cy) {} + T cx, cy; +}; + +template struct Rect +{ + Rect(T left, T top, T right, T bottom): left(left), top(top), right(right), bottom(bottom) {} + T left, top, right, bottom; +}; + +template struct Array2D +{ + Array2D() : m_width(0), m_height(0), m_data(NULL) + { + } + + Array2D(size_t width, size_t height) : m_width(width), m_height(height), m_data(new T[width * height]) + { + memset(m_data, 0, m_width * m_height * sizeof(T)); + } + + Array2D(const Array2D& other) : m_width(other.m_width), m_height(other.m_height), m_data(new T[other.m_width * other.m_height]) + { + memcpy(m_data, other.m_data, m_width * m_height * sizeof(T)); + } + + Array2D& operator=(const Array2D& other) + { + if (this != &other) + { + delete[] m_data; + m_width = other.m_width; + m_height = other.m_height; + m_data = new T[other.m_width * other.m_height]; + memcpy(m_data, other.m_data, m_width * m_height * sizeof(T)); + } + return *this; + } + + ~Array2D() + { + delete[] m_data; + } + + void resize(size_t width, size_t height) + { + delete[] m_data; + m_data = new T[width * height]; + m_width = width; + m_height = height; + memset(m_data, 0, sizeof(T) * width * height); + } + + T& operator()(int x, int y) + { + return m_data[y * m_width + x]; + } + + const T& operator()(int x, int y) const + { + return m_data[y * m_width + x]; + } + + void clear() + { + delete[] m_data; + m_data = NULL; + m_width = 0; + m_height = 0; + } + + size_t height() const + { + return m_height; + } + + size_t width() const + { + return m_width; + } + + size_t m_width, m_height; + T* m_data; +}; + +struct DiffInfo +{ + enum OP_TYPE + { + OP_NONE = 0, OP_1STONLY, OP_2NDONLY, OP_3RDONLY, OP_DIFF, OP_TRIVIAL + }; + DiffInfo(int op, int x, int y) : op(op), rc(x, y, x + 1, y + 1) {} + int op; + Rect rc; +}; + +struct UndoRecord +{ + UndoRecord(int pane, FIBITMAP *oldbitmap, FIBITMAP *newbitmap, const int modcountnew[3]) : + pane(pane), oldbitmap(oldbitmap), newbitmap(newbitmap) + { + for (int i = 0; i < 3; ++i) + modcount[i] = modcountnew[i]; + } + int pane; + int modcount[3]; + FIBITMAP *oldbitmap, *newbitmap; +}; + +struct UndoRecords +{ + UndoRecords() : m_currentUndoBufIndex(-1) + { + clear(); + } + + ~UndoRecords() + { + clear(); + } + + void push_back(int pane, FIBITMAP *oldbitmap, FIBITMAP *newbitmap) + { + ++m_currentUndoBufIndex; + while (m_currentUndoBufIndex < static_cast(m_undoBuf.size())) + { + --m_modcount[m_undoBuf.back().pane]; + FreeImage_Unload(m_undoBuf.back().newbitmap); + FreeImage_Unload(m_undoBuf.back().oldbitmap); + m_undoBuf.pop_back(); + } + ++m_modcount[pane]; + m_undoBuf.push_back(UndoRecord(pane, oldbitmap, newbitmap, m_modcount)); + } + + const UndoRecord& undo() + { + if (m_currentUndoBufIndex < 0) + throw "no undoable"; + const UndoRecord& rec = m_undoBuf[m_currentUndoBufIndex]; + --m_currentUndoBufIndex; + return rec; + } + + const UndoRecord& redo() + { + if (m_currentUndoBufIndex >= static_cast(m_undoBuf.size()) - 1) + throw "no redoable"; + ++m_currentUndoBufIndex; + const UndoRecord& rec = m_undoBuf[m_currentUndoBufIndex]; + return rec; + } + + bool is_modified(int pane) const + { + if (m_currentUndoBufIndex < 0) + return (m_modcountonsave[pane] != 0); + else + return (m_modcountonsave[pane] != m_undoBuf[m_currentUndoBufIndex].modcount[pane]); + } + + void save(int pane) + { + if (m_currentUndoBufIndex < 0) + m_modcountonsave[pane] = 0; + else + m_modcountonsave[pane] = m_undoBuf[m_currentUndoBufIndex].modcount[pane]; + } + + bool undoable() const + { + return (m_currentUndoBufIndex >= 0); + } + + bool redoable() const + { + return (m_currentUndoBufIndex < static_cast(m_undoBuf.size()) - 1); + } + + void clear() + { + m_currentUndoBufIndex = -1; + for (int i = 0; i < 3; ++i) + { + m_modcount[i] = 0; + m_modcountonsave[i] = 0; + } + while (!m_undoBuf.empty()) + { + FreeImage_Unload(m_undoBuf.back().newbitmap); + FreeImage_Unload(m_undoBuf.back().oldbitmap); + m_undoBuf.pop_back(); + } + } + + std::vector m_undoBuf; + int m_currentUndoBufIndex; + int m_modcount[3]; + int m_modcountonsave[3]; +}; + +class fipImageEx : public fipImage +{ +public: + fipImageEx(FREE_IMAGE_TYPE image_type = FIT_BITMAP, unsigned width = 0, unsigned height = 0, unsigned bpp = 0) : + fipImage(image_type, width, height, bpp) {} + fipImageEx(const fipImageEx& Image) : fipImage(Image) {} + virtual ~fipImageEx() {} + + fipImageEx& operator=(const fipImageEx& Image) + { + if (this != &Image) + { + FIBITMAP *clone = FreeImage_Clone((FIBITMAP*)Image._dib); + replace(clone); + } + return *this; + } + + fipImageEx& operator=(FIBITMAP *dib) + { + if (_dib != dib) + replace(dib); + return *this; + } + + void swap(fipImageEx& other) + { + std::swap(_dib, other._dib); + std::swap(this->_fif, other._fif); + std::swap(this->_bHasChanged, other._bHasChanged); + } + + FIBITMAP *detach() + { + FIBITMAP *dib = _dib; + _dib = NULL; + clear(); + return dib; + } + + BOOL colorQuantizeEx(FREE_IMAGE_QUANTIZE quantize = FIQ_WUQUANT, int PaletteSize = 256, int ReserveSize = 0, RGBQUAD *ReservePalette = NULL) + { + if(_dib) { + FIBITMAP *dib8 = FreeImage_ColorQuantizeEx(_dib, quantize, PaletteSize, ReserveSize, ReservePalette); + return !!replace(dib8); + } + return false; + } + + bool convertColorDepth(unsigned bpp, RGBQUAD *pPalette = NULL) + { + switch (bpp) + { + case 1: + return !!threshold(128); + case 4: + { + fipImageEx tmp = *this; + tmp.convertTo24Bits(); + if (pPalette) + tmp.colorQuantizeEx(FIQ_NNQUANT, 16, 16, pPalette); + else + tmp.colorQuantizeEx(FIQ_WUQUANT, 16); + setSize(tmp.getImageType(), tmp.getWidth(), tmp.getHeight(), 4); + for (unsigned y = 0; y < tmp.getHeight(); ++y) + { + const BYTE *line_src = tmp.getScanLine(y); + BYTE *line_dst = getScanLine(y); + for (unsigned x = 0; x < tmp.getWidth(); ++x) + line_dst[x / 2] |= ((x % 2) == 0) ? (line_src[x] << 4) : line_src[x]; + } + + RGBQUAD *rgbq_dst = getPalette(); + RGBQUAD *rgbq_src = pPalette ? pPalette : tmp.getPalette(); + memcpy(rgbq_dst, rgbq_src, sizeof(RGBQUAD) * 16); + return true; + } + case 8: + convertTo24Bits(); + if (pPalette) + return !!colorQuantizeEx(FIQ_NNQUANT, 256, 256, pPalette); + else + return !!colorQuantizeEx(FIQ_WUQUANT, 256); + case 15: + return !!convertTo16Bits555(); + case 16: + return !!convertTo16Bits565(); + case 24: + return !!convertTo24Bits(); + default: + case 32: + return !!convertTo32Bits(); + } + } + + void copyAnimationMetadata(fipImage& src) + { + fipTag tag; + fipMetadataFind finder; + if (finder.findFirstMetadata(FIMD_ANIMATION, src, tag)) + { + do + { + setMetadata(FIMD_ANIMATION, tag.getKey(), tag); + } while (finder.findNextMetadata(tag)); + } + } +}; + +class fipMultiPageEx : public fipMultiPage +{ +public: + fipMultiPageEx(BOOL keep_cache_in_memory = FALSE) : fipMultiPage(keep_cache_in_memory) {} + + BOOL openU(const wchar_t* lpszPathName, BOOL create_new, BOOL read_only, int flags = 0) + { + wchar_t shortname[260]; + char filename[260]; + GetShortPathName(lpszPathName, shortname, sizeof(shortname)); + wsprintfA(filename, "%S", shortname); + BOOL result = open(filename, create_new, read_only, flags); + return result; + } + + bool saveU(const wchar_t* lpszPathName, int flag = 0) const + { + FILE *fp = NULL; + _wfopen_s(&fp, lpszPathName, L"r+b"); + if (!fp) + return false; + FreeImageIO io; + io.read_proc = myReadProc; + io.write_proc = myWriteProc; + io.seek_proc = mySeekProc; + io.tell_proc = myTellProc; + FREE_IMAGE_FORMAT fif = fipImage::identifyFIFU(lpszPathName); + bool result = !!saveToHandle(fif, &io, (fi_handle)fp, flag); + fclose(fp); + return result; + } + +private: + static unsigned DLL_CALLCONV myReadProc(void *buffer, unsigned size, unsigned count, fi_handle handle) { + return (unsigned)fread(buffer, size, count, (FILE *)handle); + } + + static unsigned DLL_CALLCONV myWriteProc(void *buffer, unsigned size, unsigned count, fi_handle handle) { + return (unsigned)fwrite(buffer, size, count, (FILE *)handle); + } + + static int DLL_CALLCONV mySeekProc(fi_handle handle, long offset, int origin) { + return fseek((FILE *)handle, offset, origin); + } + + static long DLL_CALLCONV myTellProc(fi_handle handle) { + return ftell((FILE *)handle); + } +}; + +namespace +{ + int GetColorDistance2(RGBQUAD c1, RGBQUAD c2) + { + int rdist = c1.rgbRed - c2.rgbRed; + int gdist = c1.rgbGreen - c2.rgbGreen; + int bdist = c1.rgbBlue - c2.rgbBlue; + int adist = c1.rgbReserved - c2.rgbReserved; + return rdist * rdist + gdist * gdist + bdist * bdist + adist * adist; + } +} + +class CImgMergeBuffer +{ +public: + CImgMergeBuffer() : + m_nImages(0) + , m_diffBlockSize(8) + , m_overlayMode(IImgMergeWindow::OVERLAY_NONE) + , m_overlayAlpha(0.3) + , m_showDifferences(true) + , m_selDiffColor(RGB(0xff, 0x40, 0x40)) + , m_diffColor(RGB(0xff, 0xff, 0x40)) + , m_diffColorAlpha(0.7) + , m_diffCount(0) + , m_currentDiffIndex(-1) + , m_colorDistanceThreshold(0.0) + { + for (int i = 0; i < 3; ++i) + { + m_currentPage[i] = 0; + m_bRO[i] = false; + } + } + + ~CImgMergeBuffer() + { + } + + const wchar_t *GetFileName(int pane) + { + if (pane < 0 || pane >= m_nImages) + return NULL; + return m_filename[pane].c_str(); + } + + int GetPaneCount() const + { + return m_nImages; + } + + RGBQUAD GetPixelColor(int pane, int x, int y) const + { + RGBQUAD value = {0xFF, 0xFF, 0xFF, 0x00}; + m_imgOrig32[pane].getPixelColor(x, m_imgOrig32[pane].getHeight() - y - 1, &value); + return value; + } + + double GetColorDistance(int pane1, int pane2, int x, int y) const + { + return std::sqrt(static_cast( + ::GetColorDistance2(GetPixelColor(pane1, x, y), GetPixelColor(pane2, x, y)) )); + } + + bool GetReadOnly(int pane) const + { + if (pane < 0 || pane >= m_nImages) + return true; + return m_bRO[pane]; + } + + void SetReadOnly(int pane, bool readOnly) + { + if (pane < 0 || pane >= m_nImages) + return; + m_bRO[pane] = readOnly; + } + + COLORREF GetDiffColor() const + { + return m_diffColor; + } + + void SetDiffColor(COLORREF clrDiffColor) + { + m_diffColor = clrDiffColor; + RefreshImages(); + } + + COLORREF GetSelDiffColor() const + { + return m_selDiffColor; + } + + void SetSelDiffColor(COLORREF clrSelDiffColor) + { + m_selDiffColor = clrSelDiffColor; + RefreshImages(); + } + + double GetDiffColorAlpha() const + { + return m_diffColorAlpha; + } + + void SetDiffColorAlpha(double diffColorAlpha) + { + m_diffColorAlpha = diffColorAlpha; + RefreshImages(); + } + + int GetCurrentPage(int pane) const + { + if (pane < 0 || pane >= m_nImages) + return -1; + return m_currentPage[pane]; + } + + void SetCurrentPage(int pane, int page) + { + if (page >= 0 && page < GetPageCount(pane)) + { + if (m_imgOrigMultiPage[pane].isValid()) + { + m_currentPage[pane] = page; + FIBITMAP *bitmap = m_imgOrigMultiPage[pane].lockPage(page); + m_imgOrig[pane] = FreeImage_Clone(bitmap); + m_imgOrig32[pane] = m_imgOrig[pane]; + FreeImage_UnlockPage(m_imgOrigMultiPage[pane], bitmap, false); + m_imgOrig32[pane].convertTo32Bits(); + CompareImages(); + } + } + } + + void SetCurrentPageAll(int page) + { + for (int i = 0; i < m_nImages; ++i) + SetCurrentPage(i, page); + } + + int GetCurrentMaxPage() const + { + int maxpage = 0; + for (int i = 0; i < m_nImages; ++i) + { + int page = GetCurrentPage(i); + maxpage = maxpage < page ? page : maxpage; + } + return maxpage; + } + + int GetPageCount(int pane) const + { + if (pane < 0 || pane >= m_nImages) + return -1; + if (m_imgOrigMultiPage[pane].isValid()) + return m_imgOrigMultiPage[pane].getPageCount(); + else + return 1; + } + + int GetMaxPageCount() const + { + int maxpage = 0; + for (int i = 0; i < m_nImages; ++i) + { + int page = GetPageCount(i); + maxpage = page > maxpage ? page : maxpage; + } + return maxpage; + } + + double GetColorDistanceThreshold() const + { + return m_colorDistanceThreshold; + } + + void SetColorDistanceThreshold(double threshold) + { + m_colorDistanceThreshold = threshold; + CompareImages(); + } + + int GetDiffBlockSize() const + { + return m_diffBlockSize; + } + + void SetDiffBlockSize(int blockSize) + { + m_diffBlockSize = blockSize; + CompareImages(); + } + + IImgMergeWindow::OVERLAY_MODE GetOverlayMode() const + { + return m_overlayMode; + } + + void SetOverlayMode(IImgMergeWindow::OVERLAY_MODE overlayMode) + { + m_overlayMode = overlayMode; + RefreshImages(); + } + + double GetOverlayAlpha() const + { + return m_overlayAlpha; + } + + void SetOverlayAlpha(double overlayAlpha) + { + m_overlayAlpha = overlayAlpha; + RefreshImages(); + } + + bool GetShowDifferences() const + { + return m_showDifferences; + } + + void SetShowDifferences(bool visible) + { + m_showDifferences = visible; + CompareImages(); + } + + const DiffInfo *GetDiffInfo(int diffIndex) const + { + if (diffIndex < 0 || diffIndex >= m_diffCount) + return NULL; + return &m_diffInfos[diffIndex]; + } + + int GetDiffCount() const + { + return m_diffCount; + } + + int GetConflictCount() const + { + int conflictCount = 0; + for (int i = 0; i < m_diffCount; ++i) + if (m_diffInfos[i].op == DiffInfo::OP_DIFF) + ++conflictCount; + return conflictCount; + } + + int GetCurrentDiffIndex() const + { + return m_currentDiffIndex; + } + + bool FirstDiff() + { + if (m_diffCount == 0) + m_currentDiffIndex = -1; + else + m_currentDiffIndex = 0; + RefreshImages(); + return true; + } + + bool LastDiff() + { + m_currentDiffIndex = m_diffCount - 1; + RefreshImages(); + return true; + } + + bool NextDiff() + { + ++m_currentDiffIndex; + if (m_currentDiffIndex >= m_diffCount) + m_currentDiffIndex = m_diffCount - 1; + RefreshImages(); + return true; + } + + bool PrevDiff() + { + if (m_diffCount == 0) + m_currentDiffIndex = -1; + else + { + --m_currentDiffIndex; + if (m_currentDiffIndex < 0) + m_currentDiffIndex = 0; + } + RefreshImages(); + return true; + } + + bool FirstConflict() + { + for (int i = 0; i < m_diffInfos.size(); ++i) + if (m_diffInfos[i].op == DiffInfo::OP_DIFF) + m_currentDiffIndex = i; + RefreshImages(); + return true; + } + + bool LastConflict() + { + for (int i = static_cast(m_diffInfos.size() - 1); i >= 0; --i) + { + if (m_diffInfos[i].op == DiffInfo::OP_DIFF) + { + m_currentDiffIndex = i; + break; + } + } + RefreshImages(); + return true; + } + + bool NextConflict() + { + for (size_t i = m_currentDiffIndex + 1; i < m_diffInfos.size(); ++i) + { + if (m_diffInfos[i].op == DiffInfo::OP_DIFF) + { + m_currentDiffIndex = static_cast(i); + break; + } + } + RefreshImages(); + return true; + } + + bool PrevConflict() + { + for (int i = m_currentDiffIndex - 1; i >= 0; --i) + { + if (m_diffInfos[i].op == DiffInfo::OP_DIFF) + { + m_currentDiffIndex = i; + break; + } + } + RefreshImages(); + return true; + } + + bool SelectDiff(int diffIndex) + { + m_currentDiffIndex = diffIndex; + RefreshImages(); + return true; + } + + int GetNextDiffIndex() const + { + if (m_diffCount == 0 || m_currentDiffIndex >= m_diffCount - 1) + return -1; + return m_currentDiffIndex + 1; + } + + int GetPrevDiffIndex() const + { + if (m_diffCount == 0 || m_currentDiffIndex <= 0) + return -1; + return m_currentDiffIndex - 1; + } + + int GetNextConflictIndex() const + { + for (size_t i = m_currentDiffIndex + 1; i < m_diffInfos.size(); ++i) + if (m_diffInfos[i].op == DiffInfo::OP_DIFF) + return static_cast(i); + return -1; + } + + int GetPrevConflictIndex() const + { + for (int i = static_cast(m_currentDiffIndex - 1); i >= 0; --i) + if (m_diffInfos[i].op == DiffInfo::OP_DIFF) + return i; + return -1; + } + + void CopyDiffInternal(int diffIndex, int srcPane, int dstPane) + { + if (srcPane < 0 || srcPane >= m_nImages) + return; + if (dstPane < 0 || dstPane >= m_nImages) + return; + if (diffIndex < 0 || diffIndex >= m_diffCount) + return; + if (m_bRO[dstPane]) + return; + + const Rect& rc = m_diffInfos[diffIndex].rc; + unsigned wsrc = m_imgOrig32[srcPane].getWidth(); + unsigned hsrc = m_imgOrig32[srcPane].getHeight(); + unsigned wdst = m_imgOrig32[dstPane].getWidth(); + unsigned hdst = m_imgOrig32[dstPane].getHeight(); + if (rc.right * m_diffBlockSize > wdst) + { + if ((std::max)(wsrc, wdst) < rc.right * m_diffBlockSize) + wdst = (std::max)(wsrc, wdst); + else + wdst = rc.right * m_diffBlockSize; + } + if (rc.bottom * m_diffBlockSize > hdst) + { + if ((std::max)(hsrc, hdst) < rc.bottom * m_diffBlockSize) + hdst = (std::max)(hsrc, hdst); + else + hdst = rc.bottom * m_diffBlockSize; + } + if (rc.right * m_diffBlockSize > wsrc) + wdst = wsrc; + if (rc.bottom * m_diffBlockSize > hsrc) + hdst = hsrc; + if (wdst != m_imgOrig32[dstPane].getWidth() || hdst != m_imgOrig32[dstPane].getHeight()) + { + fipImage imgTemp = m_imgOrig32[srcPane]; + m_imgOrig32[dstPane].setSize(imgTemp.getImageType(), wdst, hdst, imgTemp.getBitsPerPixel()); + m_imgOrig32[dstPane].pasteSubImage(imgTemp, 0, 0); + } + + for (unsigned y = rc.top * m_diffBlockSize; y < rc.bottom * m_diffBlockSize; y += m_diffBlockSize) + { + for (unsigned x = rc.left * m_diffBlockSize; x < rc.right * m_diffBlockSize; x += m_diffBlockSize) + { + if (m_diff(x / m_diffBlockSize, y / m_diffBlockSize) == diffIndex + 1) + { + int sizex = ((x + m_diffBlockSize) < wsrc) ? m_diffBlockSize : (wsrc - x); + int sizey = ((y + m_diffBlockSize) < hsrc) ? m_diffBlockSize : (hsrc - y); + if (sizex > 0 && sizey > 0) + { + for (int i = 0; i < sizey; ++i) + { + const BYTE *scanline_src = m_imgOrig32[srcPane].getScanLine(hsrc - (y + i) - 1); + BYTE *scanline_dst = m_imgOrig32[dstPane].getScanLine(hdst - (y + i) - 1); + memcpy(&scanline_dst[x * 4], &scanline_src[x * 4], sizex * 4); + } + } + } + } + } + } + + void CopyDiff(int diffIndex, int srcPane, int dstPane) + { + if (srcPane < 0 || srcPane >= m_nImages) + return; + if (dstPane < 0 || dstPane >= m_nImages) + return; + if (diffIndex < 0 || diffIndex >= m_diffCount) + return; + if (m_bRO[dstPane]) + return; + if (srcPane == dstPane) + return; + + FIBITMAP *oldbitmap = FreeImage_Clone(m_imgOrig32[dstPane]); + + CopyDiffInternal(diffIndex, srcPane, dstPane); + + FIBITMAP *newbitmap = FreeImage_Clone(m_imgOrig32[dstPane]); + m_undoRecords.push_back(dstPane, oldbitmap, newbitmap); + CompareImages(); + } + + void CopyDiffAll(int srcPane, int dstPane) + { + if (srcPane < 0 || srcPane >= m_nImages) + return; + if (dstPane < 0 || dstPane >= m_nImages) + return; + if (m_bRO[dstPane]) + return; + if (srcPane == dstPane) + return; + + FIBITMAP *oldbitmap = FreeImage_Clone(m_imgOrig32[dstPane]); + + for (int diffIndex = 0; diffIndex < m_diffCount; ++diffIndex) + CopyDiffInternal(diffIndex, srcPane, dstPane); + + FIBITMAP *newbitmap = FreeImage_Clone(m_imgOrig32[dstPane]); + m_undoRecords.push_back(dstPane, oldbitmap, newbitmap); + CompareImages(); + } + + int CopyDiff3Way(int dstPane) + { + if (dstPane < 0 || dstPane >= m_nImages) + return 0; + if (m_bRO[dstPane]) + return 0; + + FIBITMAP *oldbitmap = FreeImage_Clone(m_imgOrig32[dstPane]); + + int nMerged = 0; + for (int diffIndex = 0; diffIndex < m_diffCount; ++diffIndex) + { + int srcPane; + switch (m_diffInfos[diffIndex].op) + { + case DiffInfo::OP_1STONLY: + if (dstPane == 1) + srcPane = 0; + else + srcPane = -1; + break; + case DiffInfo::OP_2NDONLY: + if (dstPane != 1) + srcPane = 1; + else + srcPane = -1; + break; + case DiffInfo::OP_3RDONLY: + if (dstPane == 1) + srcPane = 2; + else + srcPane = -1; + break; + case DiffInfo::OP_DIFF: + srcPane = -1; + break; + } + + if (srcPane >= 0) + { + CopyDiffInternal(diffIndex, srcPane, dstPane); + ++nMerged; + } + } + + FIBITMAP *newbitmap = FreeImage_Clone(m_imgOrig32[dstPane]); + m_undoRecords.push_back(dstPane, oldbitmap, newbitmap); + CompareImages(); + + return nMerged; + } + + bool IsModified(int pane) const + { + return m_undoRecords.is_modified(pane); + } + + bool IsUndoable() const + { + return m_undoRecords.undoable(); + } + + bool IsRedoable() const + { + return m_undoRecords.redoable(); + } + + bool Undo() + { + if (!m_undoRecords.undoable()) + return false; + const UndoRecord& rec = m_undoRecords.undo(); + m_imgOrig32[rec.pane] = FreeImage_Clone(rec.oldbitmap); + CompareImages(); + return true; + } + + bool Redo() + { + if (!m_undoRecords.redoable()) + return false; + const UndoRecord& rec = m_undoRecords.redo(); + m_imgOrig32[rec.pane] = FreeImage_Clone(rec.newbitmap); + CompareImages(); + return true; + } + + void CompareImages() + { + if (m_nImages <= 1) + return; + InitializeDiff(); + if (m_nImages == 2) + { + CompareImages2(0, 1, m_diff); + m_diffCount = MarkDiffIndex(m_diff); + } + else if (m_nImages == 3) + { + CompareImages2(0, 1, m_diff01); + CompareImages2(2, 1, m_diff21); + CompareImages2(0, 2, m_diff02); + Make3WayDiff(m_diff01, m_diff21, m_diff); + m_diffCount = MarkDiffIndex3way(m_diff01, m_diff21, m_diff02, m_diff); + } + if (m_currentDiffIndex >= m_diffCount) + m_currentDiffIndex = m_diffCount - 1; + RefreshImages(); + } + + void RefreshImages() + { + if (m_nImages <= 1) + return; + InitializeDiffImages(); + for (int i = 0; i < m_nImages; ++i) + CopyOriginalImageToDiffImage(i); + void (CImgMergeBuffer::*func)(int src, int dst) = NULL; + if (m_overlayMode == IImgMergeWindow::OVERLAY_ALPHABLEND) + func = &CImgMergeBuffer::AlphaBlendImages2; + else if (m_overlayMode == IImgMergeWindow::OVERLAY_XOR) + func = &CImgMergeBuffer::XorImages2; + if (func) + { + if (m_nImages == 2) + { + (this->*func)(1, 0); + (this->*func)(0, 1); + } + else if (m_nImages == 3) + { + (this->*func)(1, 0); + (this->*func)(0, 1); + (this->*func)(2, 1); + (this->*func)(1, 2); + } + } + if (m_showDifferences) + { + for (int i = 0; i < m_nImages; ++i) + MarkDiff(i, m_diff); + } + } + + bool OpenImages(int nImages, const wchar_t * const filename[3]) + { + CloseImages(); + m_nImages = nImages; + for (int i = 0; i < nImages; ++i) + m_filename[i] = filename[i]; + return LoadImages(); + } + + bool SaveImage(int pane) + { + if (pane < 0 || pane >= m_nImages) + return false; + if (m_bRO[pane]) + return false; + if (!m_undoRecords.is_modified(pane)) + return true; + bool result = SaveImageAs(pane, m_filename[pane].c_str()); + if (result) + m_undoRecords.save(pane); + return result; + } + + bool SaveImages() + { + for (int i = 0; i < m_nImages; ++i) + if (!SaveImage(i)) + return false; + return true; + } + + bool SaveImageAs(int pane, const wchar_t *filename) + { + if (pane < 0 || pane >= m_nImages) + return false; + unsigned bpp = m_imgOrig[pane].getBitsPerPixel(); + RGBQUAD palette[256]; + if (m_imgOrig[pane].getPaletteSize() > 0) + memcpy(palette, m_imgOrig[pane].getPalette(), m_imgOrig[pane].getPaletteSize()); + m_imgOrig[pane] = m_imgOrig32[pane]; + m_imgOrig[pane].convertColorDepth(bpp, palette); + if (m_imgOrigMultiPage[pane].isValid()) + { + fipImageEx imgOrg, imgAdd; + imgAdd = m_imgOrig[pane]; + imgOrg = m_imgOrigMultiPage[pane].lockPage(m_currentPage[pane]); + imgAdd.copyAnimationMetadata(imgOrg); + m_imgOrigMultiPage[pane].unlockPage(imgOrg, false); + m_imgOrigMultiPage[pane].insertPage(m_currentPage[pane], imgAdd); + imgAdd.detach(); + m_imgOrigMultiPage[pane].deletePage(m_currentPage[pane] + 1); + return !!m_imgOrigMultiPage[pane].saveU(filename); + } + else + { + return !!m_imgOrig[pane].saveU(filename); + } + } + + bool CloseImages() + { + for (int i = 0; i < m_nImages; ++i) + { + m_imgOrig[i].clear(); + m_imgOrig32[i].clear(); + m_undoRecords.clear(); + } + return true; + } + + bool SaveDiffImageAs(int pane, const wchar_t *filename) + { + if (pane < 0 || pane >= m_nImages) + return false; + return !!m_imgDiff[pane].saveU(filename); + } + + int GetImageWidth(int pane) const + { + if (pane < 0 || pane >= m_nImages) + return -1; + return m_imgOrig32[pane].getWidth(); + } + + int GetImageHeight(int pane) const + { + if (pane < 0 || pane >= m_nImages) + return -1; + return m_imgOrig32[pane].getHeight(); + } + + int GetImageBitsPerPixel(int pane) const + { + if (pane < 0 || pane >= m_nImages) + return -1; + return m_imgOrig[pane].getBitsPerPixel(); + } + + int GetDiffIndexFromPoint(int x, int y) const + { + if (x > 0 && y > 0 && + x < static_cast(m_imgDiff[0].getWidth()) && + y < static_cast(m_imgDiff[0].getHeight())) + { + return m_diff(x / m_diffBlockSize, y / m_diffBlockSize); + } + return -1; + } + + fipWinImage *GetImage(int pane) + { + if (pane < 0 || pane >= m_nImages) + return NULL; + return &m_imgDiff[pane]; + } + +private: + bool LoadImages() + { + bool bSucceeded = true; + for (int i = 0; i < m_nImages; ++i) + { + m_currentPage[i] = 0; + m_imgOrigMultiPage[i].openU(m_filename[i].c_str(), FALSE, FALSE, 0); + if (m_imgOrigMultiPage[i].isValid()) + { + FIBITMAP *bitmap = m_imgOrigMultiPage[i].lockPage(m_currentPage[i]); + if (bitmap) + { + m_imgOrig[i] = FreeImage_Clone(bitmap); + m_imgOrig32[i] = m_imgOrig[i]; + FreeImage_UnlockPage(m_imgOrigMultiPage[i], bitmap, false); + } + else + m_imgOrigMultiPage[i].close(); + } + if (!m_imgOrigMultiPage[i].isValid()) + { + if (!m_imgOrig[i].loadU(m_filename[i].c_str())) + bSucceeded = false; + m_imgOrig32[i] = m_imgOrig[i]; + } + + m_imgOrig32[i].convertTo32Bits(); + } + return bSucceeded; + } + + Size GetMaxWidthHeight() + { + unsigned wmax = 0; + unsigned hmax = 0; + for (int i = 0; i < m_nImages; ++i) + { + wmax = (std::max)(wmax, m_imgOrig32[i].getWidth()); + hmax = (std::max)(hmax, m_imgOrig32[i].getHeight()); + } + return Size(wmax, hmax); + } + + void InitializeDiff() + { + Size size = GetMaxWidthHeight(); + int nBlocksX = (size.cx + m_diffBlockSize - 1) / m_diffBlockSize; + int nBlocksY = (size.cy + m_diffBlockSize - 1) / m_diffBlockSize; + + m_diff.clear(); + m_diff.resize(nBlocksX, nBlocksY); + if (m_nImages == 3) + { + m_diff01.clear(); + m_diff01.resize(nBlocksX, nBlocksY); + m_diff21.clear(); + m_diff21.resize(nBlocksX, nBlocksY); + m_diff02.clear(); + m_diff02.resize(nBlocksX, nBlocksY); + } + m_diffInfos.clear(); + } + + void InitializeDiffImages() + { + Size size = GetMaxWidthHeight(); + for (int i = 0; i < m_nImages; ++i) + m_imgDiff[i].setSize(FIT_BITMAP, size.cx, size.cy, 32); + } + + void CompareImages2(int pane1, int pane2, Array2D& diff) + { + unsigned w1 = m_imgOrig32[pane1].getWidth(); + unsigned h1 = m_imgOrig32[pane1].getHeight(); + unsigned w2 = m_imgOrig32[pane2].getWidth(); + unsigned h2 = m_imgOrig32[pane2].getHeight(); + + const unsigned wmax = (std::max)(w1, w2); + const unsigned hmax = (std::max)(h1, h2); + + for (unsigned by = 0; by < diff.height(); ++by) + { + unsigned bsy = (hmax - by * m_diffBlockSize) >= m_diffBlockSize ? m_diffBlockSize : (hmax - by * m_diffBlockSize); + for (unsigned i = 0; i < bsy; ++i) + { + unsigned y = by * m_diffBlockSize + i; + if (y >= h1 || y >= h2) + { + for (unsigned bx = 0; bx < wmax / m_diffBlockSize; ++bx) + diff(bx, by) = -1; + } + else + { + const BYTE *scanline1 = m_imgOrig32[pane1].getScanLine(h1 - y - 1); + const BYTE *scanline2 = m_imgOrig32[pane2].getScanLine(h2 - y - 1); + if (w1 == w2 && m_colorDistanceThreshold == 0.0) + { + if (memcmp(scanline1, scanline2, w1 * 4) == 0) + continue; + } + for (unsigned x = 0; x < wmax; ++x) + { + if (x >= w1 || x >= w2) + diff(x / m_diffBlockSize, by) = -1; + else + { + if (m_colorDistanceThreshold > 0.0) + { + int bdist = scanline1[x * 4 + 0] - scanline2[x * 4 + 0]; + int gdist = scanline1[x * 4 + 1] - scanline2[x * 4 + 1]; + int rdist = scanline1[x * 4 + 2] - scanline2[x * 4 + 2]; + int adist = scanline1[x * 4 + 3] - scanline2[x * 4 + 3]; + int colorDistance2 = rdist * rdist + gdist * gdist + bdist * bdist + adist * adist; + if (colorDistance2 > m_colorDistanceThreshold * m_colorDistanceThreshold) + diff(x / m_diffBlockSize, by) = -1; + } + else + { + if (scanline1[x * 4 + 0] != scanline2[x * 4 + 0] || + scanline1[x * 4 + 1] != scanline2[x * 4 + 1] || + scanline1[x * 4 + 2] != scanline2[x * 4 + 2] || + scanline1[x * 4 + 3] != scanline2[x * 4 + 3]) + { + diff(x / m_diffBlockSize, by) = -1; + } + } + } + } + } + } + } + } + + void FloodFill8Directions(Array2D& data, int x, int y, unsigned val) + { + std::vector > stack; + stack.push_back(Point(x, y)); + while (!stack.empty()) + { + const Point& pt = stack.back(); + const int x = pt.x; + const int y = pt.y; + stack.pop_back(); + if (data(x, y) != -1) + continue; + data(x, y) = val; + if (x + 1 < static_cast(data.width())) + { + stack.push_back(Point(x + 1, y)); + if (y + 1 < static_cast(data.height())) + stack.push_back(Point(x + 1, y + 1)); + if (y - 1 >= 0) + stack.push_back(Point(x + 1, y - 1)); + } + if (x - 1 >= 0) + { + stack.push_back(Point(x - 1, y)); + if (y + 1 < static_cast(data.height())) + stack.push_back(Point(x - 1, y + 1)); + if (y - 1 >= 0) + stack.push_back(Point(x - 1, y - 1)); + } + if (y + 1 < static_cast(data.height())) + stack.push_back(Point(x, y + 1)); + if (y - 1 >= 0) + stack.push_back(Point(x, y - 1)); + } + } + + int MarkDiffIndex(Array2D& diff) + { + int diffCount = 0; + for (unsigned by = 0; by < diff.height(); ++by) + { + for (unsigned bx = 0; bx < diff.width(); ++bx) + { + int idx = diff(bx, by); + if (idx == -1) + { + m_diffInfos.push_back(DiffInfo(DiffInfo::OP_DIFF, bx, by)); + ++diffCount; + FloodFill8Directions(diff, bx, by, diffCount); + } + else if (idx != 0) + { + Rect& rc = m_diffInfos[idx - 1].rc; + if (static_cast(bx) < rc.left) + rc.left = bx; + else if (static_cast(bx + 1) > rc.right) + rc.right = bx + 1; + if (static_cast(by) < rc.top) + rc.top = by; + else if (static_cast(by + 1) > rc.bottom) + rc.bottom = by + 1; + } + } + } + return diffCount; + } + + int MarkDiffIndex3way(Array2D& diff01, Array2D& diff21, Array2D& diff02, Array2D& diff3) + { + int diffCount = MarkDiffIndex(diff3); + std::vector> counter(m_diffInfos.size()); + for (unsigned by = 0; by < diff3.height(); ++by) + { + for (unsigned bx = 0; bx < diff3.width(); ++bx) + { + int diffIndex = diff3(bx, by); + if (diffIndex == 0) + continue; + --diffIndex; + if (diff21(bx, by) == 0) + ++counter[diffIndex][0]; + else if (diff02(bx, by) == 0) + ++counter[diffIndex][1]; + else if (diff01(bx, by) == 0) + ++counter[diffIndex][2]; + else + ++counter[diffIndex][3]; + } + } + + for (size_t i = 0; i < m_diffInfos.size(); ++i) + { + int op; + if (counter[i][0] != 0 && counter[i][1] == 0 && counter[i][2] == 0 && counter[i][3] == 0) + op = DiffInfo::OP_1STONLY; + else if (counter[i][0] == 0 && counter[i][1] != 0 && counter[i][2] == 0 && counter[i][3] == 0) + op = DiffInfo::OP_2NDONLY; + else if (counter[i][0] == 0 && counter[i][1] == 0 && counter[i][2] != 0 && counter[i][3] == 0) + op = DiffInfo::OP_3RDONLY; + else + op = DiffInfo::OP_DIFF; + m_diffInfos[i].op = op; + } + return diffCount; + } + + void Make3WayDiff(const Array2D& diff01, const Array2D& diff21, Array2D& diff3) + { + diff3 = diff01; + for (unsigned bx = 0; bx < diff3.width(); ++bx) + { + for (unsigned by = 0; by < diff3.height(); ++by) + { + if (diff21(bx, by) != 0) + diff3(bx, by) = -1; + } + } + } + + void MarkDiff(int pane, const Array2D& diff) + { + const unsigned w = m_imgDiff[pane].getWidth(); + const unsigned h = m_imgDiff[pane].getHeight(); + + for (unsigned by = 0; by < diff.height(); ++by) + { + for (unsigned bx = 0; bx < diff.width(); ++bx) + { + unsigned diffIndex = diff(bx, by); + if (diffIndex != 0 && ( + (pane == 0 && m_diffInfos[diffIndex - 1].op != DiffInfo::OP_3RDONLY) || + (pane == 1) || + (pane == 2 && m_diffInfos[diffIndex - 1].op != DiffInfo::OP_1STONLY) + )) + { + COLORREF color = (diffIndex - 1 == m_currentDiffIndex) ? m_selDiffColor : m_diffColor; + unsigned bsy = (h - by * m_diffBlockSize < m_diffBlockSize) ? (h - by * m_diffBlockSize) : m_diffBlockSize; + for (unsigned i = 0; i < bsy; ++i) + { + unsigned y = by * m_diffBlockSize + i; + BYTE *scanline = m_imgDiff[pane].getScanLine(h - y - 1); + unsigned bsx = (w - bx * m_diffBlockSize < m_diffBlockSize) ? (w - bx * m_diffBlockSize) : m_diffBlockSize; + for (unsigned j = 0; j < bsx; ++j) + { + unsigned x = bx * m_diffBlockSize + j; + scanline[x * 4 + 0] = static_cast(scanline[x * 4 + 0] * (1 - m_diffColorAlpha) + GetBValue(color) * m_diffColorAlpha); + scanline[x * 4 + 1] = static_cast(scanline[x * 4 + 1] * (1 - m_diffColorAlpha) + GetGValue(color) * m_diffColorAlpha); + scanline[x * 4 + 2] = static_cast(scanline[x * 4 + 2] * (1 - m_diffColorAlpha) + GetRValue(color) * m_diffColorAlpha); + } + } + } + } + } + } + + void CopyOriginalImageToDiffImage(int dst) + { + unsigned w = m_imgOrig32[dst].getWidth(); + unsigned h = m_imgOrig32[dst].getHeight(); + for (unsigned y = 0; y < h; ++y) + { + const BYTE *scanline_src = m_imgOrig32[dst].getScanLine(h - y - 1); + BYTE *scanline_dst = m_imgDiff[dst].getScanLine(m_imgDiff[dst].getHeight() - y - 1); + for (unsigned x = 0; x < w; ++x) + { + scanline_dst[x * 4 + 0] = scanline_src[x * 4 + 0]; + scanline_dst[x * 4 + 1] = scanline_src[x * 4 + 1]; + scanline_dst[x * 4 + 2] = scanline_src[x * 4 + 2]; + scanline_dst[x * 4 + 3] = scanline_src[x * 4 + 3]; + } + } + } + + void XorImages2(int src, int dst) + { + unsigned w = m_imgOrig32[src].getWidth(); + unsigned h = m_imgOrig32[src].getHeight(); + for (unsigned y = 0; y < h; ++y) + { + const BYTE *scanline_src = m_imgOrig32[src].getScanLine(h - y - 1); + BYTE *scanline_dst = m_imgDiff[dst].getScanLine(m_imgDiff[dst].getHeight() - y - 1); + for (unsigned x = 0; x < w; ++x) + { + scanline_dst[x * 4 + 0] ^= scanline_src[x * 4 + 0]; + scanline_dst[x * 4 + 1] ^= scanline_src[x * 4 + 1]; + scanline_dst[x * 4 + 2] ^= scanline_src[x * 4 + 2]; + } + } + } + + void AlphaBlendImages2(int src, int dst) + { + unsigned w = m_imgOrig32[src].getWidth(); + unsigned h = m_imgOrig32[src].getHeight(); + for (unsigned y = 0; y < h; ++y) + { + const BYTE *scanline_src = m_imgOrig32[src].getScanLine(h - y - 1); + BYTE *scanline_dst = m_imgDiff[dst].getScanLine(m_imgDiff[dst].getHeight() - y - 1); + for (unsigned x = 0; x < w; ++x) + { + scanline_dst[x * 4 + 0] = static_cast(scanline_dst[x * 4 + 0] * (1 - m_overlayAlpha) + scanline_src[x * 4 + 0] * m_overlayAlpha); + scanline_dst[x * 4 + 1] = static_cast(scanline_dst[x * 4 + 1] * (1 - m_overlayAlpha) + scanline_src[x * 4 + 1] * m_overlayAlpha); + scanline_dst[x * 4 + 2] = static_cast(scanline_dst[x * 4 + 2] * (1 - m_overlayAlpha) + scanline_src[x * 4 + 2] * m_overlayAlpha); + scanline_dst[x * 4 + 3] = static_cast(scanline_dst[x * 4 + 3] * (1 - m_overlayAlpha) + scanline_src[x * 4 + 3] * m_overlayAlpha); + } + } + } + + int m_nImages; + fipMultiPageEx m_imgOrigMultiPage[3]; + fipImageEx m_imgOrig[3]; + fipImageEx m_imgOrig32[3]; + fipWinImage m_imgDiff[3]; + std::wstring m_filename[3]; + bool m_bRO[3]; + IImgMergeWindow::OVERLAY_MODE m_overlayMode; + bool m_showDifferences; + double m_overlayAlpha; + unsigned m_diffBlockSize; + COLORREF m_selDiffColor; + COLORREF m_diffColor; + double m_diffColorAlpha; + double m_colorDistanceThreshold; + int m_currentPage[3]; + int m_currentDiffIndex; + int m_diffCount; + Array2D m_diff, m_diff01, m_diff21, m_diff02; + std::vector m_diffInfos; + UndoRecords m_undoRecords; +}; + diff --git a/src/CImgMergeWindow.hpp b/src/CImgMergeWindow.hpp index 7c9da1012..b25facea5 100644 --- a/src/CImgMergeWindow.hpp +++ b/src/CImgMergeWindow.hpp @@ -18,385 +18,11 @@ #pragma once #include -#include -#include -#include -#include -#include #include "FreeImagePlus.h" #include "CImgWindow.hpp" +#include "CImgMergeBuffer.hpp" #include "WinIMergeLib.h" -template struct Point -{ - Point(T x, T y): x(x), y(y) {} - T x, y; -}; - -template struct Size -{ - Size(T cx, T cy): cx(cx), cy(cy) {} - T cx, cy; -}; - -template struct Rect -{ - Rect(T left, T top, T right, T bottom): left(left), top(top), right(right), bottom(bottom) {} - T left, top, right, bottom; -}; - -template struct Array2D -{ - Array2D() : m_width(0), m_height(0), m_data(NULL) - { - } - - Array2D(size_t width, size_t height) : m_width(width), m_height(height), m_data(new T[width * height]) - { - memset(m_data, 0, m_width * m_height * sizeof(T)); - } - - Array2D(const Array2D& other) : m_width(other.m_width), m_height(other.m_height), m_data(new T[other.m_width * other.m_height]) - { - memcpy(m_data, other.m_data, m_width * m_height * sizeof(T)); - } - - Array2D& operator=(const Array2D& other) - { - if (this != &other) - { - delete[] m_data; - m_width = other.m_width; - m_height = other.m_height; - m_data = new T[other.m_width * other.m_height]; - memcpy(m_data, other.m_data, m_width * m_height * sizeof(T)); - } - return *this; - } - - ~Array2D() - { - delete[] m_data; - } - - void resize(size_t width, size_t height) - { - delete[] m_data; - m_data = new T[width * height]; - m_width = width; - m_height = height; - memset(m_data, 0, sizeof(T) * width * height); - } - - T& operator()(int x, int y) - { - return m_data[y * m_width + x]; - } - - const T& operator()(int x, int y) const - { - return m_data[y * m_width + x]; - } - - void clear() - { - delete[] m_data; - m_data = NULL; - m_width = 0; - m_height = 0; - } - - size_t height() const - { - return m_height; - } - - size_t width() const - { - return m_width; - } - - size_t m_width, m_height; - T* m_data; -}; - -struct DiffInfo -{ - enum OP_TYPE - { - OP_NONE = 0, OP_1STONLY, OP_2NDONLY, OP_3RDONLY, OP_DIFF, OP_TRIVIAL - }; - DiffInfo(int op, int x, int y) : op(op), rc(x, y, x + 1, y + 1) {} - int op; - Rect rc; -}; - -struct UndoRecord -{ - UndoRecord(int pane, FIBITMAP *oldbitmap, FIBITMAP *newbitmap, const int modcountnew[3]) : - pane(pane), oldbitmap(oldbitmap), newbitmap(newbitmap) - { - for (int i = 0; i < 3; ++i) - modcount[i] = modcountnew[i]; - } - int pane; - int modcount[3]; - FIBITMAP *oldbitmap, *newbitmap; -}; - -struct UndoRecords -{ - UndoRecords() : m_currentUndoBufIndex(-1) - { - clear(); - } - - ~UndoRecords() - { - clear(); - } - - void push_back(int pane, FIBITMAP *oldbitmap, FIBITMAP *newbitmap) - { - ++m_currentUndoBufIndex; - while (m_currentUndoBufIndex < static_cast(m_undoBuf.size())) - { - --m_modcount[m_undoBuf.back().pane]; - FreeImage_Unload(m_undoBuf.back().newbitmap); - FreeImage_Unload(m_undoBuf.back().oldbitmap); - m_undoBuf.pop_back(); - } - ++m_modcount[pane]; - m_undoBuf.push_back(UndoRecord(pane, oldbitmap, newbitmap, m_modcount)); - } - - const UndoRecord& undo() - { - if (m_currentUndoBufIndex < 0) - throw "no undoable"; - const UndoRecord& rec = m_undoBuf[m_currentUndoBufIndex]; - --m_currentUndoBufIndex; - return rec; - } - - const UndoRecord& redo() - { - if (m_currentUndoBufIndex >= static_cast(m_undoBuf.size()) - 1) - throw "no redoable"; - ++m_currentUndoBufIndex; - const UndoRecord& rec = m_undoBuf[m_currentUndoBufIndex]; - return rec; - } - - bool is_modified(int pane) const - { - if (m_currentUndoBufIndex < 0) - return (m_modcountonsave[pane] != 0); - else - return (m_modcountonsave[pane] != m_undoBuf[m_currentUndoBufIndex].modcount[pane]); - } - - void save(int pane) - { - if (m_currentUndoBufIndex < 0) - m_modcountonsave[pane] = 0; - else - m_modcountonsave[pane] = m_undoBuf[m_currentUndoBufIndex].modcount[pane]; - } - - bool undoable() const - { - return (m_currentUndoBufIndex >= 0); - } - - bool redoable() const - { - return (m_currentUndoBufIndex < static_cast(m_undoBuf.size()) - 1); - } - - void clear() - { - m_currentUndoBufIndex = -1; - for (int i = 0; i < 3; ++i) - { - m_modcount[i] = 0; - m_modcountonsave[i] = 0; - } - while (!m_undoBuf.empty()) - { - FreeImage_Unload(m_undoBuf.back().newbitmap); - FreeImage_Unload(m_undoBuf.back().oldbitmap); - m_undoBuf.pop_back(); - } - } - - std::vector m_undoBuf; - int m_currentUndoBufIndex; - int m_modcount[3]; - int m_modcountonsave[3]; -}; - -class fipImageEx : public fipImage -{ -public: - fipImageEx(FREE_IMAGE_TYPE image_type = FIT_BITMAP, unsigned width = 0, unsigned height = 0, unsigned bpp = 0) : - fipImage(image_type, width, height, bpp) {} - fipImageEx(const fipImageEx& Image) : fipImage(Image) {} - virtual ~fipImageEx() {} - - fipImageEx& operator=(const fipImageEx& Image) - { - if (this != &Image) - { - FIBITMAP *clone = FreeImage_Clone((FIBITMAP*)Image._dib); - replace(clone); - } - return *this; - } - - fipImageEx& operator=(FIBITMAP *dib) - { - if (_dib != dib) - replace(dib); - return *this; - } - - void swap(fipImageEx& other) - { - std::swap(_dib, other._dib); - std::swap(this->_fif, other._fif); - std::swap(this->_bHasChanged, other._bHasChanged); - } - - FIBITMAP *detach() - { - FIBITMAP *dib = _dib; - _dib = NULL; - clear(); - return dib; - } - - BOOL colorQuantizeEx(FREE_IMAGE_QUANTIZE quantize = FIQ_WUQUANT, int PaletteSize = 256, int ReserveSize = 0, RGBQUAD *ReservePalette = NULL) - { - if(_dib) { - FIBITMAP *dib8 = FreeImage_ColorQuantizeEx(_dib, quantize, PaletteSize, ReserveSize, ReservePalette); - return !!replace(dib8); - } - return false; - } - - bool convertColorDepth(unsigned bpp, RGBQUAD *pPalette = NULL) - { - switch (bpp) - { - case 1: - return !!threshold(128); - case 4: - { - fipImageEx tmp = *this; - tmp.convertTo24Bits(); - if (pPalette) - tmp.colorQuantizeEx(FIQ_NNQUANT, 16, 16, pPalette); - else - tmp.colorQuantizeEx(FIQ_WUQUANT, 16); - setSize(tmp.getImageType(), tmp.getWidth(), tmp.getHeight(), 4); - for (unsigned y = 0; y < tmp.getHeight(); ++y) - { - const BYTE *line_src = tmp.getScanLine(y); - BYTE *line_dst = getScanLine(y); - for (unsigned x = 0; x < tmp.getWidth(); ++x) - line_dst[x / 2] |= ((x % 2) == 0) ? (line_src[x] << 4) : line_src[x]; - } - - RGBQUAD *rgbq_dst = getPalette(); - RGBQUAD *rgbq_src = pPalette ? pPalette : tmp.getPalette(); - memcpy(rgbq_dst, rgbq_src, sizeof(RGBQUAD) * 16); - return true; - } - case 8: - convertTo24Bits(); - if (pPalette) - return !!colorQuantizeEx(FIQ_NNQUANT, 256, 256, pPalette); - else - return !!colorQuantizeEx(FIQ_WUQUANT, 256); - case 15: - return !!convertTo16Bits555(); - case 16: - return !!convertTo16Bits565(); - case 24: - return !!convertTo24Bits(); - default: - case 32: - return !!convertTo32Bits(); - } - } - - void copyAnimationMetadata(fipImage& src) - { - fipTag tag; - fipMetadataFind finder; - if (finder.findFirstMetadata(FIMD_ANIMATION, src, tag)) - { - do - { - setMetadata(FIMD_ANIMATION, tag.getKey(), tag); - } while (finder.findNextMetadata(tag)); - } - } -}; - -class fipMultiPageEx : public fipMultiPage -{ -public: - fipMultiPageEx(BOOL keep_cache_in_memory = FALSE) : fipMultiPage(keep_cache_in_memory) {} - - bool saveU(const wchar_t* lpszPathName, int flag = 0) const - { - FILE *fp = NULL; - _wfopen_s(&fp, lpszPathName, L"r+b"); - if (!fp) - return false; - FreeImageIO io; - io.read_proc = myReadProc; - io.write_proc = myWriteProc; - io.seek_proc = mySeekProc; - io.tell_proc = myTellProc; - FREE_IMAGE_FORMAT fif = fipImage::identifyFIFU(lpszPathName); - bool result = !!saveToHandle(fif, &io, (fi_handle)fp, flag); - fclose(fp); - return result; - } - -private: - static unsigned DLL_CALLCONV myReadProc(void *buffer, unsigned size, unsigned count, fi_handle handle) { - return (unsigned)fread(buffer, size, count, (FILE *)handle); - } - - static unsigned DLL_CALLCONV myWriteProc(void *buffer, unsigned size, unsigned count, fi_handle handle) { - return (unsigned)fwrite(buffer, size, count, (FILE *)handle); - } - - static int DLL_CALLCONV mySeekProc(fi_handle handle, long offset, int origin) { - return fseek((FILE *)handle, offset, origin); - } - - static long DLL_CALLCONV myTellProc(fi_handle handle) { - return ftell((FILE *)handle); - } -}; - -namespace -{ - int GetColorDistance2(RGBQUAD c1, RGBQUAD c2) - { - int rdist = c1.rgbRed - c2.rgbRed; - int gdist = c1.rgbGreen - c2.rgbGreen; - int bdist = c1.rgbBlue - c2.rgbBlue; - int adist = c1.rgbReserved - c2.rgbReserved; - return rdist * rdist + gdist * gdist + bdist * bdist + adist * adist; - } -} - class CImgMergeWindow : public IImgMergeWindow { struct EventListenerInfo @@ -413,25 +39,11 @@ public: , m_hInstance(NULL) , m_nDraggingSplitter(-1) , m_bHorizontalSplit(false) - , m_diffBlockSize(8) - , m_overlayMode(OVERLAY_NONE) - , m_overlayAlpha(0.3) - , m_showDifferences(true) - , m_selDiffColor(RGB(0xff, 0x40, 0x40)) - , m_diffColor(RGB(0xff, 0xff, 0x40)) - , m_diffColorAlpha(0.7) - , m_diffCount(0) - , m_currentDiffIndex(-1) , m_oldSplitPosX(-4) , m_oldSplitPosY(-4) - , m_colorDistanceThreshold(0.0) { for (int i = 0; i < 3; ++i) - { m_ChildWndProc[i] = NULL; - m_currentPage[i] = 0; - m_bRO[i] = false; - } } ~CImgMergeWindow() @@ -461,9 +73,7 @@ public: const wchar_t *GetFileName(int pane) { - if (pane < 0 || pane >= m_nImages) - return NULL; - return m_filename[pane].c_str(); + return m_buffer.GetFileName(pane); } int GetPaneCount() const @@ -517,15 +127,12 @@ public: RGBQUAD GetPixelColor(int pane, int x, int y) const { - RGBQUAD value = {0xFF, 0xFF, 0xFF, 0x00}; - m_imgOrig32[pane].getPixelColor(x, m_imgOrig32[pane].getHeight() - y - 1, &value); - return value; + return m_buffer.GetPixelColor(pane, x, y); } double GetColorDistance(int pane1, int pane2, int x, int y) const { - return std::sqrt(static_cast( - ::GetColorDistance2(GetPixelColor(pane1, x, y), GetPixelColor(pane2, x, y)) )); + return m_buffer.GetColorDistance(pane1, pane2, x, y); } int GetActivePane() const @@ -545,16 +152,12 @@ public: bool GetReadOnly(int pane) const { - if (pane < 0 || pane >= m_nImages) - return true; - return m_bRO[pane]; + return m_buffer.GetReadOnly(pane); } void SetReadOnly(int pane, bool readOnly) { - if (pane < 0 || pane >= m_nImages) - return; - m_bRO[pane] = readOnly; + m_buffer.SetReadOnly(pane, readOnly); } bool GetHorizontalSplit() const @@ -572,35 +175,35 @@ public: COLORREF GetDiffColor() const { - return m_diffColor; + return m_buffer.GetDiffColor(); } void SetDiffColor(COLORREF clrDiffColor) { - m_diffColor = clrDiffColor; - RefreshImages(); + m_buffer.SetDiffColor(clrDiffColor); + Invalidate(); } COLORREF GetSelDiffColor() const { - return m_selDiffColor; + return m_buffer.GetSelDiffColor(); } void SetSelDiffColor(COLORREF clrSelDiffColor) { - m_selDiffColor = clrSelDiffColor; - RefreshImages(); + m_buffer.SetSelDiffColor(clrSelDiffColor); + Invalidate(); } double GetDiffColorAlpha() const { - return m_diffColorAlpha; + return m_buffer.GetDiffColorAlpha(); } void SetDiffColorAlpha(double diffColorAlpha) { - m_diffColorAlpha = diffColorAlpha; - RefreshImages(); + m_buffer.SetDiffColorAlpha(diffColorAlpha); + Invalidate(); } RGBQUAD GetBackColor() const @@ -638,556 +241,282 @@ public: int GetCurrentPage(int pane) const { - if (pane < 0 || pane >= m_nImages) - return -1; - return m_currentPage[pane]; + return m_buffer.GetCurrentPage(pane); } void SetCurrentPage(int pane, int page) { - if (page >= 0 && page < GetPageCount(pane)) - { - if (m_imgOrigMultiPage[pane].isValid()) - { - m_currentPage[pane] = page; - FIBITMAP *bitmap = m_imgOrigMultiPage[pane].lockPage(page); - m_imgOrig[pane] = FreeImage_Clone(bitmap); - m_imgOrig32[pane] = m_imgOrig[pane]; - FreeImage_UnlockPage(m_imgOrigMultiPage[pane], bitmap, false); - m_imgOrig32[pane].convertTo32Bits(); - CompareImages(); - } - } + m_buffer.SetCurrentPage(pane, page); + Invalidate(); } void SetCurrentPageAll(int page) { - for (int i = 0; i < m_nImages; ++i) - SetCurrentPage(i, page); + m_buffer.SetCurrentPageAll(page); + Invalidate(); } int GetCurrentMaxPage() const { - int maxpage = 0; - for (int i = 0; i < m_nImages; ++i) - { - int page = GetCurrentPage(i); - maxpage = maxpage < page ? page : maxpage; - } - return maxpage; + return m_buffer.GetCurrentMaxPage(); } int GetPageCount(int pane) const { - if (pane < 0 || pane >= m_nImages) - return -1; - if (m_imgOrigMultiPage[pane].isValid()) - return m_imgOrigMultiPage[pane].getPageCount(); - else - return 1; + return m_buffer.GetPageCount(pane); } int GetMaxPageCount() const { - int maxpage = 0; - for (int i = 0; i < m_nImages; ++i) - { - int page = GetPageCount(i); - maxpage = page > maxpage ? page : maxpage; - } - return maxpage; + return m_buffer.GetMaxPageCount(); } double GetColorDistanceThreshold() const { - return m_colorDistanceThreshold; + return m_buffer.GetColorDistanceThreshold(); } void SetColorDistanceThreshold(double threshold) { - m_colorDistanceThreshold = threshold; - CompareImages(); + m_buffer.SetColorDistanceThreshold(threshold); + Invalidate(); } int GetDiffBlockSize() const { - return m_diffBlockSize; + return m_buffer.GetDiffBlockSize(); } void SetDiffBlockSize(int blockSize) { - m_diffBlockSize = blockSize; - CompareImages(); + m_buffer.SetDiffBlockSize(blockSize); + Invalidate(); } OVERLAY_MODE GetOverlayMode() const { - return m_overlayMode; + return m_buffer.GetOverlayMode(); } void SetOverlayMode(OVERLAY_MODE overlayMode) { - m_overlayMode = overlayMode; - RefreshImages(); + m_buffer.SetOverlayMode(overlayMode); + Invalidate(); } double GetOverlayAlpha() const { - return m_overlayAlpha; + return m_buffer.GetOverlayAlpha(); } void SetOverlayAlpha(double overlayAlpha) { - m_overlayAlpha = overlayAlpha; - RefreshImages(); + m_buffer.SetOverlayAlpha(overlayAlpha); + Invalidate(); } bool GetShowDifferences() const { - return m_showDifferences; + return m_buffer.GetShowDifferences(); } void SetShowDifferences(bool visible) { - m_showDifferences = visible; - CompareImages(); + m_buffer.SetShowDifferences(visible); + Invalidate(); } int GetDiffCount() const { - return m_diffCount; + return m_buffer.GetDiffCount(); } int GetConflictCount() const { - int conflictCount = 0; - for (int i = 0; i < m_diffCount; ++i) - if (m_diffInfos[i].op == DiffInfo::OP_DIFF) - ++conflictCount; - return conflictCount; + return m_buffer.GetConflictCount(); } int GetCurrentDiffIndex() const { - return m_currentDiffIndex; + return m_buffer.GetCurrentDiffIndex(); } bool FirstDiff() { - if (m_diffCount == 0) - m_currentDiffIndex = -1; - else - m_currentDiffIndex = 0; - RefreshImages(); - ScrollToDiff(m_currentDiffIndex); - return true; + bool result = m_buffer.FirstDiff(); + if (result) + ScrollToDiff(m_buffer.GetCurrentDiffIndex()); + return result; } bool LastDiff() { - m_currentDiffIndex = m_diffCount - 1; - RefreshImages(); - ScrollToDiff(m_currentDiffIndex); - return true; + bool result = m_buffer.LastDiff(); + if (result) + ScrollToDiff(m_buffer.GetCurrentDiffIndex()); + return result; } bool NextDiff() { - ++m_currentDiffIndex; - if (m_currentDiffIndex >= m_diffCount) - m_currentDiffIndex = m_diffCount - 1; - RefreshImages(); - ScrollToDiff(m_currentDiffIndex); - return true; + bool result = m_buffer.NextDiff(); + if (result) + ScrollToDiff(m_buffer.GetCurrentDiffIndex()); + return result; } bool PrevDiff() { - if (m_diffCount == 0) - m_currentDiffIndex = -1; - else - { - --m_currentDiffIndex; - if (m_currentDiffIndex < 0) - m_currentDiffIndex = 0; - } - RefreshImages(); - ScrollToDiff(m_currentDiffIndex); - return true; + bool result = m_buffer.PrevDiff(); + if (result) + ScrollToDiff(m_buffer.GetCurrentDiffIndex()); + return result; } bool FirstConflict() { - for (int i = 0; i < m_diffInfos.size(); ++i) - if (m_diffInfos[i].op == DiffInfo::OP_DIFF) - m_currentDiffIndex = i; - RefreshImages(); - ScrollToDiff(m_currentDiffIndex); - return true; + bool result = m_buffer.FirstConflict(); + if (result) + ScrollToDiff(m_buffer.GetCurrentDiffIndex()); + return result; } bool LastConflict() { - for (int i = static_cast(m_diffInfos.size() - 1); i >= 0; --i) - { - if (m_diffInfos[i].op == DiffInfo::OP_DIFF) - { - m_currentDiffIndex = i; - break; - } - } - RefreshImages(); - ScrollToDiff(m_currentDiffIndex); - return true; + bool result = m_buffer.LastConflict(); + if (result) + ScrollToDiff(m_buffer.GetCurrentDiffIndex()); + return result; } bool NextConflict() { - for (size_t i = m_currentDiffIndex + 1; i < m_diffInfos.size(); ++i) - { - if (m_diffInfos[i].op == DiffInfo::OP_DIFF) - { - m_currentDiffIndex = static_cast(i); - break; - } - } - RefreshImages(); - ScrollToDiff(m_currentDiffIndex); - return true; + bool result = m_buffer.NextConflict(); + if (result) + ScrollToDiff(m_buffer.GetCurrentDiffIndex()); + return result; } bool PrevConflict() { - for (int i = m_currentDiffIndex - 1; i >= 0; --i) - { - if (m_diffInfos[i].op == DiffInfo::OP_DIFF) - { - m_currentDiffIndex = i; - break; - } - } - RefreshImages(); - ScrollToDiff(m_currentDiffIndex); - return true; + bool result = m_buffer.PrevConflict(); + if (result) + ScrollToDiff(m_buffer.GetCurrentDiffIndex()); + return result; } bool SelectDiff(int diffIndex) { - m_currentDiffIndex = diffIndex; - RefreshImages(); - ScrollToDiff(m_currentDiffIndex); - return true; + bool result = m_buffer.SelectDiff(diffIndex); + if (result) + ScrollToDiff(m_buffer.GetCurrentDiffIndex()); + return result; } int GetNextDiffIndex() const { - if (m_diffCount == 0 || m_currentDiffIndex >= m_diffCount - 1) - return -1; - return m_currentDiffIndex + 1; + return m_buffer.GetNextDiffIndex(); } int GetPrevDiffIndex() const { - if (m_diffCount == 0 || m_currentDiffIndex <= 0) - return -1; - return m_currentDiffIndex - 1; + return m_buffer.GetPrevDiffIndex(); } int GetNextConflictIndex() const { - for (size_t i = m_currentDiffIndex + 1; i < m_diffInfos.size(); ++i) - if (m_diffInfos[i].op == DiffInfo::OP_DIFF) - return static_cast(i); - return -1; + return m_buffer.GetNextConflictIndex(); } int GetPrevConflictIndex() const { - for (int i = static_cast(m_currentDiffIndex - 1); i >= 0; --i) - if (m_diffInfos[i].op == DiffInfo::OP_DIFF) - return i; - return -1; - } - - void CopyDiffInternal(int diffIndex, int srcPane, int dstPane) - { - if (srcPane < 0 || srcPane >= m_nImages) - return; - if (dstPane < 0 || dstPane >= m_nImages) - return; - if (diffIndex < 0 || diffIndex >= m_diffCount) - return; - if (m_bRO[dstPane]) - return; - - const Rect& rc = m_diffInfos[diffIndex].rc; - unsigned wsrc = m_imgOrig32[srcPane].getWidth(); - unsigned hsrc = m_imgOrig32[srcPane].getHeight(); - unsigned wdst = m_imgOrig32[dstPane].getWidth(); - unsigned hdst = m_imgOrig32[dstPane].getHeight(); - if (rc.right * m_diffBlockSize > wdst) - { - if ((std::max)(wsrc, wdst) < rc.right * m_diffBlockSize) - wdst = (std::max)(wsrc, wdst); - else - wdst = rc.right * m_diffBlockSize; - } - if (rc.bottom * m_diffBlockSize > hdst) - { - if ((std::max)(hsrc, hdst) < rc.bottom * m_diffBlockSize) - hdst = (std::max)(hsrc, hdst); - else - hdst = rc.bottom * m_diffBlockSize; - } - if (rc.right * m_diffBlockSize > wsrc) - wdst = wsrc; - if (rc.bottom * m_diffBlockSize > hsrc) - hdst = hsrc; - if (wdst != m_imgOrig32[dstPane].getWidth() || hdst != m_imgOrig32[dstPane].getHeight()) - { - fipImage imgTemp = m_imgOrig32[srcPane]; - m_imgOrig32[dstPane].setSize(imgTemp.getImageType(), wdst, hdst, imgTemp.getBitsPerPixel()); - m_imgOrig32[dstPane].pasteSubImage(imgTemp, 0, 0); - } - - for (unsigned y = rc.top * m_diffBlockSize; y < rc.bottom * m_diffBlockSize; y += m_diffBlockSize) - { - for (unsigned x = rc.left * m_diffBlockSize; x < rc.right * m_diffBlockSize; x += m_diffBlockSize) - { - if (m_diff(x / m_diffBlockSize, y / m_diffBlockSize) == diffIndex + 1) - { - int sizex = ((x + m_diffBlockSize) < wsrc) ? m_diffBlockSize : (wsrc - x); - int sizey = ((y + m_diffBlockSize) < hsrc) ? m_diffBlockSize : (hsrc - y); - if (sizex > 0 && sizey > 0) - { - for (int i = 0; i < sizey; ++i) - { - const BYTE *scanline_src = m_imgOrig32[srcPane].getScanLine(hsrc - (y + i) - 1); - BYTE *scanline_dst = m_imgOrig32[dstPane].getScanLine(hdst - (y + i) - 1); - memcpy(&scanline_dst[x * 4], &scanline_src[x * 4], sizex * 4); - } - } - } - } - } + return m_buffer.GetPrevConflictIndex(); } void CopyDiff(int diffIndex, int srcPane, int dstPane) { - if (srcPane < 0 || srcPane >= m_nImages) - return; - if (dstPane < 0 || dstPane >= m_nImages) - return; - if (diffIndex < 0 || diffIndex >= m_diffCount) - return; - if (m_bRO[dstPane]) - return; - if (srcPane == dstPane) - return; - - FIBITMAP *oldbitmap = FreeImage_Clone(m_imgOrig32[dstPane]); - - CopyDiffInternal(diffIndex, srcPane, dstPane); - - FIBITMAP *newbitmap = FreeImage_Clone(m_imgOrig32[dstPane]); - m_undoRecords.push_back(dstPane, oldbitmap, newbitmap); - CompareImages(); + m_buffer.CopyDiff(diffIndex, srcPane, dstPane); + Invalidate(); } void CopyDiffAll(int srcPane, int dstPane) { - if (srcPane < 0 || srcPane >= m_nImages) - return; - if (dstPane < 0 || dstPane >= m_nImages) - return; - if (m_bRO[dstPane]) - return; - if (srcPane == dstPane) - return; - - FIBITMAP *oldbitmap = FreeImage_Clone(m_imgOrig32[dstPane]); - - for (int diffIndex = 0; diffIndex < m_diffCount; ++diffIndex) - CopyDiffInternal(diffIndex, srcPane, dstPane); - - FIBITMAP *newbitmap = FreeImage_Clone(m_imgOrig32[dstPane]); - m_undoRecords.push_back(dstPane, oldbitmap, newbitmap); - CompareImages(); + m_buffer.CopyDiffAll(srcPane, dstPane); + Invalidate(); } int CopyDiff3Way(int dstPane) { - if (dstPane < 0 || dstPane >= m_nImages) - return 0; - if (m_bRO[dstPane]) - return 0; - - FIBITMAP *oldbitmap = FreeImage_Clone(m_imgOrig32[dstPane]); - - int nMerged = 0; - for (int diffIndex = 0; diffIndex < m_diffCount; ++diffIndex) - { - int srcPane; - switch (m_diffInfos[diffIndex].op) - { - case DiffInfo::OP_1STONLY: - if (dstPane == 1) - srcPane = 0; - else - srcPane = -1; - break; - case DiffInfo::OP_2NDONLY: - if (dstPane != 1) - srcPane = 1; - else - srcPane = -1; - break; - case DiffInfo::OP_3RDONLY: - if (dstPane == 1) - srcPane = 2; - else - srcPane = -1; - break; - case DiffInfo::OP_DIFF: - srcPane = -1; - break; - } - - if (srcPane >= 0) - { - CopyDiffInternal(diffIndex, srcPane, dstPane); - ++nMerged; - } - } - - FIBITMAP *newbitmap = FreeImage_Clone(m_imgOrig32[dstPane]); - m_undoRecords.push_back(dstPane, oldbitmap, newbitmap); - CompareImages(); - - return nMerged; + int result = m_buffer.CopyDiff3Way(dstPane); + Invalidate(); + return result; } bool IsModified(int pane) const { - return m_undoRecords.is_modified(pane); + return m_buffer.IsModified(pane); } bool IsUndoable() const { - return m_undoRecords.undoable(); + return m_buffer.IsUndoable(); } bool IsRedoable() const { - return m_undoRecords.redoable(); + return m_buffer.IsRedoable(); } bool Undo() { - if (!m_undoRecords.undoable()) - return false; - const UndoRecord& rec = m_undoRecords.undo(); - m_imgOrig32[rec.pane] = FreeImage_Clone(rec.oldbitmap); - CompareImages(); - return true; + bool result = m_buffer.Undo(); + if (result) + Invalidate(); + return result; } bool Redo() { - if (!m_undoRecords.redoable()) - return false; - const UndoRecord& rec = m_undoRecords.redo(); - m_imgOrig32[rec.pane] = FreeImage_Clone(rec.newbitmap); - CompareImages(); - return true; - } - - void CompareImages() - { - if (m_nImages <= 1) - return; - InitializeDiff(); - if (m_nImages == 2) - { - CompareImages2(0, 1, m_diff); - m_diffCount = MarkDiffIndex(m_diff); - } - else if (m_nImages == 3) - { - CompareImages2(0, 1, m_diff01); - CompareImages2(2, 1, m_diff21); - CompareImages2(0, 2, m_diff02); - Make3WayDiff(m_diff01, m_diff21, m_diff); - m_diffCount = MarkDiffIndex3way(m_diff01, m_diff21, m_diff02, m_diff); - } - if (m_currentDiffIndex >= m_diffCount) - m_currentDiffIndex = m_diffCount - 1; - RefreshImages(); + bool result = m_buffer.Redo(); + if (result) + Invalidate(); + return result; } void ScrollToDiff(int diffIndex) { - if (diffIndex >= 0 && diffIndex < static_cast(m_diffInfos.size())) + if (diffIndex >= 0 && diffIndex < m_buffer.GetDiffCount()) { + Rect rc = m_buffer.GetDiffInfo(diffIndex)->rc; for (int i = 0; i < m_nImages; ++i) - m_imgWindow[i].ScrollTo(m_diffInfos[diffIndex].rc.left * m_diffBlockSize, m_diffInfos[diffIndex].rc.top * m_diffBlockSize); + m_imgWindow[i].ScrollTo(rc.left * m_buffer.GetDiffBlockSize(), rc.top * m_buffer.GetDiffBlockSize()); } } - void RefreshImages() + void Invalidate() { if (m_nImages <= 1) return; - InitializeDiffImages(); - for (int i = 0; i < m_nImages; ++i) - CopyOriginalImageToDiffImage(i); - void (CImgMergeWindow::*func)(int src, int dst) = NULL; - if (m_overlayMode == OVERLAY_ALPHABLEND) - func = &CImgMergeWindow::AlphaBlendImages2; - else if (m_overlayMode == OVERLAY_XOR) - func = &CImgMergeWindow::XorImages2; - if (func) - { - if (m_nImages == 2) - { - (this->*func)(1, 0); - (this->*func)(0, 1); - } - else if (m_nImages == 3) - { - (this->*func)(1, 0); - (this->*func)(0, 1); - (this->*func)(2, 1); - (this->*func)(1, 2); - } - } - if (m_showDifferences) - { - for (int i = 0; i < m_nImages; ++i) - MarkDiff(i, m_diff); - } for (int i = 0; i < m_nImages; ++i) m_imgWindow[i].Invalidate(); } - bool OpenImages(int nImages, const wchar_t *filename[3]) + bool OpenImages(int nImages, const wchar_t * const filename[3]) { CloseImages(); m_nImages = nImages; - for (int i = 0; i < nImages; ++i) - m_filename[i] = filename[i]; - bool bSucceeded = LoadImages(); + bool bSucceeded = m_buffer.OpenImages(nImages, filename); for (int i = 0; i < nImages; ++i) { m_imgWindow[i].Create(m_hInstance, m_hWnd); m_ChildWndProc[i] = (WNDPROC)SetWindowLongPtr(m_imgWindow[i].GetHWND(), GWLP_WNDPROC, (LONG_PTR)&ChildWndProc); } - CompareImages(); + m_buffer.CompareImages(); std::vector rects = CalcChildImgWindowRect(m_hWnd, nImages, m_bHorizontalSplit); for (int i = 0; i < nImages; ++i) { m_imgWindow[i].SetWindowRect(rects[i]); - m_imgWindow[i].SetImage(&m_imgDiff[i]); + m_imgWindow[i].SetImage(m_buffer.GetImage(i)); } return bSucceeded; } @@ -1207,79 +536,38 @@ public: bool ReloadImages() { if (m_nImages == 2) - return OpenImages(m_filename[0].c_str(), m_filename[1].c_str()); + return OpenImages(m_buffer.GetFileName(0), m_buffer.GetFileName(1)); else if (m_nImages == 3) - return OpenImages(m_filename[0].c_str(), m_filename[1].c_str(), m_filename[2].c_str()); + return OpenImages(m_buffer.GetFileName(0), m_buffer.GetFileName(1), m_buffer.GetFileName(2)); return false; } bool SaveImage(int pane) { - if (pane < 0 || pane >= m_nImages) - return false; - if (m_bRO[pane]) - return false; - if (!m_undoRecords.is_modified(pane)) - return true; - bool result = SaveImageAs(pane, m_filename[pane].c_str()); - if (result) - m_undoRecords.save(pane); - return result; + return m_buffer.SaveImage(pane); } bool SaveImages() { - for (int i = 0; i < m_nImages; ++i) - if (!SaveImage(i)) - return false; - return true; + return m_buffer.SaveImages(); } bool SaveImageAs(int pane, const wchar_t *filename) { - if (pane < 0 || pane >= m_nImages) - return false; - unsigned bpp = m_imgOrig[pane].getBitsPerPixel(); - RGBQUAD palette[256]; - if (m_imgOrig[pane].getPaletteSize() > 0) - memcpy(palette, m_imgOrig[pane].getPalette(), m_imgOrig[pane].getPaletteSize()); - m_imgOrig[pane] = m_imgOrig32[pane]; - m_imgOrig[pane].convertColorDepth(bpp, palette); - if (m_imgOrigMultiPage[pane].isValid()) - { - fipImageEx imgOrg, imgAdd; - imgAdd = m_imgOrig[pane]; - imgOrg = m_imgOrigMultiPage[pane].lockPage(m_currentPage[pane]); - imgAdd.copyAnimationMetadata(imgOrg); - m_imgOrigMultiPage[pane].unlockPage(imgOrg, false); - m_imgOrigMultiPage[pane].insertPage(m_currentPage[pane], imgAdd); - imgAdd.detach(); - m_imgOrigMultiPage[pane].deletePage(m_currentPage[pane] + 1); - return !!m_imgOrigMultiPage[pane].saveU(filename); - } - else - { - return !!m_imgOrig[pane].saveU(filename); - } + return m_buffer.SaveImageAs(pane, filename); } bool CloseImages() { + m_buffer.CloseImages(); for (int i = 0; i < m_nImages; ++i) - { m_imgWindow[i].Destroy(); - m_imgOrig[i].clear(); - m_imgOrig32[i].clear(); - m_undoRecords.clear(); - } return true; } bool SaveDiffImageAs(int pane, const wchar_t *filename) { - if (pane < 0 || pane >= m_nImages) - return false; - return !!m_imgDiff[pane].saveU(filename); + return m_buffer.SaveDiffImageAs(pane, filename); } HWND GetPaneHWND(int pane) const @@ -1296,23 +584,22 @@ public: int GetImageWidth(int pane) const { - if (pane < 0 || pane >= m_nImages) - return -1; - return m_imgOrig32[pane].getWidth(); + return m_buffer.GetImageWidth(pane); } int GetImageHeight(int pane) const { - if (pane < 0 || pane >= m_nImages) - return -1; - return m_imgOrig32[pane].getHeight(); + return m_buffer.GetImageHeight(pane); } int GetImageBitsPerPixel(int pane) const { - if (pane < 0 || pane >= m_nImages) - return -1; - return m_imgOrig[pane].getBitsPerPixel(); + return m_buffer.GetImageBitsPerPixel(pane); + } + + int GetDiffIndexFromPoint(int x, int y) const + { + return m_buffer.GetDiffIndexFromPoint(x, y); } private: @@ -1422,349 +709,6 @@ private: m_imgWindow[i].SetWindowRect(rc[i]); } - bool LoadImages() - { - bool bSucceeded = true; - for (int i = 0; i < m_nImages; ++i) - { - m_currentPage[i] = 0; - char szFileName[260]; - wsprintfA(szFileName, "%S", m_filename[i].c_str()); - m_imgOrigMultiPage[i].open(szFileName, FALSE, FALSE, 0); - if (m_imgOrigMultiPage[i].isValid()) - { - FIBITMAP *bitmap = m_imgOrigMultiPage[i].lockPage(m_currentPage[i]); - if (bitmap) - { - m_imgOrig[i] = FreeImage_Clone(bitmap); - m_imgOrig32[i] = m_imgOrig[i]; - FreeImage_UnlockPage(m_imgOrigMultiPage[i], bitmap, false); - } - else - m_imgOrigMultiPage[i].close(); - } - if (!m_imgOrigMultiPage[i].isValid()) - { - if (!m_imgOrig[i].loadU(m_filename[i].c_str())) - bSucceeded = false; - m_imgOrig32[i] = m_imgOrig[i]; - } - - m_imgOrig32[i].convertTo32Bits(); - } - return bSucceeded; - } - - Size GetMaxWidthHeight() - { - unsigned wmax = 0; - unsigned hmax = 0; - for (int i = 0; i < m_nImages; ++i) - { - wmax = (std::max)(wmax, m_imgOrig32[i].getWidth()); - hmax = (std::max)(hmax, m_imgOrig32[i].getHeight()); - } - return Size(wmax, hmax); - } - - void InitializeDiff() - { - Size size = GetMaxWidthHeight(); - int nBlocksX = (size.cx + m_diffBlockSize - 1) / m_diffBlockSize; - int nBlocksY = (size.cy + m_diffBlockSize - 1) / m_diffBlockSize; - - m_diff.clear(); - m_diff.resize(nBlocksX, nBlocksY); - if (m_nImages == 3) - { - m_diff01.clear(); - m_diff01.resize(nBlocksX, nBlocksY); - m_diff21.clear(); - m_diff21.resize(nBlocksX, nBlocksY); - m_diff02.clear(); - m_diff02.resize(nBlocksX, nBlocksY); - } - m_diffInfos.clear(); - } - - void InitializeDiffImages() - { - Size size = GetMaxWidthHeight(); - for (int i = 0; i < m_nImages; ++i) - m_imgDiff[i].setSize(FIT_BITMAP, size.cx, size.cy, 32); - } - - void CompareImages2(int pane1, int pane2, Array2D& diff) - { - unsigned w1 = m_imgOrig32[pane1].getWidth(); - unsigned h1 = m_imgOrig32[pane1].getHeight(); - unsigned w2 = m_imgOrig32[pane2].getWidth(); - unsigned h2 = m_imgOrig32[pane2].getHeight(); - - const unsigned wmax = (std::max)(w1, w2); - const unsigned hmax = (std::max)(h1, h2); - - for (unsigned by = 0; by < diff.height(); ++by) - { - unsigned bsy = (hmax - by * m_diffBlockSize) >= m_diffBlockSize ? m_diffBlockSize : (hmax - by * m_diffBlockSize); - for (unsigned i = 0; i < bsy; ++i) - { - unsigned y = by * m_diffBlockSize + i; - if (y >= h1 || y >= h2) - { - for (unsigned bx = 0; bx < wmax / m_diffBlockSize; ++bx) - diff(bx, by) = -1; - } - else - { - const BYTE *scanline1 = m_imgOrig32[pane1].getScanLine(h1 - y - 1); - const BYTE *scanline2 = m_imgOrig32[pane2].getScanLine(h2 - y - 1); - if (w1 == w2 && m_colorDistanceThreshold == 0.0) - { - if (memcmp(scanline1, scanline2, w1 * 4) == 0) - continue; - } - for (unsigned x = 0; x < wmax; ++x) - { - if (x >= w1 || x >= w2) - diff(x / m_diffBlockSize, by) = -1; - else - { - if (m_colorDistanceThreshold > 0.0) - { - int bdist = scanline1[x * 4 + 0] - scanline2[x * 4 + 0]; - int gdist = scanline1[x * 4 + 1] - scanline2[x * 4 + 1]; - int rdist = scanline1[x * 4 + 2] - scanline2[x * 4 + 2]; - int adist = scanline1[x * 4 + 3] - scanline2[x * 4 + 3]; - int colorDistance2 = rdist * rdist + gdist * gdist + bdist * bdist + adist * adist; - if (colorDistance2 > m_colorDistanceThreshold * m_colorDistanceThreshold) - diff(x / m_diffBlockSize, by) = -1; - } - else - { - if (scanline1[x * 4 + 0] != scanline2[x * 4 + 0] || - scanline1[x * 4 + 1] != scanline2[x * 4 + 1] || - scanline1[x * 4 + 2] != scanline2[x * 4 + 2] || - scanline1[x * 4 + 3] != scanline2[x * 4 + 3]) - { - diff(x / m_diffBlockSize, by) = -1; - } - } - } - } - } - } - } - } - - void FloodFill8Directions(Array2D& data, int x, int y, unsigned val) - { - std::vector > stack; - stack.push_back(Point(x, y)); - while (!stack.empty()) - { - const Point& pt = stack.back(); - const int x = pt.x; - const int y = pt.y; - stack.pop_back(); - if (data(x, y) != -1) - continue; - data(x, y) = val; - if (x + 1 < static_cast(data.width())) - { - stack.push_back(Point(x + 1, y)); - if (y + 1 < static_cast(data.height())) - stack.push_back(Point(x + 1, y + 1)); - if (y - 1 >= 0) - stack.push_back(Point(x + 1, y - 1)); - } - if (x - 1 >= 0) - { - stack.push_back(Point(x - 1, y)); - if (y + 1 < static_cast(data.height())) - stack.push_back(Point(x - 1, y + 1)); - if (y - 1 >= 0) - stack.push_back(Point(x - 1, y - 1)); - } - if (y + 1 < static_cast(data.height())) - stack.push_back(Point(x, y + 1)); - if (y - 1 >= 0) - stack.push_back(Point(x, y - 1)); - } - } - - int MarkDiffIndex(Array2D& diff) - { - int diffCount = 0; - for (unsigned by = 0; by < diff.height(); ++by) - { - for (unsigned bx = 0; bx < diff.width(); ++bx) - { - int idx = diff(bx, by); - if (idx == -1) - { - m_diffInfos.push_back(DiffInfo(DiffInfo::OP_DIFF, bx, by)); - ++diffCount; - FloodFill8Directions(diff, bx, by, diffCount); - } - else if (idx != 0) - { - Rect& rc = m_diffInfos[idx - 1].rc; - if (static_cast(bx) < rc.left) - rc.left = bx; - else if (static_cast(bx + 1) > rc.right) - rc.right = bx + 1; - if (static_cast(by) < rc.top) - rc.top = by; - else if (static_cast(by + 1) > rc.bottom) - rc.bottom = by + 1; - } - } - } - return diffCount; - } - - int MarkDiffIndex3way(Array2D& diff01, Array2D& diff21, Array2D& diff02, Array2D& diff3) - { - int diffCount = MarkDiffIndex(diff3); - std::vector> counter(m_diffInfos.size()); - for (unsigned by = 0; by < diff3.height(); ++by) - { - for (unsigned bx = 0; bx < diff3.width(); ++bx) - { - int diffIndex = diff3(bx, by); - if (diffIndex == 0) - continue; - --diffIndex; - if (diff21(bx, by) == 0) - ++counter[diffIndex][0]; - else if (diff02(bx, by) == 0) - ++counter[diffIndex][1]; - else if (diff01(bx, by) == 0) - ++counter[diffIndex][2]; - else - ++counter[diffIndex][3]; - } - } - - for (size_t i = 0; i < m_diffInfos.size(); ++i) - { - int op; - if (counter[i][0] != 0 && counter[i][1] == 0 && counter[i][2] == 0 && counter[i][3] == 0) - op = DiffInfo::OP_1STONLY; - else if (counter[i][0] == 0 && counter[i][1] != 0 && counter[i][2] == 0 && counter[i][3] == 0) - op = DiffInfo::OP_2NDONLY; - else if (counter[i][0] == 0 && counter[i][1] == 0 && counter[i][2] != 0 && counter[i][3] == 0) - op = DiffInfo::OP_3RDONLY; - else - op = DiffInfo::OP_DIFF; - m_diffInfos[i].op = op; - } - return diffCount; - } - - void Make3WayDiff(const Array2D& diff01, const Array2D& diff21, Array2D& diff3) - { - diff3 = diff01; - for (unsigned bx = 0; bx < diff3.width(); ++bx) - { - for (unsigned by = 0; by < diff3.height(); ++by) - { - if (diff21(bx, by) != 0) - diff3(bx, by) = -1; - } - } - } - - void MarkDiff(int pane, const Array2D& diff) - { - const unsigned w = m_imgDiff[pane].getWidth(); - const unsigned h = m_imgDiff[pane].getHeight(); - - for (unsigned by = 0; by < diff.height(); ++by) - { - for (unsigned bx = 0; bx < diff.width(); ++bx) - { - unsigned diffIndex = diff(bx, by); - if (diffIndex != 0 && ( - (pane == 0 && m_diffInfos[diffIndex - 1].op != DiffInfo::OP_3RDONLY) || - (pane == 1) || - (pane == 2 && m_diffInfos[diffIndex - 1].op != DiffInfo::OP_1STONLY) - )) - { - COLORREF color = (diffIndex - 1 == m_currentDiffIndex) ? m_selDiffColor : m_diffColor; - unsigned bsy = (h - by * m_diffBlockSize < m_diffBlockSize) ? (h - by * m_diffBlockSize) : m_diffBlockSize; - for (unsigned i = 0; i < bsy; ++i) - { - unsigned y = by * m_diffBlockSize + i; - BYTE *scanline = m_imgDiff[pane].getScanLine(h - y - 1); - unsigned bsx = (w - bx * m_diffBlockSize < m_diffBlockSize) ? (w - bx * m_diffBlockSize) : m_diffBlockSize; - for (unsigned j = 0; j < bsx; ++j) - { - unsigned x = bx * m_diffBlockSize + j; - scanline[x * 4 + 0] = static_cast(scanline[x * 4 + 0] * (1 - m_diffColorAlpha) + GetBValue(color) * m_diffColorAlpha); - scanline[x * 4 + 1] = static_cast(scanline[x * 4 + 1] * (1 - m_diffColorAlpha) + GetGValue(color) * m_diffColorAlpha); - scanline[x * 4 + 2] = static_cast(scanline[x * 4 + 2] * (1 - m_diffColorAlpha) + GetRValue(color) * m_diffColorAlpha); - } - } - } - } - } - } - - void CopyOriginalImageToDiffImage(int dst) - { - unsigned w = m_imgOrig32[dst].getWidth(); - unsigned h = m_imgOrig32[dst].getHeight(); - for (unsigned y = 0; y < h; ++y) - { - const BYTE *scanline_src = m_imgOrig32[dst].getScanLine(h - y - 1); - BYTE *scanline_dst = m_imgDiff[dst].getScanLine(m_imgDiff[dst].getHeight() - y - 1); - for (unsigned x = 0; x < w; ++x) - { - scanline_dst[x * 4 + 0] = scanline_src[x * 4 + 0]; - scanline_dst[x * 4 + 1] = scanline_src[x * 4 + 1]; - scanline_dst[x * 4 + 2] = scanline_src[x * 4 + 2]; - scanline_dst[x * 4 + 3] = scanline_src[x * 4 + 3]; - } - } - } - - void XorImages2(int src, int dst) - { - unsigned w = m_imgOrig32[src].getWidth(); - unsigned h = m_imgOrig32[src].getHeight(); - for (unsigned y = 0; y < h; ++y) - { - const BYTE *scanline_src = m_imgOrig32[src].getScanLine(h - y - 1); - BYTE *scanline_dst = m_imgDiff[dst].getScanLine(m_imgDiff[dst].getHeight() - y - 1); - for (unsigned x = 0; x < w; ++x) - { - scanline_dst[x * 4 + 0] ^= scanline_src[x * 4 + 0]; - scanline_dst[x * 4 + 1] ^= scanline_src[x * 4 + 1]; - scanline_dst[x * 4 + 2] ^= scanline_src[x * 4 + 2]; - } - } - } - - void AlphaBlendImages2(int src, int dst) - { - unsigned w = m_imgOrig32[src].getWidth(); - unsigned h = m_imgOrig32[src].getHeight(); - for (unsigned y = 0; y < h; ++y) - { - const BYTE *scanline_src = m_imgOrig32[src].getScanLine(h - y - 1); - BYTE *scanline_dst = m_imgDiff[dst].getScanLine(m_imgDiff[dst].getHeight() - y - 1); - for (unsigned x = 0; x < w; ++x) - { - scanline_dst[x * 4 + 0] = static_cast(scanline_dst[x * 4 + 0] * (1 - m_overlayAlpha) + scanline_src[x * 4 + 0] * m_overlayAlpha); - scanline_dst[x * 4 + 1] = static_cast(scanline_dst[x * 4 + 1] * (1 - m_overlayAlpha) + scanline_src[x * 4 + 1] * m_overlayAlpha); - scanline_dst[x * 4 + 2] = static_cast(scanline_dst[x * 4 + 2] * (1 - m_overlayAlpha) + scanline_src[x * 4 + 2] * m_overlayAlpha); - scanline_dst[x * 4 + 3] = static_cast(scanline_dst[x * 4 + 3] * (1 - m_overlayAlpha) + scanline_src[x * 4 + 3] * m_overlayAlpha); - } - } - } - void DrawXorBar(HDC hdc, int x1, int y1, int width, int height) { static WORD _dotPatternBmp[8] = @@ -1977,16 +921,11 @@ private: case WM_LBUTTONDBLCLK: { POINT pt = pImgWnd->GetCursorPos(i); - if (pt.x > 0 && pt.y > 0 && - pt.x < static_cast(pImgWnd->m_imgDiff[i].getWidth()) && - pt.y < static_cast(pImgWnd->m_imgDiff[i].getHeight())) - { - int diffIndex = pImgWnd->m_diff(pt.x / pImgWnd->m_diffBlockSize, pt.y / pImgWnd->m_diffBlockSize); - if (diffIndex != 0) - pImgWnd->SelectDiff(diffIndex - 1); - else - pImgWnd->SelectDiff(-1); - } + int diffIndex = pImgWnd->GetDiffIndexFromPoint(pt.x, pt.y); + if (diffIndex != 0) + pImgWnd->SelectDiff(diffIndex - 1); + else + pImgWnd->SelectDiff(-1); break; } case WM_HSCROLL: @@ -2005,31 +944,12 @@ private: int m_nImages; HWND m_hWnd; HINSTANCE m_hInstance; - fipMultiPageEx m_imgOrigMultiPage[3]; - fipImageEx m_imgOrig[3]; - fipImageEx m_imgOrig32[3]; - fipWinImage m_imgDiff[3]; CImgWindow m_imgWindow[3]; WNDPROC m_ChildWndProc[3]; std::vector m_listener; - std::wstring m_filename[3]; - bool m_bRO[3]; int m_nDraggingSplitter; bool m_bHorizontalSplit; int m_oldSplitPosX; int m_oldSplitPosY; - OVERLAY_MODE m_overlayMode; - bool m_showDifferences; - double m_overlayAlpha; - unsigned m_diffBlockSize; - COLORREF m_selDiffColor; - COLORREF m_diffColor; - double m_diffColorAlpha; - double m_colorDistanceThreshold; - int m_currentPage[3]; - int m_currentDiffIndex; - int m_diffCount; - Array2D m_diff, m_diff01, m_diff21, m_diff02; - std::vector m_diffInfos; - UndoRecords m_undoRecords; + CImgMergeBuffer m_buffer; }; diff --git a/src/WinIMergeLib.vcxproj b/src/WinIMergeLib.vcxproj index df7c37ef4..e5e7a1e0b 100644 --- a/src/WinIMergeLib.vcxproj +++ b/src/WinIMergeLib.vcxproj @@ -169,6 +169,7 @@ + diff --git a/src/WinIMergeLib.vcxproj.filters b/src/WinIMergeLib.vcxproj.filters index de5f70853..3cb919728 100644 --- a/src/WinIMergeLib.vcxproj.filters +++ b/src/WinIMergeLib.vcxproj.filters @@ -21,10 +21,13 @@ Header Files - + Header Files - + + Header Files + + Header Files -- 2.11.0 From 82645f0addec724db104f7054aeab251422df4e6 Mon Sep 17 00:00:00 2001 From: sdottaka Date: Sat, 4 Oct 2014 18:48:09 +0900 Subject: [PATCH 02/16] make CImgMergeBuffer.hpp compilable on Linux --- src/CImgMergeBuffer.hpp | 144 +++++++++++++++++++++++++++++++++--------------- src/CImgMergeWindow.hpp | 31 +++++++++-- 2 files changed, 125 insertions(+), 50 deletions(-) diff --git a/src/CImgMergeBuffer.hpp b/src/CImgMergeBuffer.hpp index a381bcc31..b7e9e4492 100644 --- a/src/CImgMergeBuffer.hpp +++ b/src/CImgMergeBuffer.hpp @@ -18,12 +18,11 @@ #pragma once #include "FreeImagePlus.h" -#include "WinIMergeLib.h" #include #include #include +#include #include -#include #include template struct Point @@ -130,6 +129,8 @@ struct DiffInfo Rect rc; }; +struct DiffStat { int d1, d2, d3, detc; }; + struct UndoRecord { UndoRecord(int pane, FIBITMAP *oldbitmap, FIBITMAP *newbitmap, const int modcountnew[3]) : @@ -235,6 +236,17 @@ struct UndoRecords int m_modcountonsave[3]; }; +#ifndef _WIN32 +class fipWinImage : public fipImage +{ +public: + fipWinImage(FREE_IMAGE_TYPE image_type = FIT_BITMAP, unsigned width = 0, unsigned height = 0, unsigned bpp = 0) : + fipImage(image_type, width, height, bpp) {} + fipWinImage(const fipWinImage& Image) : fipImage(Image) {} + virtual ~fipWinImage() {} +}; +#endif + class fipImageEx : public fipImage { public: @@ -351,10 +363,15 @@ public: BOOL openU(const wchar_t* lpszPathName, BOOL create_new, BOOL read_only, int flags = 0) { - wchar_t shortname[260]; char filename[260]; - GetShortPathName(lpszPathName, shortname, sizeof(shortname)); +#ifdef _WIN32 + wchar_t shortname[260] = {0}; + GetShortPathNameW(lpszPathName, shortname, sizeof(shortname)/sizeof(shortname[0])); wsprintfA(filename, "%S", shortname); +#else + snprintf(filename, sizeof(filename), "%ls", lpszPathName); + +#endif BOOL result = open(filename, create_new, read_only, flags); return result; } @@ -362,7 +379,13 @@ public: bool saveU(const wchar_t* lpszPathName, int flag = 0) const { FILE *fp = NULL; +#ifdef _WIN32 _wfopen_s(&fp, lpszPathName, L"r+b"); +#else + char filename[260]; + snprintf(filename, sizeof(filename), "%ls", lpszPathName); + fp = fopen(filename, "r+b"); +#endif if (!fp) return false; FreeImageIO io; @@ -408,20 +431,32 @@ namespace class CImgMergeBuffer { + typedef Array2D DiffBlocks; + public: + enum OVERLAY_MODE { + OVERLAY_NONE = 0, OVERLAY_XOR, OVERLAY_ALPHABLEND + }; + CImgMergeBuffer() : m_nImages(0) - , m_diffBlockSize(8) - , m_overlayMode(IImgMergeWindow::OVERLAY_NONE) - , m_overlayAlpha(0.3) , m_showDifferences(true) - , m_selDiffColor(RGB(0xff, 0x40, 0x40)) - , m_diffColor(RGB(0xff, 0xff, 0x40)) + , m_overlayMode(OVERLAY_NONE) + , m_overlayAlpha(0.3) + , m_diffBlockSize(8) + , m_selDiffColor() + , m_diffColor() , m_diffColorAlpha(0.7) - , m_diffCount(0) - , m_currentDiffIndex(-1) , m_colorDistanceThreshold(0.0) + , m_currentDiffIndex(-1) + , m_diffCount(0) { + m_selDiffColor.rgbRed = 0xff; + m_selDiffColor.rgbGreen = 0x40; + m_selDiffColor.rgbBlue = 0x40; + m_diffColor.rgbRed = 0xff; + m_diffColor.rgbGreen = 0xff; + m_diffColor.rgbBlue = 0x40; for (int i = 0; i < 3; ++i) { m_currentPage[i] = 0; @@ -431,6 +466,7 @@ public: ~CImgMergeBuffer() { + CloseImages(); } const wchar_t *GetFileName(int pane) @@ -472,23 +508,23 @@ public: m_bRO[pane] = readOnly; } - COLORREF GetDiffColor() const + RGBQUAD GetDiffColor() const { return m_diffColor; } - void SetDiffColor(COLORREF clrDiffColor) + void SetDiffColor(RGBQUAD clrDiffColor) { m_diffColor = clrDiffColor; RefreshImages(); } - COLORREF GetSelDiffColor() const + RGBQUAD GetSelDiffColor() const { return m_selDiffColor; } - void SetSelDiffColor(COLORREF clrSelDiffColor) + void SetSelDiffColor(RGBQUAD clrSelDiffColor) { m_selDiffColor = clrSelDiffColor; RefreshImages(); @@ -589,12 +625,12 @@ public: CompareImages(); } - IImgMergeWindow::OVERLAY_MODE GetOverlayMode() const + OVERLAY_MODE GetOverlayMode() const { return m_overlayMode; } - void SetOverlayMode(IImgMergeWindow::OVERLAY_MODE overlayMode) + void SetOverlayMode(OVERLAY_MODE overlayMode) { m_overlayMode = overlayMode; RefreshImages(); @@ -690,9 +726,9 @@ public: bool FirstConflict() { - for (int i = 0; i < m_diffInfos.size(); ++i) + for (size_t i = 0; i < m_diffInfos.size(); ++i) if (m_diffInfos[i].op == DiffInfo::OP_DIFF) - m_currentDiffIndex = i; + m_currentDiffIndex = static_cast(i); RefreshImages(); return true; } @@ -1000,9 +1036,9 @@ public: for (int i = 0; i < m_nImages; ++i) CopyOriginalImageToDiffImage(i); void (CImgMergeBuffer::*func)(int src, int dst) = NULL; - if (m_overlayMode == IImgMergeWindow::OVERLAY_ALPHABLEND) + if (m_overlayMode == OVERLAY_ALPHABLEND) func = &CImgMergeBuffer::AlphaBlendImages2; - else if (m_overlayMode == IImgMergeWindow::OVERLAY_XOR) + else if (m_overlayMode == OVERLAY_XOR) func = &CImgMergeBuffer::XorImages2; if (func) { @@ -1081,7 +1117,13 @@ public: } else { +#ifdef _WIN32 return !!m_imgOrig[pane].saveU(filename); +#else + char filenameA[260]; + snprintf(filenameA, sizeof(filenameA), "%ls", filename); + return !!m_imgOrig[pane].save(filenameA); +#endif } } @@ -1093,6 +1135,7 @@ public: m_imgOrig32[i].clear(); m_undoRecords.clear(); } + m_nImages = 0; return true; } @@ -1100,7 +1143,13 @@ public: { if (pane < 0 || pane >= m_nImages) return false; +#ifdef _WIN32 return !!m_imgDiff[pane].saveU(filename); +#else + char filenameA[260]; + snprintf(filenameA, sizeof(filenameA), "%ls", filename); + return !!m_imgDiff[pane].save(filenameA); +#endif } int GetImageWidth(int pane) const @@ -1164,8 +1213,15 @@ private: } if (!m_imgOrigMultiPage[i].isValid()) { +#ifdef _WIN32 if (!m_imgOrig[i].loadU(m_filename[i].c_str())) bSucceeded = false; +#else + char filename[260]; + snprintf(filename, sizeof(filename), "%ls", m_filename[i].c_str()); + if (!m_imgOrig[i].load(filename)) + bSucceeded = false; +#endif m_imgOrig32[i] = m_imgOrig[i]; } @@ -1213,7 +1269,7 @@ private: m_imgDiff[i].setSize(FIT_BITMAP, size.cx, size.cy, 32); } - void CompareImages2(int pane1, int pane2, Array2D& diff) + void CompareImages2(int pane1, int pane2, DiffBlocks& diff) { unsigned w1 = m_imgOrig32[pane1].getWidth(); unsigned h1 = m_imgOrig32[pane1].getHeight(); @@ -1276,7 +1332,7 @@ private: } } - void FloodFill8Directions(Array2D& data, int x, int y, unsigned val) + void FloodFill8Directions(DiffBlocks& data, int x, int y, unsigned val) { std::vector > stack; stack.push_back(Point(x, y)); @@ -1312,7 +1368,7 @@ private: } } - int MarkDiffIndex(Array2D& diff) + int MarkDiffIndex(DiffBlocks& diff) { int diffCount = 0; for (unsigned by = 0; by < diff.height(); ++by) @@ -1343,10 +1399,10 @@ private: return diffCount; } - int MarkDiffIndex3way(Array2D& diff01, Array2D& diff21, Array2D& diff02, Array2D& diff3) + int MarkDiffIndex3way(DiffBlocks& diff01, DiffBlocks& diff21, DiffBlocks& diff02, DiffBlocks& diff3) { int diffCount = MarkDiffIndex(diff3); - std::vector> counter(m_diffInfos.size()); + std::vector counter(m_diffInfos.size()); for (unsigned by = 0; by < diff3.height(); ++by) { for (unsigned bx = 0; bx < diff3.width(); ++bx) @@ -1356,24 +1412,24 @@ private: continue; --diffIndex; if (diff21(bx, by) == 0) - ++counter[diffIndex][0]; + ++counter[diffIndex].d1; else if (diff02(bx, by) == 0) - ++counter[diffIndex][1]; + ++counter[diffIndex].d2; else if (diff01(bx, by) == 0) - ++counter[diffIndex][2]; + ++counter[diffIndex].d3; else - ++counter[diffIndex][3]; + ++counter[diffIndex].detc; } } for (size_t i = 0; i < m_diffInfos.size(); ++i) { int op; - if (counter[i][0] != 0 && counter[i][1] == 0 && counter[i][2] == 0 && counter[i][3] == 0) + if (counter[i].d1 != 0 && counter[i].d2 == 0 && counter[i].d3 == 0 && counter[i].detc == 0) op = DiffInfo::OP_1STONLY; - else if (counter[i][0] == 0 && counter[i][1] != 0 && counter[i][2] == 0 && counter[i][3] == 0) + else if (counter[i].d1 == 0 && counter[i].d2 != 0 && counter[i].d3 == 0 && counter[i].detc == 0) op = DiffInfo::OP_2NDONLY; - else if (counter[i][0] == 0 && counter[i][1] == 0 && counter[i][2] != 0 && counter[i][3] == 0) + else if (counter[i].d1 == 0 && counter[i].d2 == 0 && counter[i].d3 != 0 && counter[i].detc == 0) op = DiffInfo::OP_3RDONLY; else op = DiffInfo::OP_DIFF; @@ -1382,7 +1438,7 @@ private: return diffCount; } - void Make3WayDiff(const Array2D& diff01, const Array2D& diff21, Array2D& diff3) + void Make3WayDiff(const DiffBlocks& diff01, const DiffBlocks& diff21, DiffBlocks& diff3) { diff3 = diff01; for (unsigned bx = 0; bx < diff3.width(); ++bx) @@ -1395,7 +1451,7 @@ private: } } - void MarkDiff(int pane, const Array2D& diff) + void MarkDiff(int pane, const DiffBlocks& diff) { const unsigned w = m_imgDiff[pane].getWidth(); const unsigned h = m_imgDiff[pane].getHeight(); @@ -1404,14 +1460,14 @@ private: { for (unsigned bx = 0; bx < diff.width(); ++bx) { - unsigned diffIndex = diff(bx, by); + int diffIndex = diff(bx, by); if (diffIndex != 0 && ( (pane == 0 && m_diffInfos[diffIndex - 1].op != DiffInfo::OP_3RDONLY) || (pane == 1) || (pane == 2 && m_diffInfos[diffIndex - 1].op != DiffInfo::OP_1STONLY) )) { - COLORREF color = (diffIndex - 1 == m_currentDiffIndex) ? m_selDiffColor : m_diffColor; + RGBQUAD color = (diffIndex - 1 == m_currentDiffIndex) ? m_selDiffColor : m_diffColor; unsigned bsy = (h - by * m_diffBlockSize < m_diffBlockSize) ? (h - by * m_diffBlockSize) : m_diffBlockSize; for (unsigned i = 0; i < bsy; ++i) { @@ -1421,9 +1477,9 @@ private: for (unsigned j = 0; j < bsx; ++j) { unsigned x = bx * m_diffBlockSize + j; - scanline[x * 4 + 0] = static_cast(scanline[x * 4 + 0] * (1 - m_diffColorAlpha) + GetBValue(color) * m_diffColorAlpha); - scanline[x * 4 + 1] = static_cast(scanline[x * 4 + 1] * (1 - m_diffColorAlpha) + GetGValue(color) * m_diffColorAlpha); - scanline[x * 4 + 2] = static_cast(scanline[x * 4 + 2] * (1 - m_diffColorAlpha) + GetRValue(color) * m_diffColorAlpha); + scanline[x * 4 + 0] = static_cast(scanline[x * 4 + 0] * (1 - m_diffColorAlpha) + color.rgbBlue * m_diffColorAlpha); + scanline[x * 4 + 1] = static_cast(scanline[x * 4 + 1] * (1 - m_diffColorAlpha) + color.rgbGreen * m_diffColorAlpha); + scanline[x * 4 + 2] = static_cast(scanline[x * 4 + 2] * (1 - m_diffColorAlpha) + color.rgbRed * m_diffColorAlpha); } } } @@ -1491,18 +1547,18 @@ private: fipWinImage m_imgDiff[3]; std::wstring m_filename[3]; bool m_bRO[3]; - IImgMergeWindow::OVERLAY_MODE m_overlayMode; bool m_showDifferences; + OVERLAY_MODE m_overlayMode; double m_overlayAlpha; unsigned m_diffBlockSize; - COLORREF m_selDiffColor; - COLORREF m_diffColor; + RGBQUAD m_selDiffColor; + RGBQUAD m_diffColor; double m_diffColorAlpha; double m_colorDistanceThreshold; int m_currentPage[3]; int m_currentDiffIndex; int m_diffCount; - Array2D m_diff, m_diff01, m_diff21, m_diff02; + DiffBlocks m_diff, m_diff01, m_diff21, m_diff02; std::vector m_diffInfos; UndoRecords m_undoRecords; }; diff --git a/src/CImgMergeWindow.hpp b/src/CImgMergeWindow.hpp index b25facea5..651159901 100644 --- a/src/CImgMergeWindow.hpp +++ b/src/CImgMergeWindow.hpp @@ -23,6 +23,25 @@ #include "CImgMergeBuffer.hpp" #include "WinIMergeLib.h" + +namespace +{ + RGBQUAD COLORREFtoRGBQUAD(COLORREF c) + { + RGBQUAD rgb; + rgb.rgbRed = GetRValue(c); + rgb.rgbGreen = GetGValue(c); + rgb.rgbBlue = GetBValue(c); + rgb.rgbReserved = (c >> 24); + return rgb; + } + + COLORREF RGBQUADtoCOLORREF(RGBQUAD c) + { + return RGB(c.rgbRed, c.rgbGreen, c.rgbBlue) | (c.rgbReserved << 24); + } +} + class CImgMergeWindow : public IImgMergeWindow { struct EventListenerInfo @@ -175,23 +194,23 @@ public: COLORREF GetDiffColor() const { - return m_buffer.GetDiffColor(); + return RGBQUADtoCOLORREF(m_buffer.GetDiffColor()); } void SetDiffColor(COLORREF clrDiffColor) { - m_buffer.SetDiffColor(clrDiffColor); + m_buffer.SetDiffColor(COLORREFtoRGBQUAD(clrDiffColor)); Invalidate(); } COLORREF GetSelDiffColor() const { - return m_buffer.GetSelDiffColor(); + return RGBQUADtoCOLORREF(m_buffer.GetSelDiffColor()); } void SetSelDiffColor(COLORREF clrSelDiffColor) { - m_buffer.SetSelDiffColor(clrSelDiffColor); + m_buffer.SetSelDiffColor(COLORREFtoRGBQUAD(clrSelDiffColor)); Invalidate(); } @@ -295,12 +314,12 @@ public: OVERLAY_MODE GetOverlayMode() const { - return m_buffer.GetOverlayMode(); + return static_cast(m_buffer.GetOverlayMode()); } void SetOverlayMode(OVERLAY_MODE overlayMode) { - m_buffer.SetOverlayMode(overlayMode); + m_buffer.SetOverlayMode(static_cast(overlayMode)); Invalidate(); } -- 2.11.0 From 109a30d532420a01466c75dda7752cb074db0dc0 Mon Sep 17 00:00:00 2001 From: sdottaka Date: Sat, 4 Oct 2014 18:51:29 +0900 Subject: [PATCH 03/16] Add cidiff command --- WinIMerge.sln | 20 ++++++- src/Makefile | 19 ++++++ src/cidiff.cpp | 36 ++++++++++++ src/cidiff.vcxproj | 168 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 240 insertions(+), 3 deletions(-) create mode 100644 src/Makefile create mode 100644 src/cidiff.cpp create mode 100644 src/cidiff.vcxproj diff --git a/WinIMerge.sln b/WinIMerge.sln index 261f375f7..62af93c27 100644 --- a/WinIMerge.sln +++ b/WinIMerge.sln @@ -51,6 +51,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WinIMergeLib", "src\WinIMer EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FreeImage", "FreeImage", "{AD6E6E47-0363-409F-8B7A-48341147E632}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cidiff", "src\cidiff.vcxproj", "{49A7104F-08CD-48B1-99E6-6A7C3FFB0EEC}" + ProjectSection(ProjectDependencies) = postProject + {94F36908-A4E2-4533-939D-64FF6EADA5A1} = {94F36908-A4E2-4533-939D-64FF6EADA5A1} + {9E219DF2-315D-478E-8A07-8960C377CE1E} = {9E219DF2-315D-478E-8A07-8960C377CE1E} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -163,14 +169,19 @@ Global {C9101B3C-4CFE-4D3E-9708-5C0F55AC909A}.Release|Win32.Build.0 = Release|Win32 {C9101B3C-4CFE-4D3E-9708-5C0F55AC909A}.Release|x64.ActiveCfg = Release|x64 {C9101B3C-4CFE-4D3E-9708-5C0F55AC909A}.Release|x64.Build.0 = Release|x64 + {49A7104F-08CD-48B1-99E6-6A7C3FFB0EEC}.Debug|Win32.ActiveCfg = Debug|Win32 + {49A7104F-08CD-48B1-99E6-6A7C3FFB0EEC}.Debug|Win32.Build.0 = Debug|Win32 + {49A7104F-08CD-48B1-99E6-6A7C3FFB0EEC}.Debug|x64.ActiveCfg = Debug|x64 + {49A7104F-08CD-48B1-99E6-6A7C3FFB0EEC}.Debug|x64.Build.0 = Debug|x64 + {49A7104F-08CD-48B1-99E6-6A7C3FFB0EEC}.Release|Win32.ActiveCfg = Release|Win32 + {49A7104F-08CD-48B1-99E6-6A7C3FFB0EEC}.Release|Win32.Build.0 = Release|Win32 + {49A7104F-08CD-48B1-99E6-6A7C3FFB0EEC}.Release|x64.ActiveCfg = Release|x64 + {49A7104F-08CD-48B1-99E6-6A7C3FFB0EEC}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {9E219DF2-315D-478E-8A07-8960C377CE1E} = {AD6E6E47-0363-409F-8B7A-48341147E632} - {17A4874B-0606-4687-90B6-F91F8CB3B8AF} = {AD6E6E47-0363-409F-8B7A-48341147E632} - {94F36908-A4E2-4533-939D-64FF6EADA5A1} = {AD6E6E47-0363-409F-8B7A-48341147E632} {5E1D4E5F-E10C-4BA3-B663-F33014FD21D9} = {AD6E6E47-0363-409F-8B7A-48341147E632} {244455E0-5F25-4451-9540-F317883E52A8} = {AD6E6E47-0363-409F-8B7A-48341147E632} {E3536C28-A7F1-4B53-8E52-7D2232F9E098} = {AD6E6E47-0363-409F-8B7A-48341147E632} @@ -178,6 +189,9 @@ Global {07F662C1-1323-42AB-B6AF-FBFD34A7437A} = {AD6E6E47-0363-409F-8B7A-48341147E632} {EC085CBD-E9C3-477F-9A97-CB9D5DA30E27} = {AD6E6E47-0363-409F-8B7A-48341147E632} {097D9F6C-FD0E-4CBC-9676-009012AAECA8} = {AD6E6E47-0363-409F-8B7A-48341147E632} + {17A4874B-0606-4687-90B6-F91F8CB3B8AF} = {AD6E6E47-0363-409F-8B7A-48341147E632} {33134F61-C1AD-4B6F-9CEA-503A9F140C52} = {AD6E6E47-0363-409F-8B7A-48341147E632} + {94F36908-A4E2-4533-939D-64FF6EADA5A1} = {AD6E6E47-0363-409F-8B7A-48341147E632} + {9E219DF2-315D-478E-8A07-8960C377CE1E} = {AD6E6E47-0363-409F-8B7A-48341147E632} EndGlobalSection EndGlobal diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 000000000..271f5c6bb --- /dev/null +++ b/src/Makefile @@ -0,0 +1,19 @@ +TARGETS=cidiff +CXXFLAGS+=-Wall -Wextra -I../../freeimage/Source -I../../freeimage/Wrapper/FreeImagePlus +SRCS=cidiff.cpp +OBJS=$(SRCS:.cpp=*.o) +HEADERS=CImgMergeBuffer.hpp +LIBS=-L../../freeimage/ -lfreeimage -L../../freeimage/ -lfreeimageplus + +all: $(TARGETS) + +clean: + @rm -f $(TARGETS) $(OBJS) + +%.o : %.cpp $(HEADERS) + $(CXX) $(CXXFLAGS) -c $< + +cidiff: cidiff.o + $(CXX) $< $(LIBS) -o $@ + + diff --git a/src/cidiff.cpp b/src/cidiff.cpp new file mode 100644 index 000000000..6aa09e177 --- /dev/null +++ b/src/cidiff.cpp @@ -0,0 +1,36 @@ +#include "CImgMergeBuffer.hpp" +#include +#include + +int main(int argc, char* argv[]) +{ + CImgMergeBuffer buffer; + wchar_t filenameW[2][260]; + const wchar_t *filenames[2] = { filenameW[0], filenameW[1] }; + + if (argc < 3) + { + std::wcerr << L"usage: cmdidiff image_file1 image_file2" << std::endl; + exit(1); + } + + FreeImage_Initialise(); + setlocale(LC_ALL, ""); + + mbstowcs(filenameW[0], argv[1], strlen(argv[1]) + 1); + mbstowcs(filenameW[1], argv[2], strlen(argv[2]) + 1); + + if (!buffer.OpenImages(2, filenames)) + { + std::wcerr << L"cmdidiff: could not open files. (" << filenameW[0] << ", " << filenameW[1] << L")" << std::endl; + exit(1); + } + + buffer.CompareImages(); + buffer.SaveDiffImageAs(1, L"diff.png"); + buffer.CloseImages(); + + return 0; +} + + diff --git a/src/cidiff.vcxproj b/src/cidiff.vcxproj new file mode 100644 index 000000000..6447c5ff6 --- /dev/null +++ b/src/cidiff.vcxproj @@ -0,0 +1,168 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {49A7104F-08CD-48B1-99E6-6A7C3FFB0EEC} + Win32Proj + cidiff + cidiff + + + + Application + true + Unicode + + + Application + true + Unicode + + + Application + false + true + Unicode + + + Application + false + true + Unicode + + + + + + + + + + + + + + + + + + + true + ..\Build\$(Configuration)\ + ..\BuildTmp\$(Configuration)\ + + + true + ..\Build\$(Platform)\$(Configuration)\ + ..\BuildTmp\$(Platform)\$(Configuration)\ + + + false + ..\Build\$(Configuration)\ + ..\BuildTmp\$(Configuration)\ + + + false + ..\Build\$(Platform)\$(Configuration)\ + ..\BuildTmp\$(Platform)\$(Configuration)\ + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;FREEIMAGE_LIB;%(PreprocessorDefinitions) + ..\..\FreeImage\Dist;..\..\FreeImage\Wrapper\FreeImagePlus\dist + MultiThreadedDebug + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;FreeImaged.lib;FreeImagePlusd.lib;%(AdditionalDependencies) + ..\..\FreeImage\Dist;..\..\FreeImage\Wrapper\FreeImagePlus\dist; + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;FREEIMAGE_LIB;%(PreprocessorDefinitions) + ..\..\FreeImage\Dist\x64;..\..\FreeImage\Wrapper\FreeImagePlus\dist\x64 + MultiThreadedDebug + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;FreeImaged.lib;FreeImagePlusd.lib;%(AdditionalDependencies) + ..\..\FreeImage\Dist\$(Platform);..\..\FreeImage\Wrapper\FreeImagePlus\dist\$(Platform) + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;FREEIMAGE_LIB;%(PreprocessorDefinitions) + ..\..\FreeImage\Dist;..\..\FreeImage\Wrapper\FreeImagePlus\dist + MultiThreaded + + + Console + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;FreeImage.lib;FreeImagePlus.lib;%(AdditionalDependencies) + ..\..\FreeImage\Dist;..\..\FreeImage\Wrapper\FreeImagePlus\dist; + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;FREEIMAGE_LIB;%(PreprocessorDefinitions) + ..\..\FreeImage\Dist\x64;..\..\FreeImage\Wrapper\FreeImagePlus\dist\x64 + MultiThreaded + + + Console + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;FreeImage.lib;FreeImagePlus.lib;%(AdditionalDependencies) + ..\..\FreeImage\Dist\$(Platform);..\..\FreeImage\Wrapper\FreeImagePlus\dist\$(Platform) + + + + + + + + + \ No newline at end of file -- 2.11.0 From 3f93f07c8bd3e26faee7f220183e545d13f4e185 Mon Sep 17 00:00:00 2001 From: sdottaka Date: Sun, 5 Oct 2014 20:57:55 +0900 Subject: [PATCH 04/16] Bump revision to 0.9.4.0 --- SetVersion.cmd | 2 +- Version.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/SetVersion.cmd b/SetVersion.cmd index d36af9c93..6c9b52c63 100644 --- a/SetVersion.cmd +++ b/SetVersion.cmd @@ -1,4 +1,4 @@ set MAJOR=0 set MINOR=9 -set REVISION=3 +set REVISION=4 set PATCHLEVEL=0 diff --git a/Version.h b/Version.h index b6b51007f..ca5d94423 100644 --- a/Version.h +++ b/Version.h @@ -1,4 +1,4 @@ -#define FILEVER 0,9,3,0 -#define PRODUCTVER 0,9,3,0 -#define STRFILEVER "0.9.3.0" -#define STRPRODUCTVER "0.9.3.0" +#define FILEVER 0,9,4,0 +#define PRODUCTVER 0,9,4,0 +#define STRFILEVER "0.9.4.0" +#define STRPRODUCTVER "0.9.4.0" -- 2.11.0 From 13d76e9e9343d88ed3075c9b61bec443955b35af Mon Sep 17 00:00:00 2001 From: sdottaka Date: Sun, 5 Oct 2014 20:58:13 +0900 Subject: [PATCH 05/16] Added tag 0.9.4.0 for changeset 0ef16028a292 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 8b3b2ae72..02dd96eee 100644 --- a/.hgtags +++ b/.hgtags @@ -1,2 +1,3 @@ 5710fff75af907f9c292950e3dc9bf3ab4c448ca 0.9.2.0 9e83a2640c31b4fb8321fdcaf03b7ac539c320c8 0.9.3.0 +0ef16028a292e33f87cbe75f83a7f799299a1ac9 0.9.4.0 -- 2.11.0 From c8069b064632291ac6a1d970d91a673234f6c531 Mon Sep 17 00:00:00 2001 From: sdottaka Date: Sun, 5 Oct 2014 21:11:59 +0900 Subject: [PATCH 06/16] Add cidiff command to archive file --- BuildArc.cmd | 2 ++ BuildBin.cmd | 1 + BuildBin_vc11.cmd | 1 + 3 files changed, 4 insertions(+) diff --git a/BuildArc.cmd b/BuildArc.cmd index c14f713ab..564c85b80 100644 --- a/BuildArc.cmd +++ b/BuildArc.cmd @@ -10,8 +10,10 @@ mkdir "%DISTDIR%\WinIMerge\bin64" 2> NUL copy Build\Release\WinIMerge.exe "%DISTDIR%\WinIMerge\bin" copy Build\Release\WinIMergeLib.dll "%DISTDIR%\WinIMerge\bin" +copy Build\Release\cidiff.exe "%DISTDIR%\WinIMerge\bin" copy Build\x64\Release\WinIMerge.exe "%DISTDIR%\WinIMerge\bin64" copy Build\x64\Release\WinIMergeLib.dll "%DISTDIR%\WinIMerge\bin64" +copy Build\x64\Release\cidiff.exe "%DISTDIR%\WinIMerge\bin64" copy GPL.txt "%DISTDIR%\WinIMerge" copy freeimage-license-gplv2.txt "%DISTDIR%\WinIMerge" diff --git a/BuildBin.cmd b/BuildBin.cmd index 48fd07c89..54aae3930 100644 --- a/BuildBin.cmd +++ b/BuildBin.cmd @@ -13,6 +13,7 @@ for %%i in ( ^ ..\freeimage\Wrapper\FreeImagePlus\FreeImagePlus.vcxproj ^ src\WinIMergeLib.vcxproj ^ src\WinIMerge.vcxproj ^ + src\cidiff.vcxproj ^ ) do ( MSBuild %%i /t:build /p:Configuration=Release /p:Platform="Win32" || pause MSBuild %%i /t:build /p:Configuration=Release /p:Platform="x64" || pause diff --git a/BuildBin_vc11.cmd b/BuildBin_vc11.cmd index a3d8443ea..421843443 100644 --- a/BuildBin_vc11.cmd +++ b/BuildBin_vc11.cmd @@ -14,6 +14,7 @@ for %%i in ( ^ ..\freeimage\Wrapper\FreeImagePlus\FreeImagePlus.vcxproj ^ src\WinIMergeLib.vcxproj ^ src\WinIMerge.vcxproj ^ + src\cidiff.vcxproj ^ ) do ( MSBuild %%i /t:build /p:Configuration=Release /p:Platform="Win32" /p:PlatformToolset=v110_xp || pause MSBuild %%i /t:build /p:Configuration=Release /p:Platform="x64" /p:PlatformToolset=v110_xp || pause -- 2.11.0 From b5b3d98ec0dfae671eb3e285ec3240d73c753767 Mon Sep 17 00:00:00 2001 From: sdottaka Date: Tue, 7 Oct 2014 19:49:13 +0900 Subject: [PATCH 07/16] CImgMergeWindow.hpp: Did not deselect the current diff area when double-clicking non-diff area --- src/CImgMergeWindow.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/CImgMergeWindow.hpp b/src/CImgMergeWindow.hpp index 651159901..115203c49 100644 --- a/src/CImgMergeWindow.hpp +++ b/src/CImgMergeWindow.hpp @@ -510,6 +510,11 @@ public: for (int i = 0; i < m_nImages; ++i) m_imgWindow[i].ScrollTo(rc.left * m_buffer.GetDiffBlockSize(), rc.top * m_buffer.GetDiffBlockSize()); } + else + { + for (int i = 0; i < m_nImages; ++i) + m_imgWindow[i].Invalidate(); + } } void Invalidate() -- 2.11.0 From ccfe9ef1749b5c37eb058cc5366dc0add866d686 Mon Sep 17 00:00:00 2001 From: sdottaka Date: Tue, 7 Oct 2014 20:09:05 +0900 Subject: [PATCH 08/16] CImgMergeBuffer.hpp: Did not colorize image diff area properly when comparing images of different size --- src/CImgMergeBuffer.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CImgMergeBuffer.hpp b/src/CImgMergeBuffer.hpp index b7e9e4492..6909e6819 100644 --- a/src/CImgMergeBuffer.hpp +++ b/src/CImgMergeBuffer.hpp @@ -1287,7 +1287,7 @@ private: unsigned y = by * m_diffBlockSize + i; if (y >= h1 || y >= h2) { - for (unsigned bx = 0; bx < wmax / m_diffBlockSize; ++bx) + for (unsigned bx = 0; bx < diff.width(); ++bx) diff(bx, by) = -1; } else -- 2.11.0 From edd0d99ef386444f0ea2f9689f5b2da71dc20900 Mon Sep 17 00:00:00 2001 From: sdottaka Date: Tue, 7 Oct 2014 20:12:43 +0900 Subject: [PATCH 09/16] CImgMergeBuffer.hpp: Did not merge diff area correctly when comparing images of different size --- src/CImgMergeBuffer.hpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/CImgMergeBuffer.hpp b/src/CImgMergeBuffer.hpp index 6909e6819..54762f459 100644 --- a/src/CImgMergeBuffer.hpp +++ b/src/CImgMergeBuffer.hpp @@ -842,13 +842,9 @@ public: else hdst = rc.bottom * m_diffBlockSize; } - if (rc.right * m_diffBlockSize > wsrc) - wdst = wsrc; - if (rc.bottom * m_diffBlockSize > hsrc) - hdst = hsrc; if (wdst != m_imgOrig32[dstPane].getWidth() || hdst != m_imgOrig32[dstPane].getHeight()) { - fipImage imgTemp = m_imgOrig32[srcPane]; + fipImage imgTemp = m_imgOrig32[dstPane]; m_imgOrig32[dstPane].setSize(imgTemp.getImageType(), wdst, hdst, imgTemp.getBitsPerPixel()); m_imgOrig32[dstPane].pasteSubImage(imgTemp, 0, 0); } -- 2.11.0 From b0ba3c6892f267cf1a6163e41518c0ac32beba54 Mon Sep 17 00:00:00 2001 From: sdottaka Date: Tue, 7 Oct 2014 21:04:16 +0900 Subject: [PATCH 10/16] Split CImgMergeBuffer.hpp into 2 files --- src/CImgDiffBuffer.hpp | 1190 ++++++++++++++++++++++++++++++++++++++ src/CImgMergeBuffer.hpp | 1163 +------------------------------------ src/Makefile | 2 +- src/WinIMergeLib.vcxproj | 1 + src/WinIMergeLib.vcxproj.filters | 3 + src/cidiff.cpp | 4 +- 6 files changed, 1203 insertions(+), 1160 deletions(-) create mode 100644 src/CImgDiffBuffer.hpp diff --git a/src/CImgDiffBuffer.hpp b/src/CImgDiffBuffer.hpp new file mode 100644 index 000000000..6b513094d --- /dev/null +++ b/src/CImgDiffBuffer.hpp @@ -0,0 +1,1190 @@ +///////////////////////////////////////////////////////////////////////////// +// License (GPLv2+): +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +///////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "FreeImagePlus.h" +#include +#include +#include +#include +#include +#include + +template struct Point +{ + Point(T x, T y): x(x), y(y) {} + T x, y; +}; + +template struct Size +{ + Size(T cx, T cy): cx(cx), cy(cy) {} + T cx, cy; +}; + +template struct Rect +{ + Rect(T left, T top, T right, T bottom): left(left), top(top), right(right), bottom(bottom) {} + T left, top, right, bottom; +}; + +template struct Array2D +{ + Array2D() : m_width(0), m_height(0), m_data(NULL) + { + } + + Array2D(size_t width, size_t height) : m_width(width), m_height(height), m_data(new T[width * height]) + { + memset(m_data, 0, m_width * m_height * sizeof(T)); + } + + Array2D(const Array2D& other) : m_width(other.m_width), m_height(other.m_height), m_data(new T[other.m_width * other.m_height]) + { + memcpy(m_data, other.m_data, m_width * m_height * sizeof(T)); + } + + Array2D& operator=(const Array2D& other) + { + if (this != &other) + { + delete[] m_data; + m_width = other.m_width; + m_height = other.m_height; + m_data = new T[other.m_width * other.m_height]; + memcpy(m_data, other.m_data, m_width * m_height * sizeof(T)); + } + return *this; + } + + ~Array2D() + { + delete[] m_data; + } + + void resize(size_t width, size_t height) + { + delete[] m_data; + m_data = new T[width * height]; + m_width = width; + m_height = height; + memset(m_data, 0, sizeof(T) * width * height); + } + + T& operator()(int x, int y) + { + return m_data[y * m_width + x]; + } + + const T& operator()(int x, int y) const + { + return m_data[y * m_width + x]; + } + + void clear() + { + delete[] m_data; + m_data = NULL; + m_width = 0; + m_height = 0; + } + + size_t height() const + { + return m_height; + } + + size_t width() const + { + return m_width; + } + + size_t m_width, m_height; + T* m_data; +}; + +struct DiffInfo +{ + enum OP_TYPE + { + OP_NONE = 0, OP_1STONLY, OP_2NDONLY, OP_3RDONLY, OP_DIFF, OP_TRIVIAL + }; + DiffInfo(int op, int x, int y) : op(op), rc(x, y, x + 1, y + 1) {} + int op; + Rect rc; +}; + +struct DiffStat { int d1, d2, d3, detc; }; + +#ifndef _WIN32 +class fipWinImage : public fipImage +{ +public: + fipWinImage(FREE_IMAGE_TYPE image_type = FIT_BITMAP, unsigned width = 0, unsigned height = 0, unsigned bpp = 0) : + fipImage(image_type, width, height, bpp) {} + fipWinImage(const fipWinImage& Image) : fipImage(Image) {} + virtual ~fipWinImage() {} +}; +#endif + +class fipImageEx : public fipImage +{ +public: + fipImageEx(FREE_IMAGE_TYPE image_type = FIT_BITMAP, unsigned width = 0, unsigned height = 0, unsigned bpp = 0) : + fipImage(image_type, width, height, bpp) {} + fipImageEx(const fipImageEx& Image) : fipImage(Image) {} + virtual ~fipImageEx() {} + + fipImageEx& operator=(const fipImageEx& Image) + { + if (this != &Image) + { + FIBITMAP *clone = FreeImage_Clone((FIBITMAP*)Image._dib); + replace(clone); + } + return *this; + } + + fipImageEx& operator=(FIBITMAP *dib) + { + if (_dib != dib) + replace(dib); + return *this; + } + + void swap(fipImageEx& other) + { + std::swap(_dib, other._dib); + std::swap(this->_fif, other._fif); + std::swap(this->_bHasChanged, other._bHasChanged); + } + + FIBITMAP *detach() + { + FIBITMAP *dib = _dib; + _dib = NULL; + clear(); + return dib; + } + + BOOL colorQuantizeEx(FREE_IMAGE_QUANTIZE quantize = FIQ_WUQUANT, int PaletteSize = 256, int ReserveSize = 0, RGBQUAD *ReservePalette = NULL) + { + if(_dib) { + FIBITMAP *dib8 = FreeImage_ColorQuantizeEx(_dib, quantize, PaletteSize, ReserveSize, ReservePalette); + return !!replace(dib8); + } + return false; + } + + bool convertColorDepth(unsigned bpp, RGBQUAD *pPalette = NULL) + { + switch (bpp) + { + case 1: + return !!threshold(128); + case 4: + { + fipImageEx tmp = *this; + tmp.convertTo24Bits(); + if (pPalette) + tmp.colorQuantizeEx(FIQ_NNQUANT, 16, 16, pPalette); + else + tmp.colorQuantizeEx(FIQ_WUQUANT, 16); + setSize(tmp.getImageType(), tmp.getWidth(), tmp.getHeight(), 4); + for (unsigned y = 0; y < tmp.getHeight(); ++y) + { + const BYTE *line_src = tmp.getScanLine(y); + BYTE *line_dst = getScanLine(y); + for (unsigned x = 0; x < tmp.getWidth(); ++x) + line_dst[x / 2] |= ((x % 2) == 0) ? (line_src[x] << 4) : line_src[x]; + } + + RGBQUAD *rgbq_dst = getPalette(); + RGBQUAD *rgbq_src = pPalette ? pPalette : tmp.getPalette(); + memcpy(rgbq_dst, rgbq_src, sizeof(RGBQUAD) * 16); + return true; + } + case 8: + convertTo24Bits(); + if (pPalette) + return !!colorQuantizeEx(FIQ_NNQUANT, 256, 256, pPalette); + else + return !!colorQuantizeEx(FIQ_WUQUANT, 256); + case 15: + return !!convertTo16Bits555(); + case 16: + return !!convertTo16Bits565(); + case 24: + return !!convertTo24Bits(); + default: + case 32: + return !!convertTo32Bits(); + } + } + + void copyAnimationMetadata(fipImage& src) + { + fipTag tag; + fipMetadataFind finder; + if (finder.findFirstMetadata(FIMD_ANIMATION, src, tag)) + { + do + { + setMetadata(FIMD_ANIMATION, tag.getKey(), tag); + } while (finder.findNextMetadata(tag)); + } + } +}; + +class fipMultiPageEx : public fipMultiPage +{ +public: + fipMultiPageEx(BOOL keep_cache_in_memory = FALSE) : fipMultiPage(keep_cache_in_memory) {} + + BOOL openU(const wchar_t* lpszPathName, BOOL create_new, BOOL read_only, int flags = 0) + { + char filename[260]; +#ifdef _WIN32 + wchar_t shortname[260] = {0}; + GetShortPathNameW(lpszPathName, shortname, sizeof(shortname)/sizeof(shortname[0])); + wsprintfA(filename, "%S", shortname); +#else + snprintf(filename, sizeof(filename), "%ls", lpszPathName); + +#endif + BOOL result = open(filename, create_new, read_only, flags); + return result; + } + + bool saveU(const wchar_t* lpszPathName, int flag = 0) const + { + FILE *fp = NULL; +#ifdef _WIN32 + _wfopen_s(&fp, lpszPathName, L"r+b"); +#else + char filename[260]; + snprintf(filename, sizeof(filename), "%ls", lpszPathName); + fp = fopen(filename, "r+b"); +#endif + if (!fp) + return false; + FreeImageIO io; + io.read_proc = myReadProc; + io.write_proc = myWriteProc; + io.seek_proc = mySeekProc; + io.tell_proc = myTellProc; + FREE_IMAGE_FORMAT fif = fipImage::identifyFIFU(lpszPathName); + bool result = !!saveToHandle(fif, &io, (fi_handle)fp, flag); + fclose(fp); + return result; + } + +private: + static unsigned DLL_CALLCONV myReadProc(void *buffer, unsigned size, unsigned count, fi_handle handle) { + return (unsigned)fread(buffer, size, count, (FILE *)handle); + } + + static unsigned DLL_CALLCONV myWriteProc(void *buffer, unsigned size, unsigned count, fi_handle handle) { + return (unsigned)fwrite(buffer, size, count, (FILE *)handle); + } + + static int DLL_CALLCONV mySeekProc(fi_handle handle, long offset, int origin) { + return fseek((FILE *)handle, offset, origin); + } + + static long DLL_CALLCONV myTellProc(fi_handle handle) { + return ftell((FILE *)handle); + } +}; + +namespace +{ + int GetColorDistance2(RGBQUAD c1, RGBQUAD c2) + { + int rdist = c1.rgbRed - c2.rgbRed; + int gdist = c1.rgbGreen - c2.rgbGreen; + int bdist = c1.rgbBlue - c2.rgbBlue; + int adist = c1.rgbReserved - c2.rgbReserved; + return rdist * rdist + gdist * gdist + bdist * bdist + adist * adist; + } +} + +class CImgDiffBuffer +{ + typedef Array2D DiffBlocks; + +public: + enum OVERLAY_MODE { + OVERLAY_NONE = 0, OVERLAY_XOR, OVERLAY_ALPHABLEND + }; + + CImgDiffBuffer() : + m_nImages(0) + , m_showDifferences(true) + , m_overlayMode(OVERLAY_NONE) + , m_overlayAlpha(0.3) + , m_diffBlockSize(8) + , m_selDiffColor() + , m_diffColor() + , m_diffColorAlpha(0.7) + , m_colorDistanceThreshold(0.0) + , m_currentDiffIndex(-1) + , m_diffCount(0) + { + m_selDiffColor.rgbRed = 0xff; + m_selDiffColor.rgbGreen = 0x40; + m_selDiffColor.rgbBlue = 0x40; + m_diffColor.rgbRed = 0xff; + m_diffColor.rgbGreen = 0xff; + m_diffColor.rgbBlue = 0x40; + for (int i = 0; i < 3; ++i) + m_currentPage[i] = 0; + } + + virtual ~CImgDiffBuffer() + { + CloseImages(); + } + + const wchar_t *GetFileName(int pane) + { + if (pane < 0 || pane >= m_nImages) + return NULL; + return m_filename[pane].c_str(); + } + + int GetPaneCount() const + { + return m_nImages; + } + + RGBQUAD GetPixelColor(int pane, int x, int y) const + { + RGBQUAD value = {0xFF, 0xFF, 0xFF, 0x00}; + m_imgOrig32[pane].getPixelColor(x, m_imgOrig32[pane].getHeight() - y - 1, &value); + return value; + } + + double GetColorDistance(int pane1, int pane2, int x, int y) const + { + return std::sqrt(static_cast( + ::GetColorDistance2(GetPixelColor(pane1, x, y), GetPixelColor(pane2, x, y)) )); + } + + RGBQUAD GetDiffColor() const + { + return m_diffColor; + } + + void SetDiffColor(RGBQUAD clrDiffColor) + { + m_diffColor = clrDiffColor; + RefreshImages(); + } + + RGBQUAD GetSelDiffColor() const + { + return m_selDiffColor; + } + + void SetSelDiffColor(RGBQUAD clrSelDiffColor) + { + m_selDiffColor = clrSelDiffColor; + RefreshImages(); + } + + double GetDiffColorAlpha() const + { + return m_diffColorAlpha; + } + + void SetDiffColorAlpha(double diffColorAlpha) + { + m_diffColorAlpha = diffColorAlpha; + RefreshImages(); + } + + int GetCurrentPage(int pane) const + { + if (pane < 0 || pane >= m_nImages) + return -1; + return m_currentPage[pane]; + } + + void SetCurrentPage(int pane, int page) + { + if (page >= 0 && page < GetPageCount(pane)) + { + if (m_imgOrigMultiPage[pane].isValid()) + { + m_currentPage[pane] = page; + FIBITMAP *bitmap = m_imgOrigMultiPage[pane].lockPage(page); + m_imgOrig[pane] = FreeImage_Clone(bitmap); + m_imgOrig32[pane] = m_imgOrig[pane]; + FreeImage_UnlockPage(m_imgOrigMultiPage[pane], bitmap, false); + m_imgOrig32[pane].convertTo32Bits(); + CompareImages(); + } + } + } + + void SetCurrentPageAll(int page) + { + for (int i = 0; i < m_nImages; ++i) + SetCurrentPage(i, page); + } + + int GetCurrentMaxPage() const + { + int maxpage = 0; + for (int i = 0; i < m_nImages; ++i) + { + int page = GetCurrentPage(i); + maxpage = maxpage < page ? page : maxpage; + } + return maxpage; + } + + int GetPageCount(int pane) const + { + if (pane < 0 || pane >= m_nImages) + return -1; + if (m_imgOrigMultiPage[pane].isValid()) + return m_imgOrigMultiPage[pane].getPageCount(); + else + return 1; + } + + int GetMaxPageCount() const + { + int maxpage = 0; + for (int i = 0; i < m_nImages; ++i) + { + int page = GetPageCount(i); + maxpage = page > maxpage ? page : maxpage; + } + return maxpage; + } + + double GetColorDistanceThreshold() const + { + return m_colorDistanceThreshold; + } + + void SetColorDistanceThreshold(double threshold) + { + m_colorDistanceThreshold = threshold; + CompareImages(); + } + + int GetDiffBlockSize() const + { + return m_diffBlockSize; + } + + void SetDiffBlockSize(int blockSize) + { + m_diffBlockSize = blockSize; + CompareImages(); + } + + OVERLAY_MODE GetOverlayMode() const + { + return m_overlayMode; + } + + void SetOverlayMode(OVERLAY_MODE overlayMode) + { + m_overlayMode = overlayMode; + RefreshImages(); + } + + double GetOverlayAlpha() const + { + return m_overlayAlpha; + } + + void SetOverlayAlpha(double overlayAlpha) + { + m_overlayAlpha = overlayAlpha; + RefreshImages(); + } + + bool GetShowDifferences() const + { + return m_showDifferences; + } + + void SetShowDifferences(bool visible) + { + m_showDifferences = visible; + CompareImages(); + } + + const DiffInfo *GetDiffInfo(int diffIndex) const + { + if (diffIndex < 0 || diffIndex >= m_diffCount) + return NULL; + return &m_diffInfos[diffIndex]; + } + + int GetDiffCount() const + { + return m_diffCount; + } + + int GetConflictCount() const + { + int conflictCount = 0; + for (int i = 0; i < m_diffCount; ++i) + if (m_diffInfos[i].op == DiffInfo::OP_DIFF) + ++conflictCount; + return conflictCount; + } + + int GetCurrentDiffIndex() const + { + return m_currentDiffIndex; + } + + bool FirstDiff() + { + if (m_diffCount == 0) + m_currentDiffIndex = -1; + else + m_currentDiffIndex = 0; + RefreshImages(); + return true; + } + + bool LastDiff() + { + m_currentDiffIndex = m_diffCount - 1; + RefreshImages(); + return true; + } + + bool NextDiff() + { + ++m_currentDiffIndex; + if (m_currentDiffIndex >= m_diffCount) + m_currentDiffIndex = m_diffCount - 1; + RefreshImages(); + return true; + } + + bool PrevDiff() + { + if (m_diffCount == 0) + m_currentDiffIndex = -1; + else + { + --m_currentDiffIndex; + if (m_currentDiffIndex < 0) + m_currentDiffIndex = 0; + } + RefreshImages(); + return true; + } + + bool FirstConflict() + { + for (size_t i = 0; i < m_diffInfos.size(); ++i) + if (m_diffInfos[i].op == DiffInfo::OP_DIFF) + m_currentDiffIndex = static_cast(i); + RefreshImages(); + return true; + } + + bool LastConflict() + { + for (int i = static_cast(m_diffInfos.size() - 1); i >= 0; --i) + { + if (m_diffInfos[i].op == DiffInfo::OP_DIFF) + { + m_currentDiffIndex = i; + break; + } + } + RefreshImages(); + return true; + } + + bool NextConflict() + { + for (size_t i = m_currentDiffIndex + 1; i < m_diffInfos.size(); ++i) + { + if (m_diffInfos[i].op == DiffInfo::OP_DIFF) + { + m_currentDiffIndex = static_cast(i); + break; + } + } + RefreshImages(); + return true; + } + + bool PrevConflict() + { + for (int i = m_currentDiffIndex - 1; i >= 0; --i) + { + if (m_diffInfos[i].op == DiffInfo::OP_DIFF) + { + m_currentDiffIndex = i; + break; + } + } + RefreshImages(); + return true; + } + + bool SelectDiff(int diffIndex) + { + m_currentDiffIndex = diffIndex; + RefreshImages(); + return true; + } + + int GetNextDiffIndex() const + { + if (m_diffCount == 0 || m_currentDiffIndex >= m_diffCount - 1) + return -1; + return m_currentDiffIndex + 1; + } + + int GetPrevDiffIndex() const + { + if (m_diffCount == 0 || m_currentDiffIndex <= 0) + return -1; + return m_currentDiffIndex - 1; + } + + int GetNextConflictIndex() const + { + for (size_t i = m_currentDiffIndex + 1; i < m_diffInfos.size(); ++i) + if (m_diffInfos[i].op == DiffInfo::OP_DIFF) + return static_cast(i); + return -1; + } + + int GetPrevConflictIndex() const + { + for (int i = static_cast(m_currentDiffIndex - 1); i >= 0; --i) + if (m_diffInfos[i].op == DiffInfo::OP_DIFF) + return i; + return -1; + } + + void CompareImages() + { + if (m_nImages <= 1) + return; + InitializeDiff(); + if (m_nImages == 2) + { + CompareImages2(0, 1, m_diff); + m_diffCount = MarkDiffIndex(m_diff); + } + else if (m_nImages == 3) + { + CompareImages2(0, 1, m_diff01); + CompareImages2(2, 1, m_diff21); + CompareImages2(0, 2, m_diff02); + Make3WayDiff(m_diff01, m_diff21, m_diff); + m_diffCount = MarkDiffIndex3way(m_diff01, m_diff21, m_diff02, m_diff); + } + if (m_currentDiffIndex >= m_diffCount) + m_currentDiffIndex = m_diffCount - 1; + RefreshImages(); + } + + void RefreshImages() + { + if (m_nImages <= 1) + return; + InitializeDiffImages(); + for (int i = 0; i < m_nImages; ++i) + CopyOriginalImageToDiffImage(i); + void (CImgDiffBuffer::*func)(int src, int dst) = NULL; + if (m_overlayMode == OVERLAY_ALPHABLEND) + func = &CImgDiffBuffer::AlphaBlendImages2; + else if (m_overlayMode == OVERLAY_XOR) + func = &CImgDiffBuffer::XorImages2; + if (func) + { + if (m_nImages == 2) + { + (this->*func)(1, 0); + (this->*func)(0, 1); + } + else if (m_nImages == 3) + { + (this->*func)(1, 0); + (this->*func)(0, 1); + (this->*func)(2, 1); + (this->*func)(1, 2); + } + } + if (m_showDifferences) + { + for (int i = 0; i < m_nImages; ++i) + MarkDiff(i, m_diff); + } + } + + bool OpenImages(int nImages, const wchar_t * const filename[3]) + { + CloseImages(); + m_nImages = nImages; + for (int i = 0; i < nImages; ++i) + m_filename[i] = filename[i]; + return LoadImages(); + } + + virtual bool CloseImages() + { + for (int i = 0; i < m_nImages; ++i) + { + m_imgOrig[i].clear(); + m_imgOrig32[i].clear(); + } + m_nImages = 0; + return true; + } + + bool SaveDiffImageAs(int pane, const wchar_t *filename) + { + if (pane < 0 || pane >= m_nImages) + return false; +#ifdef _WIN32 + return !!m_imgDiff[pane].saveU(filename); +#else + char filenameA[260]; + snprintf(filenameA, sizeof(filenameA), "%ls", filename); + return !!m_imgDiff[pane].save(filenameA); +#endif + } + + int GetImageWidth(int pane) const + { + if (pane < 0 || pane >= m_nImages) + return -1; + return m_imgOrig32[pane].getWidth(); + } + + int GetImageHeight(int pane) const + { + if (pane < 0 || pane >= m_nImages) + return -1; + return m_imgOrig32[pane].getHeight(); + } + + int GetImageBitsPerPixel(int pane) const + { + if (pane < 0 || pane >= m_nImages) + return -1; + return m_imgOrig[pane].getBitsPerPixel(); + } + + int GetDiffIndexFromPoint(int x, int y) const + { + if (x > 0 && y > 0 && + x < static_cast(m_imgDiff[0].getWidth()) && + y < static_cast(m_imgDiff[0].getHeight())) + { + return m_diff(x / m_diffBlockSize, y / m_diffBlockSize); + } + return -1; + } + + fipWinImage *GetImage(int pane) + { + if (pane < 0 || pane >= m_nImages) + return NULL; + return &m_imgDiff[pane]; + } + +protected: + bool LoadImages() + { + bool bSucceeded = true; + for (int i = 0; i < m_nImages; ++i) + { + m_currentPage[i] = 0; + m_imgOrigMultiPage[i].openU(m_filename[i].c_str(), FALSE, FALSE, 0); + if (m_imgOrigMultiPage[i].isValid()) + { + FIBITMAP *bitmap = m_imgOrigMultiPage[i].lockPage(m_currentPage[i]); + if (bitmap) + { + m_imgOrig[i] = FreeImage_Clone(bitmap); + m_imgOrig32[i] = m_imgOrig[i]; + FreeImage_UnlockPage(m_imgOrigMultiPage[i], bitmap, false); + } + else + m_imgOrigMultiPage[i].close(); + } + if (!m_imgOrigMultiPage[i].isValid()) + { +#ifdef _WIN32 + if (!m_imgOrig[i].loadU(m_filename[i].c_str())) + bSucceeded = false; +#else + char filename[260]; + snprintf(filename, sizeof(filename), "%ls", m_filename[i].c_str()); + if (!m_imgOrig[i].load(filename)) + bSucceeded = false; +#endif + m_imgOrig32[i] = m_imgOrig[i]; + } + + m_imgOrig32[i].convertTo32Bits(); + } + return bSucceeded; + } + + Size GetMaxWidthHeight() + { + unsigned wmax = 0; + unsigned hmax = 0; + for (int i = 0; i < m_nImages; ++i) + { + wmax = (std::max)(wmax, m_imgOrig32[i].getWidth()); + hmax = (std::max)(hmax, m_imgOrig32[i].getHeight()); + } + return Size(wmax, hmax); + } + + void InitializeDiff() + { + Size size = GetMaxWidthHeight(); + int nBlocksX = (size.cx + m_diffBlockSize - 1) / m_diffBlockSize; + int nBlocksY = (size.cy + m_diffBlockSize - 1) / m_diffBlockSize; + + m_diff.clear(); + m_diff.resize(nBlocksX, nBlocksY); + if (m_nImages == 3) + { + m_diff01.clear(); + m_diff01.resize(nBlocksX, nBlocksY); + m_diff21.clear(); + m_diff21.resize(nBlocksX, nBlocksY); + m_diff02.clear(); + m_diff02.resize(nBlocksX, nBlocksY); + } + m_diffInfos.clear(); + } + + void InitializeDiffImages() + { + Size size = GetMaxWidthHeight(); + for (int i = 0; i < m_nImages; ++i) + m_imgDiff[i].setSize(FIT_BITMAP, size.cx, size.cy, 32); + } + + void CompareImages2(int pane1, int pane2, DiffBlocks& diff) + { + unsigned w1 = m_imgOrig32[pane1].getWidth(); + unsigned h1 = m_imgOrig32[pane1].getHeight(); + unsigned w2 = m_imgOrig32[pane2].getWidth(); + unsigned h2 = m_imgOrig32[pane2].getHeight(); + + const unsigned wmax = (std::max)(w1, w2); + const unsigned hmax = (std::max)(h1, h2); + + for (unsigned by = 0; by < diff.height(); ++by) + { + unsigned bsy = (hmax - by * m_diffBlockSize) >= m_diffBlockSize ? m_diffBlockSize : (hmax - by * m_diffBlockSize); + for (unsigned i = 0; i < bsy; ++i) + { + unsigned y = by * m_diffBlockSize + i; + if (y >= h1 || y >= h2) + { + for (unsigned bx = 0; bx < diff.width(); ++bx) + diff(bx, by) = -1; + } + else + { + const BYTE *scanline1 = m_imgOrig32[pane1].getScanLine(h1 - y - 1); + const BYTE *scanline2 = m_imgOrig32[pane2].getScanLine(h2 - y - 1); + if (w1 == w2 && m_colorDistanceThreshold == 0.0) + { + if (memcmp(scanline1, scanline2, w1 * 4) == 0) + continue; + } + for (unsigned x = 0; x < wmax; ++x) + { + if (x >= w1 || x >= w2) + diff(x / m_diffBlockSize, by) = -1; + else + { + if (m_colorDistanceThreshold > 0.0) + { + int bdist = scanline1[x * 4 + 0] - scanline2[x * 4 + 0]; + int gdist = scanline1[x * 4 + 1] - scanline2[x * 4 + 1]; + int rdist = scanline1[x * 4 + 2] - scanline2[x * 4 + 2]; + int adist = scanline1[x * 4 + 3] - scanline2[x * 4 + 3]; + int colorDistance2 = rdist * rdist + gdist * gdist + bdist * bdist + adist * adist; + if (colorDistance2 > m_colorDistanceThreshold * m_colorDistanceThreshold) + diff(x / m_diffBlockSize, by) = -1; + } + else + { + if (scanline1[x * 4 + 0] != scanline2[x * 4 + 0] || + scanline1[x * 4 + 1] != scanline2[x * 4 + 1] || + scanline1[x * 4 + 2] != scanline2[x * 4 + 2] || + scanline1[x * 4 + 3] != scanline2[x * 4 + 3]) + { + diff(x / m_diffBlockSize, by) = -1; + } + } + } + } + } + } + } + } + + void FloodFill8Directions(DiffBlocks& data, int x, int y, unsigned val) + { + std::vector > stack; + stack.push_back(Point(x, y)); + while (!stack.empty()) + { + const Point& pt = stack.back(); + const int x = pt.x; + const int y = pt.y; + stack.pop_back(); + if (data(x, y) != -1) + continue; + data(x, y) = val; + if (x + 1 < static_cast(data.width())) + { + stack.push_back(Point(x + 1, y)); + if (y + 1 < static_cast(data.height())) + stack.push_back(Point(x + 1, y + 1)); + if (y - 1 >= 0) + stack.push_back(Point(x + 1, y - 1)); + } + if (x - 1 >= 0) + { + stack.push_back(Point(x - 1, y)); + if (y + 1 < static_cast(data.height())) + stack.push_back(Point(x - 1, y + 1)); + if (y - 1 >= 0) + stack.push_back(Point(x - 1, y - 1)); + } + if (y + 1 < static_cast(data.height())) + stack.push_back(Point(x, y + 1)); + if (y - 1 >= 0) + stack.push_back(Point(x, y - 1)); + } + } + + int MarkDiffIndex(DiffBlocks& diff) + { + int diffCount = 0; + for (unsigned by = 0; by < diff.height(); ++by) + { + for (unsigned bx = 0; bx < diff.width(); ++bx) + { + int idx = diff(bx, by); + if (idx == -1) + { + m_diffInfos.push_back(DiffInfo(DiffInfo::OP_DIFF, bx, by)); + ++diffCount; + FloodFill8Directions(diff, bx, by, diffCount); + } + else if (idx != 0) + { + Rect& rc = m_diffInfos[idx - 1].rc; + if (static_cast(bx) < rc.left) + rc.left = bx; + else if (static_cast(bx + 1) > rc.right) + rc.right = bx + 1; + if (static_cast(by) < rc.top) + rc.top = by; + else if (static_cast(by + 1) > rc.bottom) + rc.bottom = by + 1; + } + } + } + return diffCount; + } + + int MarkDiffIndex3way(DiffBlocks& diff01, DiffBlocks& diff21, DiffBlocks& diff02, DiffBlocks& diff3) + { + int diffCount = MarkDiffIndex(diff3); + std::vector counter(m_diffInfos.size()); + for (unsigned by = 0; by < diff3.height(); ++by) + { + for (unsigned bx = 0; bx < diff3.width(); ++bx) + { + int diffIndex = diff3(bx, by); + if (diffIndex == 0) + continue; + --diffIndex; + if (diff21(bx, by) == 0) + ++counter[diffIndex].d1; + else if (diff02(bx, by) == 0) + ++counter[diffIndex].d2; + else if (diff01(bx, by) == 0) + ++counter[diffIndex].d3; + else + ++counter[diffIndex].detc; + } + } + + for (size_t i = 0; i < m_diffInfos.size(); ++i) + { + int op; + if (counter[i].d1 != 0 && counter[i].d2 == 0 && counter[i].d3 == 0 && counter[i].detc == 0) + op = DiffInfo::OP_1STONLY; + else if (counter[i].d1 == 0 && counter[i].d2 != 0 && counter[i].d3 == 0 && counter[i].detc == 0) + op = DiffInfo::OP_2NDONLY; + else if (counter[i].d1 == 0 && counter[i].d2 == 0 && counter[i].d3 != 0 && counter[i].detc == 0) + op = DiffInfo::OP_3RDONLY; + else + op = DiffInfo::OP_DIFF; + m_diffInfos[i].op = op; + } + return diffCount; + } + + void Make3WayDiff(const DiffBlocks& diff01, const DiffBlocks& diff21, DiffBlocks& diff3) + { + diff3 = diff01; + for (unsigned bx = 0; bx < diff3.width(); ++bx) + { + for (unsigned by = 0; by < diff3.height(); ++by) + { + if (diff21(bx, by) != 0) + diff3(bx, by) = -1; + } + } + } + + void MarkDiff(int pane, const DiffBlocks& diff) + { + const unsigned w = m_imgDiff[pane].getWidth(); + const unsigned h = m_imgDiff[pane].getHeight(); + + for (unsigned by = 0; by < diff.height(); ++by) + { + for (unsigned bx = 0; bx < diff.width(); ++bx) + { + int diffIndex = diff(bx, by); + if (diffIndex != 0 && ( + (pane == 0 && m_diffInfos[diffIndex - 1].op != DiffInfo::OP_3RDONLY) || + (pane == 1) || + (pane == 2 && m_diffInfos[diffIndex - 1].op != DiffInfo::OP_1STONLY) + )) + { + RGBQUAD color = (diffIndex - 1 == m_currentDiffIndex) ? m_selDiffColor : m_diffColor; + unsigned bsy = (h - by * m_diffBlockSize < m_diffBlockSize) ? (h - by * m_diffBlockSize) : m_diffBlockSize; + for (unsigned i = 0; i < bsy; ++i) + { + unsigned y = by * m_diffBlockSize + i; + BYTE *scanline = m_imgDiff[pane].getScanLine(h - y - 1); + unsigned bsx = (w - bx * m_diffBlockSize < m_diffBlockSize) ? (w - bx * m_diffBlockSize) : m_diffBlockSize; + for (unsigned j = 0; j < bsx; ++j) + { + unsigned x = bx * m_diffBlockSize + j; + scanline[x * 4 + 0] = static_cast(scanline[x * 4 + 0] * (1 - m_diffColorAlpha) + color.rgbBlue * m_diffColorAlpha); + scanline[x * 4 + 1] = static_cast(scanline[x * 4 + 1] * (1 - m_diffColorAlpha) + color.rgbGreen * m_diffColorAlpha); + scanline[x * 4 + 2] = static_cast(scanline[x * 4 + 2] * (1 - m_diffColorAlpha) + color.rgbRed * m_diffColorAlpha); + } + } + } + } + } + } + + void CopyOriginalImageToDiffImage(int dst) + { + unsigned w = m_imgOrig32[dst].getWidth(); + unsigned h = m_imgOrig32[dst].getHeight(); + for (unsigned y = 0; y < h; ++y) + { + const BYTE *scanline_src = m_imgOrig32[dst].getScanLine(h - y - 1); + BYTE *scanline_dst = m_imgDiff[dst].getScanLine(m_imgDiff[dst].getHeight() - y - 1); + for (unsigned x = 0; x < w; ++x) + { + scanline_dst[x * 4 + 0] = scanline_src[x * 4 + 0]; + scanline_dst[x * 4 + 1] = scanline_src[x * 4 + 1]; + scanline_dst[x * 4 + 2] = scanline_src[x * 4 + 2]; + scanline_dst[x * 4 + 3] = scanline_src[x * 4 + 3]; + } + } + } + + void XorImages2(int src, int dst) + { + unsigned w = m_imgOrig32[src].getWidth(); + unsigned h = m_imgOrig32[src].getHeight(); + for (unsigned y = 0; y < h; ++y) + { + const BYTE *scanline_src = m_imgOrig32[src].getScanLine(h - y - 1); + BYTE *scanline_dst = m_imgDiff[dst].getScanLine(m_imgDiff[dst].getHeight() - y - 1); + for (unsigned x = 0; x < w; ++x) + { + scanline_dst[x * 4 + 0] ^= scanline_src[x * 4 + 0]; + scanline_dst[x * 4 + 1] ^= scanline_src[x * 4 + 1]; + scanline_dst[x * 4 + 2] ^= scanline_src[x * 4 + 2]; + } + } + } + + void AlphaBlendImages2(int src, int dst) + { + unsigned w = m_imgOrig32[src].getWidth(); + unsigned h = m_imgOrig32[src].getHeight(); + for (unsigned y = 0; y < h; ++y) + { + const BYTE *scanline_src = m_imgOrig32[src].getScanLine(h - y - 1); + BYTE *scanline_dst = m_imgDiff[dst].getScanLine(m_imgDiff[dst].getHeight() - y - 1); + for (unsigned x = 0; x < w; ++x) + { + scanline_dst[x * 4 + 0] = static_cast(scanline_dst[x * 4 + 0] * (1 - m_overlayAlpha) + scanline_src[x * 4 + 0] * m_overlayAlpha); + scanline_dst[x * 4 + 1] = static_cast(scanline_dst[x * 4 + 1] * (1 - m_overlayAlpha) + scanline_src[x * 4 + 1] * m_overlayAlpha); + scanline_dst[x * 4 + 2] = static_cast(scanline_dst[x * 4 + 2] * (1 - m_overlayAlpha) + scanline_src[x * 4 + 2] * m_overlayAlpha); + scanline_dst[x * 4 + 3] = static_cast(scanline_dst[x * 4 + 3] * (1 - m_overlayAlpha) + scanline_src[x * 4 + 3] * m_overlayAlpha); + } + } + } + + int m_nImages; + fipMultiPageEx m_imgOrigMultiPage[3]; + fipImageEx m_imgOrig[3]; + fipImageEx m_imgOrig32[3]; + fipWinImage m_imgDiff[3]; + std::wstring m_filename[3]; + bool m_showDifferences; + OVERLAY_MODE m_overlayMode; + double m_overlayAlpha; + unsigned m_diffBlockSize; + RGBQUAD m_selDiffColor; + RGBQUAD m_diffColor; + double m_diffColorAlpha; + double m_colorDistanceThreshold; + int m_currentPage[3]; + int m_currentDiffIndex; + int m_diffCount; + DiffBlocks m_diff, m_diff01, m_diff21, m_diff02; + std::vector m_diffInfos; +}; diff --git a/src/CImgMergeBuffer.hpp b/src/CImgMergeBuffer.hpp index 54762f459..d72f7e531 100644 --- a/src/CImgMergeBuffer.hpp +++ b/src/CImgMergeBuffer.hpp @@ -17,119 +17,7 @@ #pragma once -#include "FreeImagePlus.h" -#include -#include -#include -#include -#include -#include - -template struct Point -{ - Point(T x, T y): x(x), y(y) {} - T x, y; -}; - -template struct Size -{ - Size(T cx, T cy): cx(cx), cy(cy) {} - T cx, cy; -}; - -template struct Rect -{ - Rect(T left, T top, T right, T bottom): left(left), top(top), right(right), bottom(bottom) {} - T left, top, right, bottom; -}; - -template struct Array2D -{ - Array2D() : m_width(0), m_height(0), m_data(NULL) - { - } - - Array2D(size_t width, size_t height) : m_width(width), m_height(height), m_data(new T[width * height]) - { - memset(m_data, 0, m_width * m_height * sizeof(T)); - } - - Array2D(const Array2D& other) : m_width(other.m_width), m_height(other.m_height), m_data(new T[other.m_width * other.m_height]) - { - memcpy(m_data, other.m_data, m_width * m_height * sizeof(T)); - } - - Array2D& operator=(const Array2D& other) - { - if (this != &other) - { - delete[] m_data; - m_width = other.m_width; - m_height = other.m_height; - m_data = new T[other.m_width * other.m_height]; - memcpy(m_data, other.m_data, m_width * m_height * sizeof(T)); - } - return *this; - } - - ~Array2D() - { - delete[] m_data; - } - - void resize(size_t width, size_t height) - { - delete[] m_data; - m_data = new T[width * height]; - m_width = width; - m_height = height; - memset(m_data, 0, sizeof(T) * width * height); - } - - T& operator()(int x, int y) - { - return m_data[y * m_width + x]; - } - - const T& operator()(int x, int y) const - { - return m_data[y * m_width + x]; - } - - void clear() - { - delete[] m_data; - m_data = NULL; - m_width = 0; - m_height = 0; - } - - size_t height() const - { - return m_height; - } - - size_t width() const - { - return m_width; - } - - size_t m_width, m_height; - T* m_data; -}; - -struct DiffInfo -{ - enum OP_TYPE - { - OP_NONE = 0, OP_1STONLY, OP_2NDONLY, OP_3RDONLY, OP_DIFF, OP_TRIVIAL - }; - DiffInfo(int op, int x, int y) : op(op), rc(x, y, x + 1, y + 1) {} - int op; - Rect rc; -}; - -struct DiffStat { int d1, d2, d3, detc; }; +#include "CImgDiffBuffer.hpp" struct UndoRecord { @@ -236,262 +124,17 @@ struct UndoRecords int m_modcountonsave[3]; }; -#ifndef _WIN32 -class fipWinImage : public fipImage -{ -public: - fipWinImage(FREE_IMAGE_TYPE image_type = FIT_BITMAP, unsigned width = 0, unsigned height = 0, unsigned bpp = 0) : - fipImage(image_type, width, height, bpp) {} - fipWinImage(const fipWinImage& Image) : fipImage(Image) {} - virtual ~fipWinImage() {} -}; -#endif - -class fipImageEx : public fipImage -{ -public: - fipImageEx(FREE_IMAGE_TYPE image_type = FIT_BITMAP, unsigned width = 0, unsigned height = 0, unsigned bpp = 0) : - fipImage(image_type, width, height, bpp) {} - fipImageEx(const fipImageEx& Image) : fipImage(Image) {} - virtual ~fipImageEx() {} - - fipImageEx& operator=(const fipImageEx& Image) - { - if (this != &Image) - { - FIBITMAP *clone = FreeImage_Clone((FIBITMAP*)Image._dib); - replace(clone); - } - return *this; - } - - fipImageEx& operator=(FIBITMAP *dib) - { - if (_dib != dib) - replace(dib); - return *this; - } - - void swap(fipImageEx& other) - { - std::swap(_dib, other._dib); - std::swap(this->_fif, other._fif); - std::swap(this->_bHasChanged, other._bHasChanged); - } - - FIBITMAP *detach() - { - FIBITMAP *dib = _dib; - _dib = NULL; - clear(); - return dib; - } - - BOOL colorQuantizeEx(FREE_IMAGE_QUANTIZE quantize = FIQ_WUQUANT, int PaletteSize = 256, int ReserveSize = 0, RGBQUAD *ReservePalette = NULL) - { - if(_dib) { - FIBITMAP *dib8 = FreeImage_ColorQuantizeEx(_dib, quantize, PaletteSize, ReserveSize, ReservePalette); - return !!replace(dib8); - } - return false; - } - - bool convertColorDepth(unsigned bpp, RGBQUAD *pPalette = NULL) - { - switch (bpp) - { - case 1: - return !!threshold(128); - case 4: - { - fipImageEx tmp = *this; - tmp.convertTo24Bits(); - if (pPalette) - tmp.colorQuantizeEx(FIQ_NNQUANT, 16, 16, pPalette); - else - tmp.colorQuantizeEx(FIQ_WUQUANT, 16); - setSize(tmp.getImageType(), tmp.getWidth(), tmp.getHeight(), 4); - for (unsigned y = 0; y < tmp.getHeight(); ++y) - { - const BYTE *line_src = tmp.getScanLine(y); - BYTE *line_dst = getScanLine(y); - for (unsigned x = 0; x < tmp.getWidth(); ++x) - line_dst[x / 2] |= ((x % 2) == 0) ? (line_src[x] << 4) : line_src[x]; - } - - RGBQUAD *rgbq_dst = getPalette(); - RGBQUAD *rgbq_src = pPalette ? pPalette : tmp.getPalette(); - memcpy(rgbq_dst, rgbq_src, sizeof(RGBQUAD) * 16); - return true; - } - case 8: - convertTo24Bits(); - if (pPalette) - return !!colorQuantizeEx(FIQ_NNQUANT, 256, 256, pPalette); - else - return !!colorQuantizeEx(FIQ_WUQUANT, 256); - case 15: - return !!convertTo16Bits555(); - case 16: - return !!convertTo16Bits565(); - case 24: - return !!convertTo24Bits(); - default: - case 32: - return !!convertTo32Bits(); - } - } - - void copyAnimationMetadata(fipImage& src) - { - fipTag tag; - fipMetadataFind finder; - if (finder.findFirstMetadata(FIMD_ANIMATION, src, tag)) - { - do - { - setMetadata(FIMD_ANIMATION, tag.getKey(), tag); - } while (finder.findNextMetadata(tag)); - } - } -}; - -class fipMultiPageEx : public fipMultiPage -{ -public: - fipMultiPageEx(BOOL keep_cache_in_memory = FALSE) : fipMultiPage(keep_cache_in_memory) {} - - BOOL openU(const wchar_t* lpszPathName, BOOL create_new, BOOL read_only, int flags = 0) - { - char filename[260]; -#ifdef _WIN32 - wchar_t shortname[260] = {0}; - GetShortPathNameW(lpszPathName, shortname, sizeof(shortname)/sizeof(shortname[0])); - wsprintfA(filename, "%S", shortname); -#else - snprintf(filename, sizeof(filename), "%ls", lpszPathName); - -#endif - BOOL result = open(filename, create_new, read_only, flags); - return result; - } - - bool saveU(const wchar_t* lpszPathName, int flag = 0) const - { - FILE *fp = NULL; -#ifdef _WIN32 - _wfopen_s(&fp, lpszPathName, L"r+b"); -#else - char filename[260]; - snprintf(filename, sizeof(filename), "%ls", lpszPathName); - fp = fopen(filename, "r+b"); -#endif - if (!fp) - return false; - FreeImageIO io; - io.read_proc = myReadProc; - io.write_proc = myWriteProc; - io.seek_proc = mySeekProc; - io.tell_proc = myTellProc; - FREE_IMAGE_FORMAT fif = fipImage::identifyFIFU(lpszPathName); - bool result = !!saveToHandle(fif, &io, (fi_handle)fp, flag); - fclose(fp); - return result; - } - -private: - static unsigned DLL_CALLCONV myReadProc(void *buffer, unsigned size, unsigned count, fi_handle handle) { - return (unsigned)fread(buffer, size, count, (FILE *)handle); - } - - static unsigned DLL_CALLCONV myWriteProc(void *buffer, unsigned size, unsigned count, fi_handle handle) { - return (unsigned)fwrite(buffer, size, count, (FILE *)handle); - } - - static int DLL_CALLCONV mySeekProc(fi_handle handle, long offset, int origin) { - return fseek((FILE *)handle, offset, origin); - } - - static long DLL_CALLCONV myTellProc(fi_handle handle) { - return ftell((FILE *)handle); - } -}; - -namespace -{ - int GetColorDistance2(RGBQUAD c1, RGBQUAD c2) - { - int rdist = c1.rgbRed - c2.rgbRed; - int gdist = c1.rgbGreen - c2.rgbGreen; - int bdist = c1.rgbBlue - c2.rgbBlue; - int adist = c1.rgbReserved - c2.rgbReserved; - return rdist * rdist + gdist * gdist + bdist * bdist + adist * adist; - } -} - -class CImgMergeBuffer +class CImgMergeBuffer : public CImgDiffBuffer { - typedef Array2D DiffBlocks; - public: - enum OVERLAY_MODE { - OVERLAY_NONE = 0, OVERLAY_XOR, OVERLAY_ALPHABLEND - }; - - CImgMergeBuffer() : - m_nImages(0) - , m_showDifferences(true) - , m_overlayMode(OVERLAY_NONE) - , m_overlayAlpha(0.3) - , m_diffBlockSize(8) - , m_selDiffColor() - , m_diffColor() - , m_diffColorAlpha(0.7) - , m_colorDistanceThreshold(0.0) - , m_currentDiffIndex(-1) - , m_diffCount(0) + CImgMergeBuffer() { - m_selDiffColor.rgbRed = 0xff; - m_selDiffColor.rgbGreen = 0x40; - m_selDiffColor.rgbBlue = 0x40; - m_diffColor.rgbRed = 0xff; - m_diffColor.rgbGreen = 0xff; - m_diffColor.rgbBlue = 0x40; for (int i = 0; i < 3; ++i) - { - m_currentPage[i] = 0; m_bRO[i] = false; - } } - ~CImgMergeBuffer() + virtual ~CImgMergeBuffer() { - CloseImages(); - } - - const wchar_t *GetFileName(int pane) - { - if (pane < 0 || pane >= m_nImages) - return NULL; - return m_filename[pane].c_str(); - } - - int GetPaneCount() const - { - return m_nImages; - } - - RGBQUAD GetPixelColor(int pane, int x, int y) const - { - RGBQUAD value = {0xFF, 0xFF, 0xFF, 0x00}; - m_imgOrig32[pane].getPixelColor(x, m_imgOrig32[pane].getHeight() - y - 1, &value); - return value; - } - - double GetColorDistance(int pane1, int pane2, int x, int y) const - { - return std::sqrt(static_cast( - ::GetColorDistance2(GetPixelColor(pane1, x, y), GetPixelColor(pane2, x, y)) )); } bool GetReadOnly(int pane) const @@ -508,310 +151,6 @@ public: m_bRO[pane] = readOnly; } - RGBQUAD GetDiffColor() const - { - return m_diffColor; - } - - void SetDiffColor(RGBQUAD clrDiffColor) - { - m_diffColor = clrDiffColor; - RefreshImages(); - } - - RGBQUAD GetSelDiffColor() const - { - return m_selDiffColor; - } - - void SetSelDiffColor(RGBQUAD clrSelDiffColor) - { - m_selDiffColor = clrSelDiffColor; - RefreshImages(); - } - - double GetDiffColorAlpha() const - { - return m_diffColorAlpha; - } - - void SetDiffColorAlpha(double diffColorAlpha) - { - m_diffColorAlpha = diffColorAlpha; - RefreshImages(); - } - - int GetCurrentPage(int pane) const - { - if (pane < 0 || pane >= m_nImages) - return -1; - return m_currentPage[pane]; - } - - void SetCurrentPage(int pane, int page) - { - if (page >= 0 && page < GetPageCount(pane)) - { - if (m_imgOrigMultiPage[pane].isValid()) - { - m_currentPage[pane] = page; - FIBITMAP *bitmap = m_imgOrigMultiPage[pane].lockPage(page); - m_imgOrig[pane] = FreeImage_Clone(bitmap); - m_imgOrig32[pane] = m_imgOrig[pane]; - FreeImage_UnlockPage(m_imgOrigMultiPage[pane], bitmap, false); - m_imgOrig32[pane].convertTo32Bits(); - CompareImages(); - } - } - } - - void SetCurrentPageAll(int page) - { - for (int i = 0; i < m_nImages; ++i) - SetCurrentPage(i, page); - } - - int GetCurrentMaxPage() const - { - int maxpage = 0; - for (int i = 0; i < m_nImages; ++i) - { - int page = GetCurrentPage(i); - maxpage = maxpage < page ? page : maxpage; - } - return maxpage; - } - - int GetPageCount(int pane) const - { - if (pane < 0 || pane >= m_nImages) - return -1; - if (m_imgOrigMultiPage[pane].isValid()) - return m_imgOrigMultiPage[pane].getPageCount(); - else - return 1; - } - - int GetMaxPageCount() const - { - int maxpage = 0; - for (int i = 0; i < m_nImages; ++i) - { - int page = GetPageCount(i); - maxpage = page > maxpage ? page : maxpage; - } - return maxpage; - } - - double GetColorDistanceThreshold() const - { - return m_colorDistanceThreshold; - } - - void SetColorDistanceThreshold(double threshold) - { - m_colorDistanceThreshold = threshold; - CompareImages(); - } - - int GetDiffBlockSize() const - { - return m_diffBlockSize; - } - - void SetDiffBlockSize(int blockSize) - { - m_diffBlockSize = blockSize; - CompareImages(); - } - - OVERLAY_MODE GetOverlayMode() const - { - return m_overlayMode; - } - - void SetOverlayMode(OVERLAY_MODE overlayMode) - { - m_overlayMode = overlayMode; - RefreshImages(); - } - - double GetOverlayAlpha() const - { - return m_overlayAlpha; - } - - void SetOverlayAlpha(double overlayAlpha) - { - m_overlayAlpha = overlayAlpha; - RefreshImages(); - } - - bool GetShowDifferences() const - { - return m_showDifferences; - } - - void SetShowDifferences(bool visible) - { - m_showDifferences = visible; - CompareImages(); - } - - const DiffInfo *GetDiffInfo(int diffIndex) const - { - if (diffIndex < 0 || diffIndex >= m_diffCount) - return NULL; - return &m_diffInfos[diffIndex]; - } - - int GetDiffCount() const - { - return m_diffCount; - } - - int GetConflictCount() const - { - int conflictCount = 0; - for (int i = 0; i < m_diffCount; ++i) - if (m_diffInfos[i].op == DiffInfo::OP_DIFF) - ++conflictCount; - return conflictCount; - } - - int GetCurrentDiffIndex() const - { - return m_currentDiffIndex; - } - - bool FirstDiff() - { - if (m_diffCount == 0) - m_currentDiffIndex = -1; - else - m_currentDiffIndex = 0; - RefreshImages(); - return true; - } - - bool LastDiff() - { - m_currentDiffIndex = m_diffCount - 1; - RefreshImages(); - return true; - } - - bool NextDiff() - { - ++m_currentDiffIndex; - if (m_currentDiffIndex >= m_diffCount) - m_currentDiffIndex = m_diffCount - 1; - RefreshImages(); - return true; - } - - bool PrevDiff() - { - if (m_diffCount == 0) - m_currentDiffIndex = -1; - else - { - --m_currentDiffIndex; - if (m_currentDiffIndex < 0) - m_currentDiffIndex = 0; - } - RefreshImages(); - return true; - } - - bool FirstConflict() - { - for (size_t i = 0; i < m_diffInfos.size(); ++i) - if (m_diffInfos[i].op == DiffInfo::OP_DIFF) - m_currentDiffIndex = static_cast(i); - RefreshImages(); - return true; - } - - bool LastConflict() - { - for (int i = static_cast(m_diffInfos.size() - 1); i >= 0; --i) - { - if (m_diffInfos[i].op == DiffInfo::OP_DIFF) - { - m_currentDiffIndex = i; - break; - } - } - RefreshImages(); - return true; - } - - bool NextConflict() - { - for (size_t i = m_currentDiffIndex + 1; i < m_diffInfos.size(); ++i) - { - if (m_diffInfos[i].op == DiffInfo::OP_DIFF) - { - m_currentDiffIndex = static_cast(i); - break; - } - } - RefreshImages(); - return true; - } - - bool PrevConflict() - { - for (int i = m_currentDiffIndex - 1; i >= 0; --i) - { - if (m_diffInfos[i].op == DiffInfo::OP_DIFF) - { - m_currentDiffIndex = i; - break; - } - } - RefreshImages(); - return true; - } - - bool SelectDiff(int diffIndex) - { - m_currentDiffIndex = diffIndex; - RefreshImages(); - return true; - } - - int GetNextDiffIndex() const - { - if (m_diffCount == 0 || m_currentDiffIndex >= m_diffCount - 1) - return -1; - return m_currentDiffIndex + 1; - } - - int GetPrevDiffIndex() const - { - if (m_diffCount == 0 || m_currentDiffIndex <= 0) - return -1; - return m_currentDiffIndex - 1; - } - - int GetNextConflictIndex() const - { - for (size_t i = m_currentDiffIndex + 1; i < m_diffInfos.size(); ++i) - if (m_diffInfos[i].op == DiffInfo::OP_DIFF) - return static_cast(i); - return -1; - } - - int GetPrevConflictIndex() const - { - for (int i = static_cast(m_currentDiffIndex - 1); i >= 0; --i) - if (m_diffInfos[i].op == DiffInfo::OP_DIFF) - return i; - return -1; - } - void CopyDiffInternal(int diffIndex, int srcPane, int dstPane) { if (srcPane < 0 || srcPane >= m_nImages) @@ -1001,72 +340,6 @@ public: return true; } - void CompareImages() - { - if (m_nImages <= 1) - return; - InitializeDiff(); - if (m_nImages == 2) - { - CompareImages2(0, 1, m_diff); - m_diffCount = MarkDiffIndex(m_diff); - } - else if (m_nImages == 3) - { - CompareImages2(0, 1, m_diff01); - CompareImages2(2, 1, m_diff21); - CompareImages2(0, 2, m_diff02); - Make3WayDiff(m_diff01, m_diff21, m_diff); - m_diffCount = MarkDiffIndex3way(m_diff01, m_diff21, m_diff02, m_diff); - } - if (m_currentDiffIndex >= m_diffCount) - m_currentDiffIndex = m_diffCount - 1; - RefreshImages(); - } - - void RefreshImages() - { - if (m_nImages <= 1) - return; - InitializeDiffImages(); - for (int i = 0; i < m_nImages; ++i) - CopyOriginalImageToDiffImage(i); - void (CImgMergeBuffer::*func)(int src, int dst) = NULL; - if (m_overlayMode == OVERLAY_ALPHABLEND) - func = &CImgMergeBuffer::AlphaBlendImages2; - else if (m_overlayMode == OVERLAY_XOR) - func = &CImgMergeBuffer::XorImages2; - if (func) - { - if (m_nImages == 2) - { - (this->*func)(1, 0); - (this->*func)(0, 1); - } - else if (m_nImages == 3) - { - (this->*func)(1, 0); - (this->*func)(0, 1); - (this->*func)(2, 1); - (this->*func)(1, 2); - } - } - if (m_showDifferences) - { - for (int i = 0; i < m_nImages; ++i) - MarkDiff(i, m_diff); - } - } - - bool OpenImages(int nImages, const wchar_t * const filename[3]) - { - CloseImages(); - m_nImages = nImages; - for (int i = 0; i < nImages; ++i) - m_filename[i] = filename[i]; - return LoadImages(); - } - bool SaveImage(int pane) { if (pane < 0 || pane >= m_nImages) @@ -1123,439 +396,15 @@ public: } } - bool CloseImages() + virtual bool CloseImages() { for (int i = 0; i < m_nImages; ++i) - { - m_imgOrig[i].clear(); - m_imgOrig32[i].clear(); m_undoRecords.clear(); - } - m_nImages = 0; - return true; - } - - bool SaveDiffImageAs(int pane, const wchar_t *filename) - { - if (pane < 0 || pane >= m_nImages) - return false; -#ifdef _WIN32 - return !!m_imgDiff[pane].saveU(filename); -#else - char filenameA[260]; - snprintf(filenameA, sizeof(filenameA), "%ls", filename); - return !!m_imgDiff[pane].save(filenameA); -#endif - } - - int GetImageWidth(int pane) const - { - if (pane < 0 || pane >= m_nImages) - return -1; - return m_imgOrig32[pane].getWidth(); - } - - int GetImageHeight(int pane) const - { - if (pane < 0 || pane >= m_nImages) - return -1; - return m_imgOrig32[pane].getHeight(); - } - - int GetImageBitsPerPixel(int pane) const - { - if (pane < 0 || pane >= m_nImages) - return -1; - return m_imgOrig[pane].getBitsPerPixel(); - } - - int GetDiffIndexFromPoint(int x, int y) const - { - if (x > 0 && y > 0 && - x < static_cast(m_imgDiff[0].getWidth()) && - y < static_cast(m_imgDiff[0].getHeight())) - { - return m_diff(x / m_diffBlockSize, y / m_diffBlockSize); - } - return -1; - } - - fipWinImage *GetImage(int pane) - { - if (pane < 0 || pane >= m_nImages) - return NULL; - return &m_imgDiff[pane]; + return CImgDiffBuffer::CloseImages(); } private: - bool LoadImages() - { - bool bSucceeded = true; - for (int i = 0; i < m_nImages; ++i) - { - m_currentPage[i] = 0; - m_imgOrigMultiPage[i].openU(m_filename[i].c_str(), FALSE, FALSE, 0); - if (m_imgOrigMultiPage[i].isValid()) - { - FIBITMAP *bitmap = m_imgOrigMultiPage[i].lockPage(m_currentPage[i]); - if (bitmap) - { - m_imgOrig[i] = FreeImage_Clone(bitmap); - m_imgOrig32[i] = m_imgOrig[i]; - FreeImage_UnlockPage(m_imgOrigMultiPage[i], bitmap, false); - } - else - m_imgOrigMultiPage[i].close(); - } - if (!m_imgOrigMultiPage[i].isValid()) - { -#ifdef _WIN32 - if (!m_imgOrig[i].loadU(m_filename[i].c_str())) - bSucceeded = false; -#else - char filename[260]; - snprintf(filename, sizeof(filename), "%ls", m_filename[i].c_str()); - if (!m_imgOrig[i].load(filename)) - bSucceeded = false; -#endif - m_imgOrig32[i] = m_imgOrig[i]; - } - - m_imgOrig32[i].convertTo32Bits(); - } - return bSucceeded; - } - - Size GetMaxWidthHeight() - { - unsigned wmax = 0; - unsigned hmax = 0; - for (int i = 0; i < m_nImages; ++i) - { - wmax = (std::max)(wmax, m_imgOrig32[i].getWidth()); - hmax = (std::max)(hmax, m_imgOrig32[i].getHeight()); - } - return Size(wmax, hmax); - } - - void InitializeDiff() - { - Size size = GetMaxWidthHeight(); - int nBlocksX = (size.cx + m_diffBlockSize - 1) / m_diffBlockSize; - int nBlocksY = (size.cy + m_diffBlockSize - 1) / m_diffBlockSize; - - m_diff.clear(); - m_diff.resize(nBlocksX, nBlocksY); - if (m_nImages == 3) - { - m_diff01.clear(); - m_diff01.resize(nBlocksX, nBlocksY); - m_diff21.clear(); - m_diff21.resize(nBlocksX, nBlocksY); - m_diff02.clear(); - m_diff02.resize(nBlocksX, nBlocksY); - } - m_diffInfos.clear(); - } - - void InitializeDiffImages() - { - Size size = GetMaxWidthHeight(); - for (int i = 0; i < m_nImages; ++i) - m_imgDiff[i].setSize(FIT_BITMAP, size.cx, size.cy, 32); - } - - void CompareImages2(int pane1, int pane2, DiffBlocks& diff) - { - unsigned w1 = m_imgOrig32[pane1].getWidth(); - unsigned h1 = m_imgOrig32[pane1].getHeight(); - unsigned w2 = m_imgOrig32[pane2].getWidth(); - unsigned h2 = m_imgOrig32[pane2].getHeight(); - - const unsigned wmax = (std::max)(w1, w2); - const unsigned hmax = (std::max)(h1, h2); - - for (unsigned by = 0; by < diff.height(); ++by) - { - unsigned bsy = (hmax - by * m_diffBlockSize) >= m_diffBlockSize ? m_diffBlockSize : (hmax - by * m_diffBlockSize); - for (unsigned i = 0; i < bsy; ++i) - { - unsigned y = by * m_diffBlockSize + i; - if (y >= h1 || y >= h2) - { - for (unsigned bx = 0; bx < diff.width(); ++bx) - diff(bx, by) = -1; - } - else - { - const BYTE *scanline1 = m_imgOrig32[pane1].getScanLine(h1 - y - 1); - const BYTE *scanline2 = m_imgOrig32[pane2].getScanLine(h2 - y - 1); - if (w1 == w2 && m_colorDistanceThreshold == 0.0) - { - if (memcmp(scanline1, scanline2, w1 * 4) == 0) - continue; - } - for (unsigned x = 0; x < wmax; ++x) - { - if (x >= w1 || x >= w2) - diff(x / m_diffBlockSize, by) = -1; - else - { - if (m_colorDistanceThreshold > 0.0) - { - int bdist = scanline1[x * 4 + 0] - scanline2[x * 4 + 0]; - int gdist = scanline1[x * 4 + 1] - scanline2[x * 4 + 1]; - int rdist = scanline1[x * 4 + 2] - scanline2[x * 4 + 2]; - int adist = scanline1[x * 4 + 3] - scanline2[x * 4 + 3]; - int colorDistance2 = rdist * rdist + gdist * gdist + bdist * bdist + adist * adist; - if (colorDistance2 > m_colorDistanceThreshold * m_colorDistanceThreshold) - diff(x / m_diffBlockSize, by) = -1; - } - else - { - if (scanline1[x * 4 + 0] != scanline2[x * 4 + 0] || - scanline1[x * 4 + 1] != scanline2[x * 4 + 1] || - scanline1[x * 4 + 2] != scanline2[x * 4 + 2] || - scanline1[x * 4 + 3] != scanline2[x * 4 + 3]) - { - diff(x / m_diffBlockSize, by) = -1; - } - } - } - } - } - } - } - } - - void FloodFill8Directions(DiffBlocks& data, int x, int y, unsigned val) - { - std::vector > stack; - stack.push_back(Point(x, y)); - while (!stack.empty()) - { - const Point& pt = stack.back(); - const int x = pt.x; - const int y = pt.y; - stack.pop_back(); - if (data(x, y) != -1) - continue; - data(x, y) = val; - if (x + 1 < static_cast(data.width())) - { - stack.push_back(Point(x + 1, y)); - if (y + 1 < static_cast(data.height())) - stack.push_back(Point(x + 1, y + 1)); - if (y - 1 >= 0) - stack.push_back(Point(x + 1, y - 1)); - } - if (x - 1 >= 0) - { - stack.push_back(Point(x - 1, y)); - if (y + 1 < static_cast(data.height())) - stack.push_back(Point(x - 1, y + 1)); - if (y - 1 >= 0) - stack.push_back(Point(x - 1, y - 1)); - } - if (y + 1 < static_cast(data.height())) - stack.push_back(Point(x, y + 1)); - if (y - 1 >= 0) - stack.push_back(Point(x, y - 1)); - } - } - - int MarkDiffIndex(DiffBlocks& diff) - { - int diffCount = 0; - for (unsigned by = 0; by < diff.height(); ++by) - { - for (unsigned bx = 0; bx < diff.width(); ++bx) - { - int idx = diff(bx, by); - if (idx == -1) - { - m_diffInfos.push_back(DiffInfo(DiffInfo::OP_DIFF, bx, by)); - ++diffCount; - FloodFill8Directions(diff, bx, by, diffCount); - } - else if (idx != 0) - { - Rect& rc = m_diffInfos[idx - 1].rc; - if (static_cast(bx) < rc.left) - rc.left = bx; - else if (static_cast(bx + 1) > rc.right) - rc.right = bx + 1; - if (static_cast(by) < rc.top) - rc.top = by; - else if (static_cast(by + 1) > rc.bottom) - rc.bottom = by + 1; - } - } - } - return diffCount; - } - - int MarkDiffIndex3way(DiffBlocks& diff01, DiffBlocks& diff21, DiffBlocks& diff02, DiffBlocks& diff3) - { - int diffCount = MarkDiffIndex(diff3); - std::vector counter(m_diffInfos.size()); - for (unsigned by = 0; by < diff3.height(); ++by) - { - for (unsigned bx = 0; bx < diff3.width(); ++bx) - { - int diffIndex = diff3(bx, by); - if (diffIndex == 0) - continue; - --diffIndex; - if (diff21(bx, by) == 0) - ++counter[diffIndex].d1; - else if (diff02(bx, by) == 0) - ++counter[diffIndex].d2; - else if (diff01(bx, by) == 0) - ++counter[diffIndex].d3; - else - ++counter[diffIndex].detc; - } - } - - for (size_t i = 0; i < m_diffInfos.size(); ++i) - { - int op; - if (counter[i].d1 != 0 && counter[i].d2 == 0 && counter[i].d3 == 0 && counter[i].detc == 0) - op = DiffInfo::OP_1STONLY; - else if (counter[i].d1 == 0 && counter[i].d2 != 0 && counter[i].d3 == 0 && counter[i].detc == 0) - op = DiffInfo::OP_2NDONLY; - else if (counter[i].d1 == 0 && counter[i].d2 == 0 && counter[i].d3 != 0 && counter[i].detc == 0) - op = DiffInfo::OP_3RDONLY; - else - op = DiffInfo::OP_DIFF; - m_diffInfos[i].op = op; - } - return diffCount; - } - - void Make3WayDiff(const DiffBlocks& diff01, const DiffBlocks& diff21, DiffBlocks& diff3) - { - diff3 = diff01; - for (unsigned bx = 0; bx < diff3.width(); ++bx) - { - for (unsigned by = 0; by < diff3.height(); ++by) - { - if (diff21(bx, by) != 0) - diff3(bx, by) = -1; - } - } - } - - void MarkDiff(int pane, const DiffBlocks& diff) - { - const unsigned w = m_imgDiff[pane].getWidth(); - const unsigned h = m_imgDiff[pane].getHeight(); - - for (unsigned by = 0; by < diff.height(); ++by) - { - for (unsigned bx = 0; bx < diff.width(); ++bx) - { - int diffIndex = diff(bx, by); - if (diffIndex != 0 && ( - (pane == 0 && m_diffInfos[diffIndex - 1].op != DiffInfo::OP_3RDONLY) || - (pane == 1) || - (pane == 2 && m_diffInfos[diffIndex - 1].op != DiffInfo::OP_1STONLY) - )) - { - RGBQUAD color = (diffIndex - 1 == m_currentDiffIndex) ? m_selDiffColor : m_diffColor; - unsigned bsy = (h - by * m_diffBlockSize < m_diffBlockSize) ? (h - by * m_diffBlockSize) : m_diffBlockSize; - for (unsigned i = 0; i < bsy; ++i) - { - unsigned y = by * m_diffBlockSize + i; - BYTE *scanline = m_imgDiff[pane].getScanLine(h - y - 1); - unsigned bsx = (w - bx * m_diffBlockSize < m_diffBlockSize) ? (w - bx * m_diffBlockSize) : m_diffBlockSize; - for (unsigned j = 0; j < bsx; ++j) - { - unsigned x = bx * m_diffBlockSize + j; - scanline[x * 4 + 0] = static_cast(scanline[x * 4 + 0] * (1 - m_diffColorAlpha) + color.rgbBlue * m_diffColorAlpha); - scanline[x * 4 + 1] = static_cast(scanline[x * 4 + 1] * (1 - m_diffColorAlpha) + color.rgbGreen * m_diffColorAlpha); - scanline[x * 4 + 2] = static_cast(scanline[x * 4 + 2] * (1 - m_diffColorAlpha) + color.rgbRed * m_diffColorAlpha); - } - } - } - } - } - } - - void CopyOriginalImageToDiffImage(int dst) - { - unsigned w = m_imgOrig32[dst].getWidth(); - unsigned h = m_imgOrig32[dst].getHeight(); - for (unsigned y = 0; y < h; ++y) - { - const BYTE *scanline_src = m_imgOrig32[dst].getScanLine(h - y - 1); - BYTE *scanline_dst = m_imgDiff[dst].getScanLine(m_imgDiff[dst].getHeight() - y - 1); - for (unsigned x = 0; x < w; ++x) - { - scanline_dst[x * 4 + 0] = scanline_src[x * 4 + 0]; - scanline_dst[x * 4 + 1] = scanline_src[x * 4 + 1]; - scanline_dst[x * 4 + 2] = scanline_src[x * 4 + 2]; - scanline_dst[x * 4 + 3] = scanline_src[x * 4 + 3]; - } - } - } - - void XorImages2(int src, int dst) - { - unsigned w = m_imgOrig32[src].getWidth(); - unsigned h = m_imgOrig32[src].getHeight(); - for (unsigned y = 0; y < h; ++y) - { - const BYTE *scanline_src = m_imgOrig32[src].getScanLine(h - y - 1); - BYTE *scanline_dst = m_imgDiff[dst].getScanLine(m_imgDiff[dst].getHeight() - y - 1); - for (unsigned x = 0; x < w; ++x) - { - scanline_dst[x * 4 + 0] ^= scanline_src[x * 4 + 0]; - scanline_dst[x * 4 + 1] ^= scanline_src[x * 4 + 1]; - scanline_dst[x * 4 + 2] ^= scanline_src[x * 4 + 2]; - } - } - } - - void AlphaBlendImages2(int src, int dst) - { - unsigned w = m_imgOrig32[src].getWidth(); - unsigned h = m_imgOrig32[src].getHeight(); - for (unsigned y = 0; y < h; ++y) - { - const BYTE *scanline_src = m_imgOrig32[src].getScanLine(h - y - 1); - BYTE *scanline_dst = m_imgDiff[dst].getScanLine(m_imgDiff[dst].getHeight() - y - 1); - for (unsigned x = 0; x < w; ++x) - { - scanline_dst[x * 4 + 0] = static_cast(scanline_dst[x * 4 + 0] * (1 - m_overlayAlpha) + scanline_src[x * 4 + 0] * m_overlayAlpha); - scanline_dst[x * 4 + 1] = static_cast(scanline_dst[x * 4 + 1] * (1 - m_overlayAlpha) + scanline_src[x * 4 + 1] * m_overlayAlpha); - scanline_dst[x * 4 + 2] = static_cast(scanline_dst[x * 4 + 2] * (1 - m_overlayAlpha) + scanline_src[x * 4 + 2] * m_overlayAlpha); - scanline_dst[x * 4 + 3] = static_cast(scanline_dst[x * 4 + 3] * (1 - m_overlayAlpha) + scanline_src[x * 4 + 3] * m_overlayAlpha); - } - } - } - - int m_nImages; - fipMultiPageEx m_imgOrigMultiPage[3]; - fipImageEx m_imgOrig[3]; - fipImageEx m_imgOrig32[3]; - fipWinImage m_imgDiff[3]; - std::wstring m_filename[3]; bool m_bRO[3]; - bool m_showDifferences; - OVERLAY_MODE m_overlayMode; - double m_overlayAlpha; - unsigned m_diffBlockSize; - RGBQUAD m_selDiffColor; - RGBQUAD m_diffColor; - double m_diffColorAlpha; - double m_colorDistanceThreshold; - int m_currentPage[3]; - int m_currentDiffIndex; - int m_diffCount; - DiffBlocks m_diff, m_diff01, m_diff21, m_diff02; - std::vector m_diffInfos; UndoRecords m_undoRecords; }; diff --git a/src/Makefile b/src/Makefile index 271f5c6bb..8b79325c0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2,7 +2,7 @@ TARGETS=cidiff CXXFLAGS+=-Wall -Wextra -I../../freeimage/Source -I../../freeimage/Wrapper/FreeImagePlus SRCS=cidiff.cpp OBJS=$(SRCS:.cpp=*.o) -HEADERS=CImgMergeBuffer.hpp +HEADERS=CImgDiffBuffer.hpp CImgMergeBuffer.hpp LIBS=-L../../freeimage/ -lfreeimage -L../../freeimage/ -lfreeimageplus all: $(TARGETS) diff --git a/src/WinIMergeLib.vcxproj b/src/WinIMergeLib.vcxproj index e5e7a1e0b..979bc5974 100644 --- a/src/WinIMergeLib.vcxproj +++ b/src/WinIMergeLib.vcxproj @@ -169,6 +169,7 @@ + diff --git a/src/WinIMergeLib.vcxproj.filters b/src/WinIMergeLib.vcxproj.filters index 3cb919728..eab8e5d4e 100644 --- a/src/WinIMergeLib.vcxproj.filters +++ b/src/WinIMergeLib.vcxproj.filters @@ -30,6 +30,9 @@ Header Files + + Header Files + diff --git a/src/cidiff.cpp b/src/cidiff.cpp index 6aa09e177..ad9020e97 100644 --- a/src/cidiff.cpp +++ b/src/cidiff.cpp @@ -1,10 +1,10 @@ -#include "CImgMergeBuffer.hpp" +#include "CImgDiffBuffer.hpp" #include #include int main(int argc, char* argv[]) { - CImgMergeBuffer buffer; + CImgDiffBuffer buffer; wchar_t filenameW[2][260]; const wchar_t *filenames[2] = { filenameW[0], filenameW[1] }; -- 2.11.0 From dd582eb491ec3b96167176ae8dc0f9e9f4bafb5d Mon Sep 17 00:00:00 2001 From: sdottaka Date: Sun, 19 Oct 2014 10:32:50 +0900 Subject: [PATCH 11/16] Remove FreeImage dependency from CImgDiffBuffer.hpp and CImgMergeBuffer.hpp --- src/CImgDiffBuffer.hpp | 377 ++++++++++----------------------------- src/CImgMergeBuffer.hpp | 74 +++----- src/CImgMergeWindow.hpp | 6 +- src/Makefile | 2 +- src/WinIMergeLib.vcxproj | 1 + src/WinIMergeLib.vcxproj.filters | 3 + src/image.hpp | 296 ++++++++++++++++++++++++++++++ 7 files changed, 428 insertions(+), 331 deletions(-) create mode 100644 src/image.hpp diff --git a/src/CImgDiffBuffer.hpp b/src/CImgDiffBuffer.hpp index 6b513094d..bfe8c36ff 100644 --- a/src/CImgDiffBuffer.hpp +++ b/src/CImgDiffBuffer.hpp @@ -17,7 +17,7 @@ #pragma once -#include "FreeImagePlus.h" +#include "image.hpp" #include #include #include @@ -131,195 +131,14 @@ struct DiffInfo struct DiffStat { int d1, d2, d3, detc; }; -#ifndef _WIN32 -class fipWinImage : public fipImage -{ -public: - fipWinImage(FREE_IMAGE_TYPE image_type = FIT_BITMAP, unsigned width = 0, unsigned height = 0, unsigned bpp = 0) : - fipImage(image_type, width, height, bpp) {} - fipWinImage(const fipWinImage& Image) : fipImage(Image) {} - virtual ~fipWinImage() {} -}; -#endif - -class fipImageEx : public fipImage -{ -public: - fipImageEx(FREE_IMAGE_TYPE image_type = FIT_BITMAP, unsigned width = 0, unsigned height = 0, unsigned bpp = 0) : - fipImage(image_type, width, height, bpp) {} - fipImageEx(const fipImageEx& Image) : fipImage(Image) {} - virtual ~fipImageEx() {} - - fipImageEx& operator=(const fipImageEx& Image) - { - if (this != &Image) - { - FIBITMAP *clone = FreeImage_Clone((FIBITMAP*)Image._dib); - replace(clone); - } - return *this; - } - - fipImageEx& operator=(FIBITMAP *dib) - { - if (_dib != dib) - replace(dib); - return *this; - } - - void swap(fipImageEx& other) - { - std::swap(_dib, other._dib); - std::swap(this->_fif, other._fif); - std::swap(this->_bHasChanged, other._bHasChanged); - } - - FIBITMAP *detach() - { - FIBITMAP *dib = _dib; - _dib = NULL; - clear(); - return dib; - } - - BOOL colorQuantizeEx(FREE_IMAGE_QUANTIZE quantize = FIQ_WUQUANT, int PaletteSize = 256, int ReserveSize = 0, RGBQUAD *ReservePalette = NULL) - { - if(_dib) { - FIBITMAP *dib8 = FreeImage_ColorQuantizeEx(_dib, quantize, PaletteSize, ReserveSize, ReservePalette); - return !!replace(dib8); - } - return false; - } - - bool convertColorDepth(unsigned bpp, RGBQUAD *pPalette = NULL) - { - switch (bpp) - { - case 1: - return !!threshold(128); - case 4: - { - fipImageEx tmp = *this; - tmp.convertTo24Bits(); - if (pPalette) - tmp.colorQuantizeEx(FIQ_NNQUANT, 16, 16, pPalette); - else - tmp.colorQuantizeEx(FIQ_WUQUANT, 16); - setSize(tmp.getImageType(), tmp.getWidth(), tmp.getHeight(), 4); - for (unsigned y = 0; y < tmp.getHeight(); ++y) - { - const BYTE *line_src = tmp.getScanLine(y); - BYTE *line_dst = getScanLine(y); - for (unsigned x = 0; x < tmp.getWidth(); ++x) - line_dst[x / 2] |= ((x % 2) == 0) ? (line_src[x] << 4) : line_src[x]; - } - - RGBQUAD *rgbq_dst = getPalette(); - RGBQUAD *rgbq_src = pPalette ? pPalette : tmp.getPalette(); - memcpy(rgbq_dst, rgbq_src, sizeof(RGBQUAD) * 16); - return true; - } - case 8: - convertTo24Bits(); - if (pPalette) - return !!colorQuantizeEx(FIQ_NNQUANT, 256, 256, pPalette); - else - return !!colorQuantizeEx(FIQ_WUQUANT, 256); - case 15: - return !!convertTo16Bits555(); - case 16: - return !!convertTo16Bits565(); - case 24: - return !!convertTo24Bits(); - default: - case 32: - return !!convertTo32Bits(); - } - } - - void copyAnimationMetadata(fipImage& src) - { - fipTag tag; - fipMetadataFind finder; - if (finder.findFirstMetadata(FIMD_ANIMATION, src, tag)) - { - do - { - setMetadata(FIMD_ANIMATION, tag.getKey(), tag); - } while (finder.findNextMetadata(tag)); - } - } -}; - -class fipMultiPageEx : public fipMultiPage -{ -public: - fipMultiPageEx(BOOL keep_cache_in_memory = FALSE) : fipMultiPage(keep_cache_in_memory) {} - - BOOL openU(const wchar_t* lpszPathName, BOOL create_new, BOOL read_only, int flags = 0) - { - char filename[260]; -#ifdef _WIN32 - wchar_t shortname[260] = {0}; - GetShortPathNameW(lpszPathName, shortname, sizeof(shortname)/sizeof(shortname[0])); - wsprintfA(filename, "%S", shortname); -#else - snprintf(filename, sizeof(filename), "%ls", lpszPathName); - -#endif - BOOL result = open(filename, create_new, read_only, flags); - return result; - } - - bool saveU(const wchar_t* lpszPathName, int flag = 0) const - { - FILE *fp = NULL; -#ifdef _WIN32 - _wfopen_s(&fp, lpszPathName, L"r+b"); -#else - char filename[260]; - snprintf(filename, sizeof(filename), "%ls", lpszPathName); - fp = fopen(filename, "r+b"); -#endif - if (!fp) - return false; - FreeImageIO io; - io.read_proc = myReadProc; - io.write_proc = myWriteProc; - io.seek_proc = mySeekProc; - io.tell_proc = myTellProc; - FREE_IMAGE_FORMAT fif = fipImage::identifyFIFU(lpszPathName); - bool result = !!saveToHandle(fif, &io, (fi_handle)fp, flag); - fclose(fp); - return result; - } - -private: - static unsigned DLL_CALLCONV myReadProc(void *buffer, unsigned size, unsigned count, fi_handle handle) { - return (unsigned)fread(buffer, size, count, (FILE *)handle); - } - - static unsigned DLL_CALLCONV myWriteProc(void *buffer, unsigned size, unsigned count, fi_handle handle) { - return (unsigned)fwrite(buffer, size, count, (FILE *)handle); - } - - static int DLL_CALLCONV mySeekProc(fi_handle handle, long offset, int origin) { - return fseek((FILE *)handle, offset, origin); - } - - static long DLL_CALLCONV myTellProc(fi_handle handle) { - return ftell((FILE *)handle); - } -}; - namespace { - int GetColorDistance2(RGBQUAD c1, RGBQUAD c2) + int GetColorDistance2(Image::Color c1, Image::Color c2) { - int rdist = c1.rgbRed - c2.rgbRed; - int gdist = c1.rgbGreen - c2.rgbGreen; - int bdist = c1.rgbBlue - c2.rgbBlue; - int adist = c1.rgbReserved - c2.rgbReserved; + int rdist = Image::valueR(c1) - Image::valueR(c2); + int gdist = Image::valueG(c1) - Image::valueG(c2); + int bdist = Image::valueB(c1) - Image::valueB(c2); + int adist = Image::valueA(c1) - Image::valueA(c2); return rdist * rdist + gdist * gdist + bdist * bdist + adist * adist; } } @@ -339,19 +158,13 @@ public: , m_overlayMode(OVERLAY_NONE) , m_overlayAlpha(0.3) , m_diffBlockSize(8) - , m_selDiffColor() - , m_diffColor() + , m_selDiffColor(Image::Rgb(0xff, 0x40, 0x40)) + , m_diffColor(Image::Rgb(0xff, 0xff, 0x40)) , m_diffColorAlpha(0.7) , m_colorDistanceThreshold(0.0) , m_currentDiffIndex(-1) , m_diffCount(0) { - m_selDiffColor.rgbRed = 0xff; - m_selDiffColor.rgbGreen = 0x40; - m_selDiffColor.rgbBlue = 0x40; - m_diffColor.rgbRed = 0xff; - m_diffColor.rgbGreen = 0xff; - m_diffColor.rgbBlue = 0x40; for (int i = 0; i < 3; ++i) m_currentPage[i] = 0; } @@ -373,11 +186,9 @@ public: return m_nImages; } - RGBQUAD GetPixelColor(int pane, int x, int y) const + Image::Color GetPixelColor(int pane, int x, int y) const { - RGBQUAD value = {0xFF, 0xFF, 0xFF, 0x00}; - m_imgOrig32[pane].getPixelColor(x, m_imgOrig32[pane].getHeight() - y - 1, &value); - return value; + return m_imgOrig32[pane].pixel(x, y); } double GetColorDistance(int pane1, int pane2, int x, int y) const @@ -386,23 +197,23 @@ public: ::GetColorDistance2(GetPixelColor(pane1, x, y), GetPixelColor(pane2, x, y)) )); } - RGBQUAD GetDiffColor() const + Image::Color GetDiffColor() const { return m_diffColor; } - void SetDiffColor(RGBQUAD clrDiffColor) + void SetDiffColor(Image::Color clrDiffColor) { m_diffColor = clrDiffColor; RefreshImages(); } - RGBQUAD GetSelDiffColor() const + Image::Color GetSelDiffColor() const { return m_selDiffColor; } - void SetSelDiffColor(RGBQUAD clrSelDiffColor) + void SetSelDiffColor(Image::Color clrSelDiffColor) { m_selDiffColor = clrSelDiffColor; RefreshImages(); @@ -433,10 +244,8 @@ public: if (m_imgOrigMultiPage[pane].isValid()) { m_currentPage[pane] = page; - FIBITMAP *bitmap = m_imgOrigMultiPage[pane].lockPage(page); - m_imgOrig[pane] = FreeImage_Clone(bitmap); + m_imgOrig[pane] = m_imgOrigMultiPage[pane].getImage(page); m_imgOrig32[pane] = m_imgOrig[pane]; - FreeImage_UnlockPage(m_imgOrigMultiPage[pane], bitmap, false); m_imgOrig32[pane].convertTo32Bits(); CompareImages(); } @@ -564,32 +373,42 @@ public: bool FirstDiff() { + int oldDiffIndex = m_currentDiffIndex; if (m_diffCount == 0) m_currentDiffIndex = -1; else m_currentDiffIndex = 0; + if (oldDiffIndex == m_currentDiffIndex) + return false; RefreshImages(); return true; } bool LastDiff() { + int oldDiffIndex = m_currentDiffIndex; m_currentDiffIndex = m_diffCount - 1; + if (oldDiffIndex == m_currentDiffIndex) + return false; RefreshImages(); return true; } bool NextDiff() { + int oldDiffIndex = m_currentDiffIndex; ++m_currentDiffIndex; if (m_currentDiffIndex >= m_diffCount) m_currentDiffIndex = m_diffCount - 1; + if (oldDiffIndex == m_currentDiffIndex) + return false; RefreshImages(); return true; } bool PrevDiff() { + int oldDiffIndex = m_currentDiffIndex; if (m_diffCount == 0) m_currentDiffIndex = -1; else @@ -598,21 +417,27 @@ public: if (m_currentDiffIndex < 0) m_currentDiffIndex = 0; } + if (oldDiffIndex == m_currentDiffIndex) + return false; RefreshImages(); return true; } bool FirstConflict() { + int oldDiffIndex = m_currentDiffIndex; for (size_t i = 0; i < m_diffInfos.size(); ++i) if (m_diffInfos[i].op == DiffInfo::OP_DIFF) m_currentDiffIndex = static_cast(i); + if (oldDiffIndex == m_currentDiffIndex) + return false; RefreshImages(); return true; } bool LastConflict() { + int oldDiffIndex = m_currentDiffIndex; for (int i = static_cast(m_diffInfos.size() - 1); i >= 0; --i) { if (m_diffInfos[i].op == DiffInfo::OP_DIFF) @@ -621,12 +446,15 @@ public: break; } } + if (oldDiffIndex == m_currentDiffIndex) + return false; RefreshImages(); return true; } bool NextConflict() { + int oldDiffIndex = m_currentDiffIndex; for (size_t i = m_currentDiffIndex + 1; i < m_diffInfos.size(); ++i) { if (m_diffInfos[i].op == DiffInfo::OP_DIFF) @@ -635,12 +463,15 @@ public: break; } } + if (oldDiffIndex == m_currentDiffIndex) + return false; RefreshImages(); return true; } bool PrevConflict() { + int oldDiffIndex = m_currentDiffIndex; for (int i = m_currentDiffIndex - 1; i >= 0; --i) { if (m_diffInfos[i].op == DiffInfo::OP_DIFF) @@ -649,12 +480,16 @@ public: break; } } + if (oldDiffIndex == m_currentDiffIndex) + return false; RefreshImages(); return true; } bool SelectDiff(int diffIndex) { + if (diffIndex == m_currentDiffIndex || diffIndex < -1 || diffIndex >= m_diffCount) + return false; m_currentDiffIndex = diffIndex; RefreshImages(); return true; @@ -771,48 +606,42 @@ public: { if (pane < 0 || pane >= m_nImages) return false; -#ifdef _WIN32 - return !!m_imgDiff[pane].saveU(filename); -#else - char filenameA[260]; - snprintf(filenameA, sizeof(filenameA), "%ls", filename); - return !!m_imgDiff[pane].save(filenameA); -#endif + return !!m_imgDiff[pane].save(filename); } int GetImageWidth(int pane) const { if (pane < 0 || pane >= m_nImages) return -1; - return m_imgOrig32[pane].getWidth(); + return m_imgOrig32[pane].width(); } int GetImageHeight(int pane) const { if (pane < 0 || pane >= m_nImages) return -1; - return m_imgOrig32[pane].getHeight(); + return m_imgOrig32[pane].height(); } int GetImageBitsPerPixel(int pane) const { if (pane < 0 || pane >= m_nImages) return -1; - return m_imgOrig[pane].getBitsPerPixel(); + return m_imgOrig[pane].depth(); } int GetDiffIndexFromPoint(int x, int y) const { if (x > 0 && y > 0 && - x < static_cast(m_imgDiff[0].getWidth()) && - y < static_cast(m_imgDiff[0].getHeight())) + x < static_cast(m_imgDiff[0].width()) && + y < static_cast(m_imgDiff[0].height())) { - return m_diff(x / m_diffBlockSize, y / m_diffBlockSize); + return m_diff(x / m_diffBlockSize, y / m_diffBlockSize) - 1; } return -1; } - fipWinImage *GetImage(int pane) + Image *GetImage(int pane) { if (pane < 0 || pane >= m_nImages) return NULL; @@ -826,33 +655,19 @@ protected: for (int i = 0; i < m_nImages; ++i) { m_currentPage[i] = 0; - m_imgOrigMultiPage[i].openU(m_filename[i].c_str(), FALSE, FALSE, 0); - if (m_imgOrigMultiPage[i].isValid()) + m_imgOrigMultiPage[i].load(m_filename[i]); + if (m_imgOrigMultiPage[i].isValid() && m_imgOrigMultiPage[i].getPageCount() > 1) { - FIBITMAP *bitmap = m_imgOrigMultiPage[i].lockPage(m_currentPage[i]); - if (bitmap) - { - m_imgOrig[i] = FreeImage_Clone(bitmap); - m_imgOrig32[i] = m_imgOrig[i]; - FreeImage_UnlockPage(m_imgOrigMultiPage[i], bitmap, false); - } - else - m_imgOrigMultiPage[i].close(); + m_imgOrig[i] = m_imgOrigMultiPage[i].getImage(0); + m_imgOrig32[i] = m_imgOrig[i]; } - if (!m_imgOrigMultiPage[i].isValid()) + else { -#ifdef _WIN32 - if (!m_imgOrig[i].loadU(m_filename[i].c_str())) + m_imgOrigMultiPage[i] = MultiPageImages(); + if (!m_imgOrig[i].load(m_filename[i])) bSucceeded = false; -#else - char filename[260]; - snprintf(filename, sizeof(filename), "%ls", m_filename[i].c_str()); - if (!m_imgOrig[i].load(filename)) - bSucceeded = false; -#endif m_imgOrig32[i] = m_imgOrig[i]; } - m_imgOrig32[i].convertTo32Bits(); } return bSucceeded; @@ -864,8 +679,8 @@ protected: unsigned hmax = 0; for (int i = 0; i < m_nImages; ++i) { - wmax = (std::max)(wmax, m_imgOrig32[i].getWidth()); - hmax = (std::max)(hmax, m_imgOrig32[i].getHeight()); + wmax = (std::max)(wmax, static_cast(m_imgOrig32[i].width())); + hmax = (std::max)(hmax, static_cast(m_imgOrig32[i].height())); } return Size(wmax, hmax); } @@ -894,15 +709,15 @@ protected: { Size size = GetMaxWidthHeight(); for (int i = 0; i < m_nImages; ++i) - m_imgDiff[i].setSize(FIT_BITMAP, size.cx, size.cy, 32); + m_imgDiff[i].setSize(size.cx, size.cy); } void CompareImages2(int pane1, int pane2, DiffBlocks& diff) { - unsigned w1 = m_imgOrig32[pane1].getWidth(); - unsigned h1 = m_imgOrig32[pane1].getHeight(); - unsigned w2 = m_imgOrig32[pane2].getWidth(); - unsigned h2 = m_imgOrig32[pane2].getHeight(); + unsigned w1 = m_imgOrig32[pane1].width(); + unsigned h1 = m_imgOrig32[pane1].height(); + unsigned w2 = m_imgOrig32[pane2].width(); + unsigned h2 = m_imgOrig32[pane2].height(); const unsigned wmax = (std::max)(w1, w2); const unsigned hmax = (std::max)(h1, h2); @@ -920,8 +735,8 @@ protected: } else { - const BYTE *scanline1 = m_imgOrig32[pane1].getScanLine(h1 - y - 1); - const BYTE *scanline2 = m_imgOrig32[pane2].getScanLine(h2 - y - 1); + const unsigned char *scanline1 = m_imgOrig32[pane1].scanLine(y); + const unsigned char *scanline2 = m_imgOrig32[pane2].scanLine(y); if (w1 == w2 && m_colorDistanceThreshold == 0.0) { if (memcmp(scanline1, scanline2, w1 * 4) == 0) @@ -1081,8 +896,8 @@ protected: void MarkDiff(int pane, const DiffBlocks& diff) { - const unsigned w = m_imgDiff[pane].getWidth(); - const unsigned h = m_imgDiff[pane].getHeight(); + const unsigned w = m_imgDiff[pane].width(); + const unsigned h = m_imgDiff[pane].height(); for (unsigned by = 0; by < diff.height(); ++by) { @@ -1095,19 +910,19 @@ protected: (pane == 2 && m_diffInfos[diffIndex - 1].op != DiffInfo::OP_1STONLY) )) { - RGBQUAD color = (diffIndex - 1 == m_currentDiffIndex) ? m_selDiffColor : m_diffColor; + Image::Color color = (diffIndex - 1 == m_currentDiffIndex) ? m_selDiffColor : m_diffColor; unsigned bsy = (h - by * m_diffBlockSize < m_diffBlockSize) ? (h - by * m_diffBlockSize) : m_diffBlockSize; for (unsigned i = 0; i < bsy; ++i) { unsigned y = by * m_diffBlockSize + i; - BYTE *scanline = m_imgDiff[pane].getScanLine(h - y - 1); + unsigned char *scanline = m_imgDiff[pane].scanLine(y); unsigned bsx = (w - bx * m_diffBlockSize < m_diffBlockSize) ? (w - bx * m_diffBlockSize) : m_diffBlockSize; for (unsigned j = 0; j < bsx; ++j) { unsigned x = bx * m_diffBlockSize + j; - scanline[x * 4 + 0] = static_cast(scanline[x * 4 + 0] * (1 - m_diffColorAlpha) + color.rgbBlue * m_diffColorAlpha); - scanline[x * 4 + 1] = static_cast(scanline[x * 4 + 1] * (1 - m_diffColorAlpha) + color.rgbGreen * m_diffColorAlpha); - scanline[x * 4 + 2] = static_cast(scanline[x * 4 + 2] * (1 - m_diffColorAlpha) + color.rgbRed * m_diffColorAlpha); + scanline[x * 4 + 0] = static_cast(scanline[x * 4 + 0] * (1 - m_diffColorAlpha) + Image::valueB(color) * m_diffColorAlpha); + scanline[x * 4 + 1] = static_cast(scanline[x * 4 + 1] * (1 - m_diffColorAlpha) + Image::valueG(color) * m_diffColorAlpha); + scanline[x * 4 + 2] = static_cast(scanline[x * 4 + 2] * (1 - m_diffColorAlpha) + Image::valueR(color) * m_diffColorAlpha); } } } @@ -1117,12 +932,12 @@ protected: void CopyOriginalImageToDiffImage(int dst) { - unsigned w = m_imgOrig32[dst].getWidth(); - unsigned h = m_imgOrig32[dst].getHeight(); + unsigned w = m_imgOrig32[dst].width(); + unsigned h = m_imgOrig32[dst].height(); for (unsigned y = 0; y < h; ++y) { - const BYTE *scanline_src = m_imgOrig32[dst].getScanLine(h - y - 1); - BYTE *scanline_dst = m_imgDiff[dst].getScanLine(m_imgDiff[dst].getHeight() - y - 1); + const unsigned char *scanline_src = m_imgOrig32[dst].scanLine(y); + unsigned char *scanline_dst = m_imgDiff[dst].scanLine(y); for (unsigned x = 0; x < w; ++x) { scanline_dst[x * 4 + 0] = scanline_src[x * 4 + 0]; @@ -1135,12 +950,12 @@ protected: void XorImages2(int src, int dst) { - unsigned w = m_imgOrig32[src].getWidth(); - unsigned h = m_imgOrig32[src].getHeight(); + unsigned w = m_imgOrig32[src].width(); + unsigned h = m_imgOrig32[src].height(); for (unsigned y = 0; y < h; ++y) { - const BYTE *scanline_src = m_imgOrig32[src].getScanLine(h - y - 1); - BYTE *scanline_dst = m_imgDiff[dst].getScanLine(m_imgDiff[dst].getHeight() - y - 1); + const unsigned char *scanline_src = m_imgOrig32[src].scanLine(y); + unsigned char *scanline_dst = m_imgDiff[dst].scanLine(y); for (unsigned x = 0; x < w; ++x) { scanline_dst[x * 4 + 0] ^= scanline_src[x * 4 + 0]; @@ -1152,36 +967,36 @@ protected: void AlphaBlendImages2(int src, int dst) { - unsigned w = m_imgOrig32[src].getWidth(); - unsigned h = m_imgOrig32[src].getHeight(); + unsigned w = m_imgOrig32[src].width(); + unsigned h = m_imgOrig32[src].height(); for (unsigned y = 0; y < h; ++y) { - const BYTE *scanline_src = m_imgOrig32[src].getScanLine(h - y - 1); - BYTE *scanline_dst = m_imgDiff[dst].getScanLine(m_imgDiff[dst].getHeight() - y - 1); + const unsigned char *scanline_src = m_imgOrig32[src].scanLine(y); + unsigned char *scanline_dst = m_imgDiff[dst].scanLine(y); for (unsigned x = 0; x < w; ++x) { - scanline_dst[x * 4 + 0] = static_cast(scanline_dst[x * 4 + 0] * (1 - m_overlayAlpha) + scanline_src[x * 4 + 0] * m_overlayAlpha); - scanline_dst[x * 4 + 1] = static_cast(scanline_dst[x * 4 + 1] * (1 - m_overlayAlpha) + scanline_src[x * 4 + 1] * m_overlayAlpha); - scanline_dst[x * 4 + 2] = static_cast(scanline_dst[x * 4 + 2] * (1 - m_overlayAlpha) + scanline_src[x * 4 + 2] * m_overlayAlpha); - scanline_dst[x * 4 + 3] = static_cast(scanline_dst[x * 4 + 3] * (1 - m_overlayAlpha) + scanline_src[x * 4 + 3] * m_overlayAlpha); + scanline_dst[x * 4 + 0] = static_cast(scanline_dst[x * 4 + 0] * (1 - m_overlayAlpha) + scanline_src[x * 4 + 0] * m_overlayAlpha); + scanline_dst[x * 4 + 1] = static_cast(scanline_dst[x * 4 + 1] * (1 - m_overlayAlpha) + scanline_src[x * 4 + 1] * m_overlayAlpha); + scanline_dst[x * 4 + 2] = static_cast(scanline_dst[x * 4 + 2] * (1 - m_overlayAlpha) + scanline_src[x * 4 + 2] * m_overlayAlpha); + scanline_dst[x * 4 + 3] = static_cast(scanline_dst[x * 4 + 3] * (1 - m_overlayAlpha) + scanline_src[x * 4 + 3] * m_overlayAlpha); } } } int m_nImages; - fipMultiPageEx m_imgOrigMultiPage[3]; - fipImageEx m_imgOrig[3]; - fipImageEx m_imgOrig32[3]; - fipWinImage m_imgDiff[3]; + MultiPageImages m_imgOrigMultiPage[3]; + Image m_imgOrig[3]; + Image m_imgOrig32[3]; + Image m_imgDiff[3]; std::wstring m_filename[3]; bool m_showDifferences; OVERLAY_MODE m_overlayMode; double m_overlayAlpha; unsigned m_diffBlockSize; - RGBQUAD m_selDiffColor; - RGBQUAD m_diffColor; - double m_diffColorAlpha; - double m_colorDistanceThreshold; + Image::Color m_selDiffColor; + Image::Color m_diffColor; + double m_diffColorAlpha; + double m_colorDistanceThreshold; int m_currentPage[3]; int m_currentDiffIndex; int m_diffCount; diff --git a/src/CImgMergeBuffer.hpp b/src/CImgMergeBuffer.hpp index d72f7e531..267872fe2 100644 --- a/src/CImgMergeBuffer.hpp +++ b/src/CImgMergeBuffer.hpp @@ -21,7 +21,7 @@ struct UndoRecord { - UndoRecord(int pane, FIBITMAP *oldbitmap, FIBITMAP *newbitmap, const int modcountnew[3]) : + UndoRecord(int pane, Image *oldbitmap, Image *newbitmap, const int modcountnew[3]) : pane(pane), oldbitmap(oldbitmap), newbitmap(newbitmap) { for (int i = 0; i < 3; ++i) @@ -29,7 +29,7 @@ struct UndoRecord } int pane; int modcount[3]; - FIBITMAP *oldbitmap, *newbitmap; + Image *oldbitmap, *newbitmap; }; struct UndoRecords @@ -44,14 +44,14 @@ struct UndoRecords clear(); } - void push_back(int pane, FIBITMAP *oldbitmap, FIBITMAP *newbitmap) + void push_back(int pane, Image *oldbitmap, Image *newbitmap) { ++m_currentUndoBufIndex; while (m_currentUndoBufIndex < static_cast(m_undoBuf.size())) { --m_modcount[m_undoBuf.back().pane]; - FreeImage_Unload(m_undoBuf.back().newbitmap); - FreeImage_Unload(m_undoBuf.back().oldbitmap); + delete m_undoBuf.back().newbitmap; + delete m_undoBuf.back().oldbitmap; m_undoBuf.pop_back(); } ++m_modcount[pane]; @@ -112,8 +112,8 @@ struct UndoRecords } while (!m_undoBuf.empty()) { - FreeImage_Unload(m_undoBuf.back().newbitmap); - FreeImage_Unload(m_undoBuf.back().oldbitmap); + delete m_undoBuf.back().newbitmap; + delete m_undoBuf.back().oldbitmap; m_undoBuf.pop_back(); } } @@ -163,10 +163,10 @@ public: return; const Rect& rc = m_diffInfos[diffIndex].rc; - unsigned wsrc = m_imgOrig32[srcPane].getWidth(); - unsigned hsrc = m_imgOrig32[srcPane].getHeight(); - unsigned wdst = m_imgOrig32[dstPane].getWidth(); - unsigned hdst = m_imgOrig32[dstPane].getHeight(); + unsigned wsrc = m_imgOrig32[srcPane].width(); + unsigned hsrc = m_imgOrig32[srcPane].height(); + unsigned wdst = m_imgOrig32[dstPane].width(); + unsigned hdst = m_imgOrig32[dstPane].height(); if (rc.right * m_diffBlockSize > wdst) { if ((std::max)(wsrc, wdst) < rc.right * m_diffBlockSize) @@ -181,10 +181,10 @@ public: else hdst = rc.bottom * m_diffBlockSize; } - if (wdst != m_imgOrig32[dstPane].getWidth() || hdst != m_imgOrig32[dstPane].getHeight()) + if (wdst != m_imgOrig32[dstPane].width() || hdst != m_imgOrig32[dstPane].height()) { - fipImage imgTemp = m_imgOrig32[dstPane]; - m_imgOrig32[dstPane].setSize(imgTemp.getImageType(), wdst, hdst, imgTemp.getBitsPerPixel()); + Image imgTemp = m_imgOrig32[dstPane]; + m_imgOrig32[dstPane].setSize(wdst, hdst); m_imgOrig32[dstPane].pasteSubImage(imgTemp, 0, 0); } @@ -200,8 +200,8 @@ public: { for (int i = 0; i < sizey; ++i) { - const BYTE *scanline_src = m_imgOrig32[srcPane].getScanLine(hsrc - (y + i) - 1); - BYTE *scanline_dst = m_imgOrig32[dstPane].getScanLine(hdst - (y + i) - 1); + const unsigned char *scanline_src = m_imgOrig32[srcPane].scanLine(y + i); + unsigned char *scanline_dst = m_imgOrig32[dstPane].scanLine(y + i); memcpy(&scanline_dst[x * 4], &scanline_src[x * 4], sizex * 4); } } @@ -223,11 +223,11 @@ public: if (srcPane == dstPane) return; - FIBITMAP *oldbitmap = FreeImage_Clone(m_imgOrig32[dstPane]); + Image *oldbitmap = new Image(m_imgOrig32[dstPane]); CopyDiffInternal(diffIndex, srcPane, dstPane); - FIBITMAP *newbitmap = FreeImage_Clone(m_imgOrig32[dstPane]); + Image *newbitmap = new Image(m_imgOrig32[dstPane]); m_undoRecords.push_back(dstPane, oldbitmap, newbitmap); CompareImages(); } @@ -243,12 +243,12 @@ public: if (srcPane == dstPane) return; - FIBITMAP *oldbitmap = FreeImage_Clone(m_imgOrig32[dstPane]); + Image *oldbitmap = new Image(m_imgOrig32[dstPane]); for (int diffIndex = 0; diffIndex < m_diffCount; ++diffIndex) CopyDiffInternal(diffIndex, srcPane, dstPane); - FIBITMAP *newbitmap = FreeImage_Clone(m_imgOrig32[dstPane]); + Image *newbitmap = new Image(m_imgOrig32[dstPane]); m_undoRecords.push_back(dstPane, oldbitmap, newbitmap); CompareImages(); } @@ -260,7 +260,7 @@ public: if (m_bRO[dstPane]) return 0; - FIBITMAP *oldbitmap = FreeImage_Clone(m_imgOrig32[dstPane]); + Image *oldbitmap = new Image(m_imgOrig32[dstPane]); int nMerged = 0; for (int diffIndex = 0; diffIndex < m_diffCount; ++diffIndex) @@ -298,7 +298,7 @@ public: } } - FIBITMAP *newbitmap = FreeImage_Clone(m_imgOrig32[dstPane]); + Image *newbitmap = new Image(m_imgOrig32[dstPane]); m_undoRecords.push_back(dstPane, oldbitmap, newbitmap); CompareImages(); @@ -325,7 +325,7 @@ public: if (!m_undoRecords.undoable()) return false; const UndoRecord& rec = m_undoRecords.undo(); - m_imgOrig32[rec.pane] = FreeImage_Clone(rec.oldbitmap); + m_imgOrig32[rec.pane] = *rec.oldbitmap; CompareImages(); return true; } @@ -335,7 +335,7 @@ public: if (!m_undoRecords.redoable()) return false; const UndoRecord& rec = m_undoRecords.redo(); - m_imgOrig32[rec.pane] = FreeImage_Clone(rec.newbitmap); + m_imgOrig32[rec.pane] = *rec.newbitmap; CompareImages(); return true; } @@ -366,33 +366,15 @@ public: { if (pane < 0 || pane >= m_nImages) return false; - unsigned bpp = m_imgOrig[pane].getBitsPerPixel(); - RGBQUAD palette[256]; - if (m_imgOrig[pane].getPaletteSize() > 0) - memcpy(palette, m_imgOrig[pane].getPalette(), m_imgOrig[pane].getPaletteSize()); - m_imgOrig[pane] = m_imgOrig32[pane]; - m_imgOrig[pane].convertColorDepth(bpp, palette); + m_imgOrig[pane].pullImageKeepingBPP(m_imgOrig32[pane]); if (m_imgOrigMultiPage[pane].isValid()) { - fipImageEx imgOrg, imgAdd; - imgAdd = m_imgOrig[pane]; - imgOrg = m_imgOrigMultiPage[pane].lockPage(m_currentPage[pane]); - imgAdd.copyAnimationMetadata(imgOrg); - m_imgOrigMultiPage[pane].unlockPage(imgOrg, false); - m_imgOrigMultiPage[pane].insertPage(m_currentPage[pane], imgAdd); - imgAdd.detach(); - m_imgOrigMultiPage[pane].deletePage(m_currentPage[pane] + 1); - return !!m_imgOrigMultiPage[pane].saveU(filename); + m_imgOrigMultiPage[pane].replacePage(m_currentPage[pane], m_imgOrig[pane]); + return m_imgOrigMultiPage[pane].save(filename); } else { -#ifdef _WIN32 - return !!m_imgOrig[pane].saveU(filename); -#else - char filenameA[260]; - snprintf(filenameA, sizeof(filenameA), "%ls", filename); - return !!m_imgOrig[pane].save(filenameA); -#endif + return m_imgOrig[pane].save(filename); } } diff --git a/src/CImgMergeWindow.hpp b/src/CImgMergeWindow.hpp index 115203c49..e5c882332 100644 --- a/src/CImgMergeWindow.hpp +++ b/src/CImgMergeWindow.hpp @@ -540,7 +540,7 @@ public: for (int i = 0; i < nImages; ++i) { m_imgWindow[i].SetWindowRect(rects[i]); - m_imgWindow[i].SetImage(m_buffer.GetImage(i)); + m_imgWindow[i].SetImage(m_buffer.GetImage(i)->getFipImage()); } return bSucceeded; } @@ -946,8 +946,8 @@ private: { POINT pt = pImgWnd->GetCursorPos(i); int diffIndex = pImgWnd->GetDiffIndexFromPoint(pt.x, pt.y); - if (diffIndex != 0) - pImgWnd->SelectDiff(diffIndex - 1); + if (diffIndex >= 0) + pImgWnd->SelectDiff(diffIndex); else pImgWnd->SelectDiff(-1); break; diff --git a/src/Makefile b/src/Makefile index 8b79325c0..2396bc899 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2,7 +2,7 @@ TARGETS=cidiff CXXFLAGS+=-Wall -Wextra -I../../freeimage/Source -I../../freeimage/Wrapper/FreeImagePlus SRCS=cidiff.cpp OBJS=$(SRCS:.cpp=*.o) -HEADERS=CImgDiffBuffer.hpp CImgMergeBuffer.hpp +HEADERS=CImgDiffBuffer.hpp CImgMergeBuffer.hpp image.hpp LIBS=-L../../freeimage/ -lfreeimage -L../../freeimage/ -lfreeimageplus all: $(TARGETS) diff --git a/src/WinIMergeLib.vcxproj b/src/WinIMergeLib.vcxproj index 979bc5974..70f3a3a2f 100644 --- a/src/WinIMergeLib.vcxproj +++ b/src/WinIMergeLib.vcxproj @@ -173,6 +173,7 @@ + diff --git a/src/WinIMergeLib.vcxproj.filters b/src/WinIMergeLib.vcxproj.filters index eab8e5d4e..63093c94e 100644 --- a/src/WinIMergeLib.vcxproj.filters +++ b/src/WinIMergeLib.vcxproj.filters @@ -33,6 +33,9 @@ Header Files + + Header Files + diff --git a/src/image.hpp b/src/image.hpp new file mode 100644 index 000000000..e43b15b5f --- /dev/null +++ b/src/image.hpp @@ -0,0 +1,296 @@ +///////////////////////////////////////////////////////////////////////////// +// License (GPLv2+): +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +///////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "FreeImagePlus.h" +#include +#include + +#ifndef _WIN32 +typedef fipImage fipWinImage; +#endif + +class fipImageEx : public fipWinImage +{ +public: + fipImageEx(FREE_IMAGE_TYPE image_type = FIT_BITMAP, unsigned width = 0, unsigned height = 0, unsigned bpp = 0) : + fipWinImage(image_type, width, height, bpp) {} + fipImageEx(const fipImageEx& Image) : fipWinImage(Image) {} + fipImageEx(FIBITMAP *bitmap) { *this = bitmap; } + virtual ~fipImageEx() {} + + fipImageEx& operator=(const fipImageEx& Image) + { + if (this != &Image) + { + FIBITMAP *clone = FreeImage_Clone((FIBITMAP*)Image._dib); + replace(clone); + } + return *this; + } + + fipImageEx& operator=(FIBITMAP *dib) + { + if (_dib != dib) + replace(dib); + return *this; + } + + void swap(fipImageEx& other) + { + std::swap(_dib, other._dib); + std::swap(this->_fif, other._fif); + std::swap(this->_bHasChanged, other._bHasChanged); + } + + FIBITMAP *detach() + { + FIBITMAP *dib = _dib; + _dib = NULL; + clear(); + return dib; + } + + BOOL colorQuantizeEx(FREE_IMAGE_QUANTIZE quantize = FIQ_WUQUANT, int PaletteSize = 256, int ReserveSize = 0, RGBQUAD *ReservePalette = NULL) + { + if(_dib) { + FIBITMAP *dib8 = FreeImage_ColorQuantizeEx(_dib, quantize, PaletteSize, ReserveSize, ReservePalette); + return !!replace(dib8); + } + return false; + } + + bool convertColorDepth(unsigned bpp, RGBQUAD *pPalette = NULL) + { + switch (bpp) + { + case 1: + return !!threshold(128); + case 4: + { + fipImageEx tmp = *this; + tmp.convertTo24Bits(); + if (pPalette) + tmp.colorQuantizeEx(FIQ_NNQUANT, 16, 16, pPalette); + else + tmp.colorQuantizeEx(FIQ_WUQUANT, 16); + setSize(tmp.getImageType(), tmp.getWidth(), tmp.getHeight(), 4); + for (unsigned y = 0; y < tmp.getHeight(); ++y) + { + const BYTE *line_src = tmp.getScanLine(y); + BYTE *line_dst = getScanLine(y); + for (unsigned x = 0; x < tmp.getWidth(); ++x) + line_dst[x / 2] |= ((x % 2) == 0) ? (line_src[x] << 4) : line_src[x]; + } + + RGBQUAD *rgbq_dst = getPalette(); + RGBQUAD *rgbq_src = pPalette ? pPalette : tmp.getPalette(); + memcpy(rgbq_dst, rgbq_src, sizeof(RGBQUAD) * 16); + return true; + } + case 8: + convertTo24Bits(); + if (pPalette) + return !!colorQuantizeEx(FIQ_NNQUANT, 256, 256, pPalette); + else + return !!colorQuantizeEx(FIQ_WUQUANT, 256); + case 15: + return !!convertTo16Bits555(); + case 16: + return !!convertTo16Bits565(); + case 24: + return !!convertTo24Bits(); + default: + case 32: + return !!convertTo32Bits(); + } + } + + void copyAnimationMetadata(fipImage& src) + { + fipTag tag; + fipMetadataFind finder; + if (finder.findFirstMetadata(FIMD_ANIMATION, src, tag)) + { + do + { + setMetadata(FIMD_ANIMATION, tag.getKey(), tag); + } while (finder.findNextMetadata(tag)); + } + } +}; + +class fipMultiPageEx : public fipMultiPage +{ +public: + fipMultiPageEx(BOOL keep_cache_in_memory = FALSE) : fipMultiPage(keep_cache_in_memory) {} + + BOOL openU(const wchar_t* lpszPathName, BOOL create_new, BOOL read_only, int flags = 0) + { + char filename[260]; +#ifdef _WIN32 + wchar_t shortname[260] = {0}; + GetShortPathNameW(lpszPathName, shortname, sizeof(shortname)/sizeof(shortname[0])); + wsprintfA(filename, "%S", shortname); +#else + snprintf(filename, sizeof(filename), "%ls", lpszPathName); + +#endif + BOOL result = open(filename, create_new, read_only, flags); + return result; + } + + bool saveU(const wchar_t* lpszPathName, int flag = 0) const + { + FILE *fp = NULL; +#ifdef _WIN32 + _wfopen_s(&fp, lpszPathName, L"r+b"); +#else + char filename[260]; + snprintf(filename, sizeof(filename), "%ls", lpszPathName); + fp = fopen(filename, "r+b"); +#endif + if (!fp) + return false; + FreeImageIO io; + io.read_proc = myReadProc; + io.write_proc = myWriteProc; + io.seek_proc = mySeekProc; + io.tell_proc = myTellProc; + FREE_IMAGE_FORMAT fif = fipImage::identifyFIFU(lpszPathName); + bool result = !!saveToHandle(fif, &io, (fi_handle)fp, flag); + fclose(fp); + return result; + } + +private: + static unsigned DLL_CALLCONV myReadProc(void *buffer, unsigned size, unsigned count, fi_handle handle) { + return (unsigned)fread(buffer, size, count, (FILE *)handle); + } + + static unsigned DLL_CALLCONV myWriteProc(void *buffer, unsigned size, unsigned count, fi_handle handle) { + return (unsigned)fwrite(buffer, size, count, (FILE *)handle); + } + + static int DLL_CALLCONV mySeekProc(fi_handle handle, long offset, int origin) { + return fseek((FILE *)handle, offset, origin); + } + + static long DLL_CALLCONV myTellProc(fi_handle handle) { + return ftell((FILE *)handle); + } +}; + +class MultiPageImages; + +class Image +{ + friend MultiPageImages; +public: + typedef RGBQUAD Color; + Image() {} + Image(int w, int h) : image_(FIT_BITMAP, w, h, 32) {} + Image(const Image& other) { image_ = other.image_; } + Image(FIBITMAP *bitmap) : image_(bitmap) {} + BYTE *scanLine(int y) { return image_.getScanLine(image_.getHeight() - y - 1); } + const BYTE *scanLine(int y) const { return image_.getScanLine(image_.getHeight() - y - 1); } + bool convertTo32Bits() { return !!image_.convertTo32Bits(); } + bool load(const std::wstring& filename) { return !!image_.loadU(filename.c_str()); } + bool save(const std::wstring& filename) + { +#ifdef _WIN32 + return !!image_.saveU(filename.c_str()); +#else + char filenameA[260]; + snprintf(filenameA, sizeof(filenameA), "%ls", filename); + return !!image_.save(filenameA); +#endif + } + int depth() const { return image_.getBitsPerPixel(); } + unsigned width() const { return image_.getWidth(); } + unsigned height() const { return image_.getHeight(); } + void clear() { image_.clear(); } + void setSize(int w, int h) { image_.setSize(FIT_BITMAP, w, h, 32); } + const fipImageEx *getImage() const { return &image_; } + fipImageEx *getFipImage() { return &image_; } + Color pixel(int x, int y) const + { + RGBQUAD color = {0}; + color.rgbReserved = 0xFF; + image_.getPixelColor(x, y, &color); + return color; + } + bool pasteSubImage(Image& image, int x, int y) + { + return !!image_.pasteSubImage(image.image_, x, y); + } + bool pullImageKeepingBPP(const Image& other) + { + unsigned bpp = image_.getBitsPerPixel(); + RGBQUAD palette[256]; + if (image_.getPaletteSize() > 0) + memcpy(palette, image_.getPalette(), image_.getPaletteSize()); + image_ = other.image_; + return image_.convertColorDepth(bpp, palette); + } + + static int valueR(Color color) { return color.rgbRed; } + static int valueG(Color color) { return color.rgbGreen; } + static int valueB(Color color) { return color.rgbBlue; } + static int valueA(Color color) { return color.rgbReserved; } + static Color Rgb(int r, int g, int b) + { + Color color; + color.rgbRed = r; + color.rgbGreen = g; + color.rgbBlue = b; + return color; + } +private: + fipImageEx image_; +}; + +class MultiPageImages +{ +public: + bool isValid() const { return !!multi_.isValid(); } + int getPageCount() const { return multi_.getPageCount(); } + bool load(const std::wstring& filename) { return !!multi_.openU(filename.c_str(), FALSE, FALSE); } + bool save(const std::wstring& filename) { return !!multi_.saveU(filename.c_str()); } + Image getImage(int page) + { + FIBITMAP *bitmaptmp, *bitmap; + bitmaptmp = FreeImage_LockPage(multi_, page); + bitmap = FreeImage_Clone(bitmaptmp); + FreeImage_UnlockPage(multi_, bitmap, false); + return Image(bitmap); + } + void replacePage(int page, const Image& image) + { + fipImageEx imgOrg, imgAdd; + imgAdd = image.image_; + imgOrg = multi_.lockPage(page); + imgAdd.copyAnimationMetadata(imgOrg); + multi_.unlockPage(imgOrg, false); + multi_.insertPage(page, imgAdd); + imgAdd.detach(); + multi_.deletePage(page + 1); + } + + fipMultiPageEx multi_; +}; -- 2.11.0 From 2e68ef37e44b6f05109e1296ee764dfec0cb3b5f Mon Sep 17 00:00:00 2001 From: sdottaka Date: Sun, 19 Oct 2014 10:53:08 +0900 Subject: [PATCH 12/16] Rename CImg*.hpp to Img*.hpp --- src/{CImgDiffBuffer.hpp => ImgDiffBuffer.hpp} | 0 src/{CImgMergeBuffer.hpp => ImgMergeBuffer.hpp} | 2 +- src/{CImgMergeWindow.hpp => ImgMergeWindow.hpp} | 4 ++-- src/{CImgWindow.hpp => ImgWindow.hpp} | 0 src/Makefile | 2 +- src/WinIMergeLib.cpp | 4 ++-- src/WinIMergeLib.vcxproj | 8 ++++---- src/WinIMergeLib.vcxproj.filters | 10 +++++----- src/cidiff.cpp | 2 +- 9 files changed, 16 insertions(+), 16 deletions(-) rename src/{CImgDiffBuffer.hpp => ImgDiffBuffer.hpp} (100%) rename src/{CImgMergeBuffer.hpp => ImgMergeBuffer.hpp} (95%) rename src/{CImgMergeWindow.hpp => ImgMergeWindow.hpp} (95%) rename src/{CImgWindow.hpp => ImgWindow.hpp} (100%) diff --git a/src/CImgDiffBuffer.hpp b/src/ImgDiffBuffer.hpp similarity index 100% rename from src/CImgDiffBuffer.hpp rename to src/ImgDiffBuffer.hpp diff --git a/src/CImgMergeBuffer.hpp b/src/ImgMergeBuffer.hpp similarity index 95% rename from src/CImgMergeBuffer.hpp rename to src/ImgMergeBuffer.hpp index 267872fe2..1cdf5a979 100644 --- a/src/CImgMergeBuffer.hpp +++ b/src/ImgMergeBuffer.hpp @@ -17,7 +17,7 @@ #pragma once -#include "CImgDiffBuffer.hpp" +#include "ImgDiffBuffer.hpp" struct UndoRecord { diff --git a/src/CImgMergeWindow.hpp b/src/ImgMergeWindow.hpp similarity index 95% rename from src/CImgMergeWindow.hpp rename to src/ImgMergeWindow.hpp index e5c882332..6a88655d3 100644 --- a/src/CImgMergeWindow.hpp +++ b/src/ImgMergeWindow.hpp @@ -19,8 +19,8 @@ #include #include "FreeImagePlus.h" -#include "CImgWindow.hpp" -#include "CImgMergeBuffer.hpp" +#include "ImgWindow.hpp" +#include "ImgMergeBuffer.hpp" #include "WinIMergeLib.h" diff --git a/src/CImgWindow.hpp b/src/ImgWindow.hpp similarity index 100% rename from src/CImgWindow.hpp rename to src/ImgWindow.hpp diff --git a/src/Makefile b/src/Makefile index 2396bc899..c88f24986 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2,7 +2,7 @@ TARGETS=cidiff CXXFLAGS+=-Wall -Wextra -I../../freeimage/Source -I../../freeimage/Wrapper/FreeImagePlus SRCS=cidiff.cpp OBJS=$(SRCS:.cpp=*.o) -HEADERS=CImgDiffBuffer.hpp CImgMergeBuffer.hpp image.hpp +HEADERS=ImgDiffBuffer.hpp ImgMergeBuffer.hpp image.hpp LIBS=-L../../freeimage/ -lfreeimage -L../../freeimage/ -lfreeimageplus all: $(TARGETS) diff --git a/src/WinIMergeLib.cpp b/src/WinIMergeLib.cpp index 1a3eeaea4..85b4e7664 100644 --- a/src/WinIMergeLib.cpp +++ b/src/WinIMergeLib.cpp @@ -16,8 +16,8 @@ ///////////////////////////////////////////////////////////////////////////// #include -#include "CImgWindow.hpp" -#include "CImgMergeWindow.hpp" +#include "ImgWindow.hpp" +#include "ImgMergeWindow.hpp" extern "C" IImgMergeWindow * WinIMerge_CreateWindow(HINSTANCE hInstance, HWND hWndParent) diff --git a/src/WinIMergeLib.vcxproj b/src/WinIMergeLib.vcxproj index 70f3a3a2f..30a6a3394 100644 --- a/src/WinIMergeLib.vcxproj +++ b/src/WinIMergeLib.vcxproj @@ -169,10 +169,10 @@ - - - - + + + + diff --git a/src/WinIMergeLib.vcxproj.filters b/src/WinIMergeLib.vcxproj.filters index 63093c94e..4fb1a09ee 100644 --- a/src/WinIMergeLib.vcxproj.filters +++ b/src/WinIMergeLib.vcxproj.filters @@ -21,19 +21,19 @@ Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files diff --git a/src/cidiff.cpp b/src/cidiff.cpp index ad9020e97..5bb691c18 100644 --- a/src/cidiff.cpp +++ b/src/cidiff.cpp @@ -1,4 +1,4 @@ -#include "CImgDiffBuffer.hpp" +#include "ImgDiffBuffer.hpp" #include #include -- 2.11.0 From 7ca64223551bb7fd80d739bebed3dc166e9cc8c5 Mon Sep 17 00:00:00 2001 From: sdottaka Date: Sun, 19 Oct 2014 11:38:02 +0900 Subject: [PATCH 13/16] Bump revision to 0.9.5.0 --- SetVersion.cmd | 2 +- Version.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/SetVersion.cmd b/SetVersion.cmd index 6c9b52c63..06618f024 100644 --- a/SetVersion.cmd +++ b/SetVersion.cmd @@ -1,4 +1,4 @@ set MAJOR=0 set MINOR=9 -set REVISION=4 +set REVISION=5 set PATCHLEVEL=0 diff --git a/Version.h b/Version.h index ca5d94423..1e9a779d4 100644 --- a/Version.h +++ b/Version.h @@ -1,4 +1,4 @@ -#define FILEVER 0,9,4,0 -#define PRODUCTVER 0,9,4,0 -#define STRFILEVER "0.9.4.0" -#define STRPRODUCTVER "0.9.4.0" +#define FILEVER 0,9,5,0 +#define PRODUCTVER 0,9,5,0 +#define STRFILEVER "0.9.5.0" +#define STRPRODUCTVER "0.9.5.0" -- 2.11.0 From d3e8740ce09ed0f2045d69feaf51715504cfe37d Mon Sep 17 00:00:00 2001 From: sdottaka Date: Sun, 19 Oct 2014 11:39:09 +0900 Subject: [PATCH 14/16] Added tag 0.9.5.0 for changeset 078109b91064 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index 02dd96eee..5cc83cc23 100644 --- a/.hgtags +++ b/.hgtags @@ -1,3 +1,4 @@ 5710fff75af907f9c292950e3dc9bf3ab4c448ca 0.9.2.0 9e83a2640c31b4fb8321fdcaf03b7ac539c320c8 0.9.3.0 0ef16028a292e33f87cbe75f83a7f799299a1ac9 0.9.4.0 +078109b91064c140265a2e01aaf87a65d20b5253 0.9.5.0 -- 2.11.0 From b8c366288c9aa767adddba2c9e044e6c1b8d49c3 Mon Sep 17 00:00:00 2001 From: sdottaka Date: Tue, 28 Oct 2014 21:31:39 +0900 Subject: [PATCH 15/16] *.ficache file was not deleted --- src/ImgDiffBuffer.hpp | 2 +- src/image.hpp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ImgDiffBuffer.hpp b/src/ImgDiffBuffer.hpp index bfe8c36ff..dad137268 100644 --- a/src/ImgDiffBuffer.hpp +++ b/src/ImgDiffBuffer.hpp @@ -663,7 +663,7 @@ protected: } else { - m_imgOrigMultiPage[i] = MultiPageImages(); + m_imgOrigMultiPage[i].close(); if (!m_imgOrig[i].load(m_filename[i])) bSucceeded = false; m_imgOrig32[i] = m_imgOrig[i]; diff --git a/src/image.hpp b/src/image.hpp index e43b15b5f..2b7279073 100644 --- a/src/image.hpp +++ b/src/image.hpp @@ -268,6 +268,9 @@ private: class MultiPageImages { public: + MultiPageImages() {} + ~MultiPageImages() { multi_.close(); } + bool close() { return !!multi_.close(); } bool isValid() const { return !!multi_.isValid(); } int getPageCount() const { return multi_.getPageCount(); } bool load(const std::wstring& filename) { return !!multi_.openU(filename.c_str(), FALSE, FALSE); } -- 2.11.0 From 11f63b1bbff175ffc60aca359e3b46f2bf20ce44 Mon Sep 17 00:00:00 2001 From: sdottaka Date: Tue, 28 Oct 2014 21:36:45 +0900 Subject: [PATCH 16/16] Bump revision to 0.9.6.0 --- SetVersion.cmd | 2 +- Version.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/SetVersion.cmd b/SetVersion.cmd index 06618f024..e69abac18 100644 --- a/SetVersion.cmd +++ b/SetVersion.cmd @@ -1,4 +1,4 @@ set MAJOR=0 set MINOR=9 -set REVISION=5 +set REVISION=6 set PATCHLEVEL=0 diff --git a/Version.h b/Version.h index 1e9a779d4..d7e061f1a 100644 --- a/Version.h +++ b/Version.h @@ -1,4 +1,4 @@ -#define FILEVER 0,9,5,0 -#define PRODUCTVER 0,9,5,0 -#define STRFILEVER "0.9.5.0" -#define STRPRODUCTVER "0.9.5.0" +#define FILEVER 0,9,6,0 +#define PRODUCTVER 0,9,6,0 +#define STRFILEVER "0.9.6.0" +#define STRPRODUCTVER "0.9.6.0" -- 2.11.0