kde-extraapps/gwenview/lib/imagescaler.cpp
Ivailo Monev 39b4708812 gwenview: log debug messages to special area
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2023-06-20 04:13:03 +03:00

205 lines
6.1 KiB
C++

/*
Gwenview: an image viewer
Copyright 2007 Aurélien Gâteau <agateau@kde.org>
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "moc_imagescaler.cpp"
// Qt
#include <QImage>
#include <QRegion>
// KDE
#include <KDebug>
// Local
#include <lib/document/document.h>
#include <lib/paintutils.h>
namespace Gwenview
{
// Amount of pixels to keep so that smooth scale is correct
static const int SMOOTH_MARGIN = 3;
struct ImageScalerPrivate
{
Qt::TransformationMode mTransformationMode;
Document::Ptr mDocument;
qreal mZoom;
QRegion mRegion;
};
ImageScaler::ImageScaler(QObject* parent)
: QObject(parent)
, d(new ImageScalerPrivate)
{
d->mTransformationMode = Qt::FastTransformation;
d->mZoom = 0;
}
ImageScaler::~ImageScaler()
{
delete d;
}
void ImageScaler::setDocument(Document::Ptr document)
{
if (d->mDocument) {
disconnect(d->mDocument.data(), 0, this, 0);
}
d->mDocument = document;
// Used when scaler asked for a down-sampled image
connect(d->mDocument.data(), SIGNAL(downSampledImageReady()),
SLOT(doScale()));
// Used when scaler asked for a full image
connect(d->mDocument.data(), SIGNAL(loaded(KUrl)),
SLOT(doScale()));
}
void ImageScaler::setZoom(qreal zoom)
{
d->mZoom = zoom;
}
void ImageScaler::setTransformationMode(Qt::TransformationMode mode)
{
d->mTransformationMode = mode;
}
void ImageScaler::setDestinationRegion(const QRegion& region)
{
kDebug() << region;
d->mRegion = region;
if (d->mRegion.isEmpty()) {
return;
}
if (d->mDocument && d->mZoom > 0) {
doScale();
}
}
void ImageScaler::doScale()
{
if (d->mZoom < Document::maxDownSampledZoom()) {
if (!d->mDocument->prepareDownSampledImageForZoom(d->mZoom)) {
kDebug() << "Asked for a down sampled image";
return;
}
} else if (d->mDocument->image().isNull()) {
kDebug() << "Asked for the full image";
d->mDocument->startLoadingFullImage();
return;
}
kDebug() << "Starting";
Q_FOREACH(const QRect & rect, d->mRegion.rects()) {
kDebug() << rect;
scaleRect(rect);
}
kDebug() << "Done";
}
void ImageScaler::scaleRect(const QRect& rect)
{
const qreal REAL_DELTA = 0.001;
if (qAbs(d->mZoom - 1.0) < REAL_DELTA) {
QImage tmp = d->mDocument->image().copy(rect);
tmp = tmp.convertToFormat(QImage::Format_ARGB32_Premultiplied);
emit scaledRect(rect.left(), rect.top(), tmp);
return;
}
QImage image;
qreal zoom;
if (d->mZoom < Document::maxDownSampledZoom()) {
image = d->mDocument->downSampledImageForZoom(d->mZoom);
Q_ASSERT(!image.isNull());
qreal zoom1 = qreal(image.width()) / d->mDocument->width();
zoom = d->mZoom / zoom1;
} else {
image = d->mDocument->image();
zoom = d->mZoom;
}
// If rect contains "half" pixels, make sure sourceRect includes them
QRectF sourceRectF(
rect.left() / zoom,
rect.top() / zoom,
rect.width() / zoom,
rect.height() / zoom);
sourceRectF = sourceRectF.intersected(image.rect());
QRect sourceRect = PaintUtils::containingRect(sourceRectF);
if (sourceRect.isEmpty()) {
return;
}
// Compute smooth margin
bool needsSmoothMargins = d->mTransformationMode == Qt::SmoothTransformation;
int sourceLeftMargin, sourceRightMargin, sourceTopMargin, sourceBottomMargin;
int destLeftMargin, destRightMargin, destTopMargin, destBottomMargin;
if (needsSmoothMargins) {
sourceLeftMargin = qMin(sourceRect.left(), SMOOTH_MARGIN);
sourceTopMargin = qMin(sourceRect.top(), SMOOTH_MARGIN);
sourceRightMargin = qMin(image.rect().right() - sourceRect.right(), SMOOTH_MARGIN);
sourceBottomMargin = qMin(image.rect().bottom() - sourceRect.bottom(), SMOOTH_MARGIN);
sourceRect.adjust(
-sourceLeftMargin,
-sourceTopMargin,
sourceRightMargin,
sourceBottomMargin);
destLeftMargin = int(sourceLeftMargin * zoom);
destTopMargin = int(sourceTopMargin * zoom);
destRightMargin = int(sourceRightMargin * zoom);
destBottomMargin = int(sourceBottomMargin * zoom);
} else {
sourceLeftMargin = sourceRightMargin = sourceTopMargin = sourceBottomMargin = 0;
destLeftMargin = destRightMargin = destTopMargin = destBottomMargin = 0;
}
// destRect is almost like rect, but it contains only "full" pixels
QRectF destRectF = QRectF(
sourceRect.left() * zoom,
sourceRect.top() * zoom,
sourceRect.width() * zoom,
sourceRect.height() * zoom
);
QRect destRect = PaintUtils::containingRect(destRectF);
QImage tmp;
tmp = image.copy(sourceRect);
tmp = tmp.convertToFormat(QImage::Format_ARGB32_Premultiplied);
tmp = tmp.scaled(
destRect.width(),
destRect.height(),
Qt::IgnoreAspectRatio, // Do not use KeepAspectRatio, it can lead to skipped rows or columns
d->mTransformationMode);
if (needsSmoothMargins) {
tmp = tmp.copy(
destLeftMargin, destTopMargin,
destRect.width() - (destLeftMargin + destRightMargin),
destRect.height() - (destTopMargin + destBottomMargin)
);
}
emit scaledRect(destRect.left() + destLeftMargin, destRect.top() + destTopMargin, tmp);
}
} // namespace