mirror of
https://github.com/YACReader/yacreader
synced 2025-05-25 18:00:46 -04:00
292 lines
7.7 KiB
C++
292 lines
7.7 KiB
C++
#include "magnifying_glass.h"
|
|
#include "viewer.h"
|
|
#include "configuration.h"
|
|
|
|
#include <QScrollBar>
|
|
|
|
MagnifyingGlass::MagnifyingGlass(int w, int h, float zoomLevel, QWidget *parent)
|
|
: QLabel(parent), zoomLevel(zoomLevel)
|
|
{
|
|
setup(QSize(w, h));
|
|
}
|
|
|
|
MagnifyingGlass::MagnifyingGlass(const QSize &size, float zoomLevel, QWidget *parent)
|
|
: QLabel(parent), zoomLevel(zoomLevel)
|
|
{
|
|
setup(size);
|
|
}
|
|
|
|
void MagnifyingGlass::setup(const QSize &size)
|
|
{
|
|
resize(size);
|
|
setScaledContents(true);
|
|
setMouseTracking(true);
|
|
setCursor(QCursor(QBitmap(1, 1), QBitmap(1, 1)));
|
|
}
|
|
|
|
void MagnifyingGlass::mouseMoveEvent(QMouseEvent *event)
|
|
{
|
|
updateImage();
|
|
event->accept();
|
|
}
|
|
|
|
void MagnifyingGlass::updateImage(int x, int y)
|
|
{
|
|
// image section augmented
|
|
int zoomWidth = static_cast<int>(width() * zoomLevel);
|
|
int zoomHeight = static_cast<int>(height() * zoomLevel);
|
|
auto *const p = qobject_cast<const Viewer *>(parentWidget());
|
|
int currentPos = p->verticalScrollBar()->sliderPosition();
|
|
const QPixmap image = p->pixmap();
|
|
int iWidth = image.width();
|
|
int iHeight = image.height();
|
|
float wFactor = static_cast<float>(iWidth) / p->widget()->width();
|
|
float hFactor = static_cast<float>(iHeight) / p->widget()->height();
|
|
zoomWidth *= wFactor;
|
|
zoomHeight *= hFactor;
|
|
if (p->verticalScrollBar()->minimum() == p->verticalScrollBar()->maximum()) {
|
|
int xp = static_cast<int>(((x - p->widget()->pos().x()) * wFactor) - zoomWidth / 2);
|
|
int yp = static_cast<int>((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());
|
|
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<int>(((x - p->widget()->pos().x()) * wFactor) - zoomWidth / 2);
|
|
int yp = static_cast<int>((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());
|
|
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));
|
|
}
|
|
move(static_cast<int>(x - float(width()) / 2), static_cast<int>(y - float(height()) / 2));
|
|
}
|
|
|
|
void MagnifyingGlass::updateImage()
|
|
{
|
|
if (isVisible()) {
|
|
QPoint p = QPoint(cursor().pos().x(), cursor().pos().y());
|
|
p = this->parentWidget()->mapFromGlobal(p);
|
|
updateImage(p.x(), p.y());
|
|
}
|
|
}
|
|
void MagnifyingGlass::wheelEvent(QWheelEvent *event)
|
|
{
|
|
switch (event->modifiers()) {
|
|
// size
|
|
case Qt::NoModifier:
|
|
if (event->angleDelta().y() < 0)
|
|
sizeUp();
|
|
else
|
|
sizeDown();
|
|
break;
|
|
// size height
|
|
case Qt::ControlModifier:
|
|
if (event->angleDelta().y() < 0)
|
|
heightUp();
|
|
else
|
|
heightDown();
|
|
break;
|
|
// size width
|
|
case Qt::AltModifier: // alt modifier can actually modify the behavior of the event delta, so let's check both x & y
|
|
if (event->angleDelta().y() < 0 || event->angleDelta().x() < 0)
|
|
widthUp();
|
|
else
|
|
widthDown();
|
|
break;
|
|
// zoom level
|
|
case Qt::ShiftModifier:
|
|
if (event->angleDelta().y() < 0)
|
|
zoomIn();
|
|
else
|
|
zoomOut();
|
|
break;
|
|
default:
|
|
break; // Never propagate a wheel event to the parent widget, even if we ignore it.
|
|
}
|
|
event->setAccepted(true);
|
|
}
|
|
void MagnifyingGlass::zoomIn()
|
|
{
|
|
if (zoomLevel > 0.2f) {
|
|
zoomLevel -= 0.025f;
|
|
emit zoomChanged(zoomLevel);
|
|
updateImage();
|
|
}
|
|
}
|
|
|
|
void MagnifyingGlass::zoomOut()
|
|
{
|
|
if (zoomLevel < 0.9f) {
|
|
zoomLevel += 0.025f;
|
|
emit zoomChanged(zoomLevel);
|
|
updateImage();
|
|
}
|
|
}
|
|
|
|
void MagnifyingGlass::sizeUp()
|
|
{
|
|
auto w = width();
|
|
auto h = height();
|
|
if (growWidth(w) | growHeight(h)) // bitwise OR prevents short-circuiting
|
|
resizeAndUpdate(w, h);
|
|
}
|
|
|
|
void MagnifyingGlass::sizeDown()
|
|
{
|
|
auto w = width();
|
|
auto h = height();
|
|
if (shrinkWidth(w) | shrinkHeight(h)) // bitwise OR prevents short-circuiting
|
|
resizeAndUpdate(w, h);
|
|
}
|
|
|
|
void MagnifyingGlass::heightUp()
|
|
{
|
|
auto h = height();
|
|
if (growHeight(h))
|
|
resizeAndUpdate(width(), h);
|
|
}
|
|
|
|
void MagnifyingGlass::heightDown()
|
|
{
|
|
auto h = height();
|
|
if (shrinkHeight(h))
|
|
resizeAndUpdate(width(), h);
|
|
}
|
|
|
|
void MagnifyingGlass::widthUp()
|
|
{
|
|
auto w = width();
|
|
if (growWidth(w))
|
|
resizeAndUpdate(w, height());
|
|
}
|
|
|
|
void MagnifyingGlass::widthDown()
|
|
{
|
|
auto w = width();
|
|
if (shrinkWidth(w))
|
|
resizeAndUpdate(w, height());
|
|
}
|
|
|
|
void MagnifyingGlass::reset()
|
|
{
|
|
zoomLevel = 0.5f;
|
|
emit zoomChanged(zoomLevel);
|
|
resizeAndUpdate(350, 175);
|
|
}
|
|
|
|
void MagnifyingGlass::resizeAndUpdate(int w, int h)
|
|
{
|
|
resize(w, h);
|
|
emit sizeChanged(size());
|
|
updateImage();
|
|
}
|
|
|
|
static constexpr auto maxRelativeDimension = 0.9;
|
|
static constexpr auto widthStep = 30;
|
|
static constexpr auto heightStep = 15;
|
|
|
|
bool MagnifyingGlass::growWidth(int &w) const
|
|
{
|
|
const auto maxWidth = parentWidget()->width() * maxRelativeDimension;
|
|
if (w >= maxWidth)
|
|
return false;
|
|
w += widthStep;
|
|
return true;
|
|
}
|
|
|
|
bool MagnifyingGlass::shrinkWidth(int &w) const
|
|
{
|
|
constexpr auto minWidth = 175;
|
|
if (w <= minWidth)
|
|
return false;
|
|
w -= widthStep;
|
|
return true;
|
|
}
|
|
|
|
bool MagnifyingGlass::growHeight(int &h) const
|
|
{
|
|
const auto maxHeight = parentWidget()->height() * maxRelativeDimension;
|
|
if (h >= maxHeight)
|
|
return false;
|
|
h += heightStep;
|
|
return true;
|
|
}
|
|
|
|
bool MagnifyingGlass::shrinkHeight(int &h) const
|
|
{
|
|
constexpr auto minHeight = 80;
|
|
if (h <= minHeight)
|
|
return false;
|
|
h -= heightStep;
|
|
return true;
|
|
}
|