diff --git a/YACReader/magnifying_glass.cpp b/YACReader/magnifying_glass.cpp index 3c166ff3..5bc1f78d 100644 --- a/YACReader/magnifying_glass.cpp +++ b/YACReader/magnifying_glass.cpp @@ -1,10 +1,5 @@ #include "magnifying_glass.h" #include "viewer.h" -#include "configuration.h" - -#include "theme_manager.h" - -#include MagnifyingGlass::MagnifyingGlass(int w, int h, float zoomLevel, QWidget *parent) : QLabel(parent), zoomLevel(zoomLevel) @@ -34,101 +29,9 @@ void MagnifyingGlass::mouseMoveEvent(QMouseEvent *event) void MagnifyingGlass::updateImage(int x, int y) { - // image section augmented - int zoomWidth = static_cast(width() * zoomLevel); - int zoomHeight = static_cast(height() * zoomLevel); - auto *const p = qobject_cast(parentWidget()); - int currentPos = p->verticalScrollBar()->sliderPosition(); - const QPixmap image = p->pixmap(); - int iWidth = image.width(); - int iHeight = image.height(); - float wFactor = static_cast(iWidth) / p->widget()->width(); - float hFactor = static_cast(iHeight) / p->widget()->height(); - zoomWidth *= wFactor; - zoomHeight *= hFactor; - if (p->verticalScrollBar()->minimum() == p->verticalScrollBar()->maximum()) { - int xp = static_cast(((x - p->widget()->pos().x()) * wFactor) - zoomWidth / 2); - int yp = static_cast((y - p->widget()->pos().y() + currentPos) * hFactor - zoomHeight / 2); - int xOffset = 0; - int yOffset = 0; - int zw = zoomWidth; - int zh = zoomHeight; - // int wOffset,hOffset=0; - bool outImage = false; - if (xp < 0) { - xOffset = -xp; - xp = 0; - zw = zw - xOffset; - outImage = true; - } - if (yp < 0) { - yOffset = -yp; - yp = 0; - zh = zh - yOffset; - outImage = true; - } - - if (xp + zoomWidth >= image.width()) { - zw -= xp + zw - image.width(); - outImage = true; - } - if (yp + zoomHeight >= image.height()) { - zh -= yp + zh - image.height(); - outImage = true; - } - if (outImage) { - QImage img(zoomWidth, zoomHeight, QImage::Format_RGB32); - img.setDevicePixelRatio(devicePixelRatioF()); - img.fill(Configuration::getConfiguration().getBackgroundColor(ThemeManager::instance().getCurrentTheme().viewer.defaultBackgroundColor)); - if (zw > 0 && zh > 0) { - QPainter painter(&img); - painter.drawPixmap(xOffset, yOffset, image.copy(xp, yp, zw, zh)); - } - setPixmap(QPixmap().fromImage(img)); - } else - setPixmap(image.copy(xp, yp, zoomWidth, zoomHeight)); - } else { - int xp = static_cast(((x - p->widget()->pos().x()) * wFactor) - zoomWidth / 2); - int yp = static_cast((y + currentPos) * hFactor - zoomHeight / 2); - int xOffset = 0; - int yOffset = 0; - int zw = zoomWidth; - int zh = zoomHeight; - // int wOffset,hOffset=0; - bool outImage = false; - if (xp < 0) { - xOffset = -xp; - xp = 0; - zw = zw - xOffset; - outImage = true; - } - if (yp < 0) { - yOffset = -yp; - yp = 0; - zh = zh - yOffset; - outImage = true; - } - - if (xp + zoomWidth >= image.width()) { - zw -= xp + zw - image.width(); - outImage = true; - } - if (yp + zoomHeight >= image.height()) { - zh -= yp + zh - image.height(); - outImage = true; - } - if (outImage) { - QImage img(zoomWidth, zoomHeight, QImage::Format_RGB32); - img.setDevicePixelRatio(devicePixelRatioF()); - img.fill(Configuration::getConfiguration().getBackgroundColor(ThemeManager::instance().getCurrentTheme().viewer.defaultBackgroundColor)); - if (zw > 0 && zh > 0) { - QPainter painter(&img); - painter.drawPixmap(xOffset, yOffset, image.copy(xp, yp, zw, zh)); - } - setPixmap(QPixmap().fromImage(img)); - } else - setPixmap(image.copy(xp, yp, zoomWidth, zoomHeight)); - } + auto *const viewer = qobject_cast(parentWidget()); + QImage img = viewer->grabMagnifiedRegion(QPoint(x, y), size(), zoomLevel); + setPixmap(QPixmap::fromImage(img)); move(static_cast(x - float(width()) / 2), static_cast(y - float(height()) / 2)); } diff --git a/YACReader/viewer.cpp b/YACReader/viewer.cpp index 6eae05ad..4533bbbe 100644 --- a/YACReader/viewer.cpp +++ b/YACReader/viewer.cpp @@ -16,6 +16,7 @@ #include #include +#include #include @@ -850,6 +851,168 @@ QPixmap Viewer::pixmap() const return content->pixmap(); } +QImage Viewer::grabMagnifiedRegion(const QPoint &viewerPos, const QSize &glassSize, float zoomLevel) const +{ + const int glassW = glassSize.width(); + const int glassH = glassSize.height(); + const int zoomW = static_cast(glassW * zoomLevel); + const int zoomH = static_cast(glassH * zoomLevel); + const QColor bgColor = Configuration::getConfiguration().getBackgroundColor(theme.viewer.defaultBackgroundColor); + + if (continuousScroll) { + // --- continuous mode --- + // map viewer coords to continuousWidget coords + const int scrollPos = verticalScrollBar()->sliderPosition(); + const int cwX = viewerPos.x(); + const int cwY = viewerPos.y() + scrollPos; + const int widgetW = continuousWidget->width(); + + // use the page under the cursor to derive source-to-widget scale factors, + // so the result image is sized at source resolution (like single-page mode) + int centerPageIdx = continuousViewModel->pageAtY(cwY); + centerPageIdx = qBound(0, centerPageIdx, continuousViewModel->numPages() - 1); + const QImage *centerImg = render->bufferedImage(centerPageIdx); + const QSize centerScaledSize = continuousViewModel->scaledPageSize(centerPageIdx); + + float wFactor = 1.0f, hFactor = 1.0f; + if (centerImg && !centerImg->isNull() && !centerScaledSize.isEmpty()) { + wFactor = static_cast(centerImg->width()) / centerScaledSize.width(); + hFactor = static_cast(centerImg->height()) / centerScaledSize.height(); + } + + // result image sized in source-resolution pixels (full quality) + const int resultW = static_cast(zoomW * wFactor); + const int resultH = static_cast(zoomH * hFactor); + + QImage result(resultW, resultH, QImage::Format_RGB32); + result.setDevicePixelRatio(devicePixelRatioF()); + result.fill(bgColor); + + // zoom region in widget coordinates (centered on cursor) + const int regionLeft = cwX - zoomW / 2; + const int regionTop = cwY - zoomH / 2; + const int regionRight = regionLeft + zoomW; + const int regionBottom = regionTop + zoomH; + + // find which pages overlap the zoom region + int firstPage = continuousViewModel->pageAtY(regionTop); + int lastPage = continuousViewModel->pageAtY(regionBottom); + firstPage = qBound(0, firstPage, continuousViewModel->numPages() - 1); + lastPage = qBound(0, lastPage, continuousViewModel->numPages() - 1); + + QPainter painter(&result); + for (int i = firstPage; i <= lastPage; ++i) { + const QImage *srcImg = render->bufferedImage(i); + if (!srcImg || srcImg->isNull()) { + continue; + } + + const QSize scaledSize = continuousViewModel->scaledPageSize(i); + const int pageY = continuousViewModel->yPositionForPage(i); + int pageX = (widgetW - scaledSize.width()) / 2; + if (pageX < 0) { + pageX = 0; + } + + // intersection of zoom region and page rect (widget coords) + const int isectLeft = qMax(regionLeft, pageX); + const int isectTop = qMax(regionTop, pageY); + const int isectRight = qMin(regionRight, pageX + scaledSize.width()); + const int isectBottom = qMin(regionBottom, pageY + scaledSize.height()); + + if (isectLeft >= isectRight || isectTop >= isectBottom) { + continue; + } + + // map intersection to source image coordinates (full resolution crop) + const float pageScaleX = static_cast(srcImg->width()) / scaledSize.width(); + const float pageScaleY = static_cast(srcImg->height()) / scaledSize.height(); + + const int srcX = static_cast((isectLeft - pageX) * pageScaleX); + const int srcY = static_cast((isectTop - pageY) * pageScaleY); + const int srcW = static_cast((isectRight - isectLeft) * pageScaleX); + const int srcH = static_cast((isectBottom - isectTop) * pageScaleY); + + // destination in result image (source-resolution coordinates) + const int dstX = static_cast((isectLeft - regionLeft) * wFactor); + const int dstY = static_cast((isectTop - regionTop) * hFactor); + const int dstW = static_cast((isectRight - isectLeft) * wFactor); + const int dstH = static_cast((isectBottom - isectTop) * hFactor); + + QImage cropped = srcImg->copy(srcX, srcY, srcW, srcH); + if (cropped.size() != QSize(dstW, dstH)) { + cropped = cropped.scaled(dstW, dstH, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + } + painter.drawImage(dstX, dstY, cropped); + } + + return result; + } + + // --- single-page mode --- + const QPixmap image = content->pixmap(); + if (image.isNull()) { + QImage result(zoomW, zoomH, QImage::Format_RGB32); + result.setDevicePixelRatio(devicePixelRatioF()); + result.fill(bgColor); + return result; + } + + const int iWidth = image.width(); + const int iHeight = image.height(); + const float wFactor = static_cast(iWidth) / widget()->width(); + const float hFactor = static_cast(iHeight) / widget()->height(); + const int zoomWScaled = static_cast(zoomW * wFactor); + const int zoomHScaled = static_cast(zoomH * hFactor); + + const int scrollPos = verticalScrollBar()->sliderPosition(); + int xp, yp; + if (verticalScrollBar()->minimum() == verticalScrollBar()->maximum()) { + xp = static_cast(((viewerPos.x() - widget()->pos().x()) * wFactor) - zoomWScaled / 2); + yp = static_cast((viewerPos.y() - widget()->pos().y() + scrollPos) * hFactor - zoomHScaled / 2); + } else { + xp = static_cast(((viewerPos.x() - widget()->pos().x()) * wFactor) - zoomWScaled / 2); + yp = static_cast((viewerPos.y() + scrollPos) * hFactor - zoomHScaled / 2); + } + + int xOffset = 0, yOffset = 0; + int zw = zoomWScaled, zh = zoomHScaled; + bool outImage = false; + if (xp < 0) { + xOffset = -xp; + xp = 0; + zw -= xOffset; + outImage = true; + } + if (yp < 0) { + yOffset = -yp; + yp = 0; + zh -= yOffset; + outImage = true; + } + if (xp + zoomWScaled >= iWidth) { + zw -= xp + zw - iWidth; + outImage = true; + } + if (yp + zoomHScaled >= iHeight) { + zh -= yp + zh - iHeight; + outImage = true; + } + + if (outImage) { + QImage img(zoomWScaled, zoomHScaled, QImage::Format_RGB32); + img.setDevicePixelRatio(devicePixelRatioF()); + img.fill(bgColor); + if (zw > 0 && zh > 0) { + QPainter painter(&img); + painter.drawPixmap(xOffset, yOffset, image.copy(xp, yp, zw, zh)); + } + return img; + } + + return image.copy(xp, yp, zoomWScaled, zoomHScaled).toImage(); +} + void Viewer::magnifyingGlassSwitch() { magnifyingGlassShown ? hideMagnifyingGlass() : showMagnifyingGlass(); diff --git a/YACReader/viewer.h b/YACReader/viewer.h index 99e6b60a..b2393a89 100644 --- a/YACReader/viewer.h +++ b/YACReader/viewer.h @@ -214,6 +214,7 @@ public: Viewer(QWidget *parent = nullptr); ~Viewer(); QPixmap pixmap() const; + QImage grabMagnifiedRegion(const QPoint &viewerPos, const QSize &glassSize, float zoomLevel) const; // Comic * getComic(){return comic;} const BookmarksDialog *getBookmarksDialog() { return bd; } // returns the current index starting in 1 [1,nPages]