File indexing completed on 2024-05-12 05:09:49
0001 /*************************************************************************** 0002 Copyright (C) 2003-2009 Robby Stephenson <robby@periapsis.org> 0003 ***************************************************************************/ 0004 0005 /*************************************************************************** 0006 * * 0007 * This program is free software; you can redistribute it and/or * 0008 * modify it under the terms of the GNU General Public License as * 0009 * published by the Free Software Foundation; either version 2 of * 0010 * the License or (at your option) version 3 or any later version * 0011 * accepted by the membership of KDE e.V. (or its successor approved * 0012 * by the membership of KDE e.V.), which shall act as a proxy * 0013 * defined in Section 14 of version 3 of the license. * 0014 * * 0015 * This program is distributed in the hope that it will be useful, * 0016 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0018 * GNU General Public License for more details. * 0019 * * 0020 * You should have received a copy of the GNU General Public License * 0021 * along with this program. If not, see <http://www.gnu.org/licenses/>. * 0022 * * 0023 ***************************************************************************/ 0024 0025 #include "image.h" 0026 #include "../utils/string_utils.h" 0027 #include "../tellico_debug.h" 0028 0029 #include <QBuffer> 0030 #include <QRegularExpression> 0031 #include <QImageReader> 0032 #include <QImageWriter> 0033 #include <QCryptographicHash> 0034 0035 using Tellico::Data::Image; 0036 0037 const Image Image::null; 0038 QList<QByteArray> Image::s_outputFormats; 0039 0040 Image::Image() : QImage(), m_linkOnly(false) { 0041 } 0042 0043 Image::Image(const Image& other) : QImage(other) 0044 , m_id(other.m_id) 0045 , m_format(other.m_format) 0046 , m_linkOnly(other.m_linkOnly) { 0047 } 0048 0049 Image& Image::operator=(const Image& other) { 0050 QImage::operator=(other); 0051 if(this != &other) { 0052 m_id = other.m_id; 0053 m_format = other.m_format; 0054 m_linkOnly = other.m_linkOnly; 0055 } 0056 return *this; 0057 } 0058 0059 // I'm using the MD5 hash as the id. I consider it rather unlikely that two images in one 0060 // collection could ever have the same hash, and this lets me do a fast comparison of two images 0061 // simply by comparing their ids. 0062 Image::Image(const QString& filename_, const QString& id_) : QImage(), m_id(idClean(id_)), m_linkOnly(false) { 0063 QImageReader reader; 0064 reader.setAutoTransform(true); 0065 reader.setFileName(filename_); 0066 m_format = reader.format(); 0067 if(!reader.read(this)) { 0068 // Tellico had an earlier bug where images were written in PNG format with a GIF extension 0069 // and for some reason, qt doesn't recognize the file then, so fall back and try to load as PNG 0070 reader.setFormat("PNG"); 0071 if(reader.read(this)) { 0072 myWarning() << filename_ << "loaded as PNG image"; 0073 m_format = "PNG"; 0074 } 0075 } 0076 if(m_id.isEmpty()) { 0077 calculateID(); 0078 } 0079 } 0080 0081 Image::Image(const QImage& img_, const QString& format_) : QImage(img_), m_format(format_.toLatin1()), m_linkOnly(false) { 0082 calculateID(); 0083 } 0084 0085 Image::Image(const QByteArray& data_, const QString& format_, const QString& id_) 0086 : QImage(QImage::fromData(data_)), m_id(idClean(id_)), m_format(format_.toLatin1()), m_linkOnly(false) { 0087 if(isNull()) { 0088 m_id.clear(); 0089 } else if(m_id.isEmpty()) { 0090 calculateID(); 0091 } 0092 } 0093 0094 Image::~Image() { 0095 } 0096 0097 QByteArray Image::byteArray() const { 0098 return byteArray(*this, outputFormat(m_format)); 0099 } 0100 0101 bool Image::isNull() const { 0102 // 1x1 images are considered null for Tellico. Amazon returns some like that. 0103 return QImage::isNull() || (width() < 2 && height() < 2); 0104 } 0105 0106 QPixmap Image::convertToPixmap() const { 0107 return QPixmap::fromImage(*this); 0108 } 0109 0110 QPixmap Image::convertToPixmap(int w_, int h_) const { 0111 if(w_ < width() || h_ < height()) { 0112 return QPixmap::fromImage(*this).scaled(w_, h_, Qt::KeepAspectRatio); 0113 } else { 0114 return QPixmap::fromImage(*this); 0115 } 0116 } 0117 0118 QByteArray Image::outputFormat(const QByteArray& inputFormat) { 0119 if(s_outputFormats.isEmpty()) { 0120 QList<QByteArray> list = QImageWriter::supportedImageFormats(); 0121 foreach(const QByteArray& format, list) { 0122 s_outputFormats.append(format.toUpper()); 0123 } 0124 } 0125 if(s_outputFormats.contains(inputFormat.toUpper())) { 0126 return inputFormat; 0127 } 0128 myWarning() << "writing" << inputFormat << "as PNG"; 0129 return "PNG"; 0130 } 0131 0132 QByteArray Image::byteArray(const QImage& img_, const QByteArray& outputFormat_) { 0133 QByteArray ba; 0134 QBuffer buf(&ba); 0135 buf.open(QIODevice::WriteOnly); 0136 QImageWriter writer(&buf, outputFormat_); 0137 if(!writer.write(img_)) { 0138 myDebug() << writer.errorString(); 0139 } 0140 buf.close(); 0141 return ba; 0142 } 0143 0144 QString Image::idClean(const QString& id_) { 0145 static const QRegularExpression rx(QLatin1Char('[') + QRegularExpression::escape(QLatin1String("/@<>#\"&%?={}|^~[]'`\\:+")) + QLatin1Char(']')); 0146 QString clean = id_; 0147 return Tellico::shareString(clean.remove(rx)); 0148 } 0149 0150 void Image::setID(const QString& id_) { 0151 // don't clean the id if we're linking only 0152 m_id = m_linkOnly ? id_ : idClean(id_); 0153 } 0154 0155 void Image::calculateID() { 0156 // the id will eventually be used as a filename 0157 if(!isNull()) { 0158 m_id = calculateID(byteArray(), QLatin1String(m_format)); 0159 } 0160 } 0161 0162 QString Image::calculateID(const QByteArray& data_, const QString& format_) { 0163 QCryptographicHash md5(QCryptographicHash::Md5); 0164 md5.addData(data_); 0165 QString id = QLatin1String(md5.result().toHex()) + QLatin1Char('.') + format_.toLower(); 0166 return idClean(id); 0167 }