/* This file is part of Telegram Desktop, the official desktop version of Telegram messaging app, see https://telegram.org Telegram Desktop 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 3 of the License, or (at your option) any later version. It 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. Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014 John Preston, https://desktop.telegram.org */ #include "stdafx.h" #include "localimageloader.h" #include "gui/filedialog.h" #include LocalImageLoaderPrivate::LocalImageLoaderPrivate(int32 currentUser, LocalImageLoader *loader, QThread *thread) : QObject(0) , loader(loader) , user(currentUser) { moveToThread(thread); connect(loader, SIGNAL(needToPrepare()), this, SLOT(prepareImages())); connect(this, SIGNAL(imageReady()), loader, SLOT(onImageReady())); connect(this, SIGNAL(imageFailed(quint64)), loader, SLOT(onImageFailed(quint64))); }; void LocalImageLoaderPrivate::prepareImages() { QString file, filename, mime; int32 filesize = 0; QImage img; QByteArray data; PeerId peer; uint64 id, jpeg_id = 0; ToPrepareMediaType type; bool animated = false; bool ctrlShiftEnter = false; { QMutexLocker lock(loader->toPrepareMutex()); ToPrepareMedias &list(loader->toPrepareMedias()); if (list.isEmpty()) return; file = list.front().file; img = list.front().img; data = list.front().data; peer = list.front().peer; id = list.front().id; type = list.front().type; ctrlShiftEnter = list.front().ctrlShiftEnter; } if (img.isNull()) { if (!file.isEmpty()) { QFileInfo info(file); if (type == ToPrepareAuto) { QString lower(file.toLower()); const QStringList &photoExtensions(cPhotoExtensions()); for (QStringList::const_iterator i = photoExtensions.cbegin(), e = photoExtensions.cend(); i != e; ++i) { if (lower.lastIndexOf(*i) == lower.size() - i->size()) { if (info.size() < MaxUploadPhotoSize) { type = ToPreparePhoto; break; } } } if (type == ToPrepareAuto && info.size() < MaxUploadDocumentSize) { type = ToPrepareDocument; } } if (type != ToPrepareAuto && info.size() < MaxUploadPhotoSize) { img = App::readImage(file, 0, true, &animated); } if (type == ToPrepareDocument) { mime = QMimeDatabase().mimeTypeForFile(info).name(); } filename = info.fileName(); filesize = info.size(); } else if (!data.isEmpty()) { img = App::readImage(data, 0, true, &animated); if (type == ToPrepareAuto) { if (!img.isNull() && data.size() < MaxUploadPhotoSize) { type = ToPreparePhoto; } else if (data.size() < MaxUploadDocumentSize) { type = ToPrepareDocument; } else { img = QImage(); } } QMimeType mimeType = QMimeDatabase().mimeTypeForData(data); if (type == ToPrepareDocument) { mime = mimeType.name(); } filename = qsl("Document"); QStringList patterns = mimeType.globPatterns(); if (!patterns.isEmpty()) { filename = patterns.front().replace('*', filename); } filesize = data.size(); } } else { if (type == ToPrepareDocument) { filename = filedialogDefaultName(qsl("image"), qsl(".png"), QString(), true); QMimeType mimeType = QMimeDatabase().mimeTypeForName("image/png"); mime = mimeType.name(); data = QByteArray(); { QBuffer b(&data); img.save(&b, "PNG"); } filesize = data.size(); } else { type = ToPreparePhoto; // only photo from QImage filename = qsl("Untitled.jpg"); filesize = 0; } } if ((img.isNull() && (type != ToPrepareDocument || !filesize)) || type == ToPrepareAuto || (img.isNull() && file.isEmpty() && data.isEmpty())) { // if could not decide what type { QMutexLocker lock(loader->toPrepareMutex()); ToPrepareMedias &list(loader->toPrepareMedias()); list.pop_front(); } QTimer::singleShot(1, this, SLOT(prepareImages())); emit imageFailed(id); } else { PreparedPhotoThumbs photoThumbs; QVector photoSizes; QVector attributes(1, MTP_documentAttributeFilename(MTP_string(filename))); MTPPhotoSize thumb(MTP_photoSizeEmpty(MTP_string(""))); MTPPhoto photo(MTP_photoEmpty(MTP_long(0))); MTPDocument document(MTP_documentEmpty(MTP_long(0))); QByteArray jpeg; if (type == ToPreparePhoto) { int32 w = img.width(), h = img.height(); QPixmap thumb = (w > 100 || h > 100) ? QPixmap::fromImage(img.scaled(100, 100, Qt::KeepAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly) : QPixmap::fromImage(img); photoThumbs.insert('s', thumb); photoSizes.push_back(MTP_photoSize(MTP_string("s"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(thumb.width()), MTP_int(thumb.height()), MTP_int(0))); QPixmap medium = (w > 320 || h > 320) ? QPixmap::fromImage(img.scaled(320, 320, Qt::KeepAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly) : QPixmap::fromImage(img); photoThumbs.insert('m', medium); photoSizes.push_back(MTP_photoSize(MTP_string("m"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(medium.width()), MTP_int(medium.height()), MTP_int(0))); QPixmap full = (w > 1280 || h > 1280) ? QPixmap::fromImage(img.scaled(1280, 1280, Qt::KeepAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly) : QPixmap::fromImage(img); photoThumbs.insert('y', full); photoSizes.push_back(MTP_photoSize(MTP_string("y"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(full.width()), MTP_int(full.height()), MTP_int(0))); { QBuffer jpegBuffer(&jpeg); full.save(&jpegBuffer, "JPG", 77); } if (!filesize) filesize = jpeg.size(); photo = MTP_photo(MTP_long(id), MTP_long(0), MTP_int(user), MTP_int(unixtime()), MTP_string(""), MTP_geoPointEmpty(), MTP_vector(photoSizes)); jpeg_id = id; } else if ((type == ToPrepareVideo || type == ToPrepareDocument) && !img.isNull()) { int32 w = img.width(), h = img.height(); QByteArray thumbFormat = "JPG"; if (animated) { attributes.push_back(MTP_documentAttributeAnimated()); } else if (mime == qsl("image/webp") && w > 0 && h > 0 && filesize < StickerInMemory) { attributes.push_back(MTP_documentAttributeSticker()); thumbFormat = "webp"; } attributes.push_back(MTP_documentAttributeImageSize(MTP_int(w), MTP_int(h))); QPixmap full = (w > 90 || h > 90) ? QPixmap::fromImage(img.scaled(90, 90, Qt::KeepAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly) : QPixmap::fromImage(img, Qt::ColorOnly); { QBuffer jpegBuffer(&jpeg); full.save(&jpegBuffer, thumbFormat, 87); } photoThumbs.insert('0', full); thumb = MTP_photoSize(MTP_string(""), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(full.width()), MTP_int(full.height()), MTP_int(0)); jpeg_id = MTP::nonce(); } if (type == ToPrepareDocument) { document = MTP_document(MTP_long(id), MTP_long(0), MTP_int(unixtime()), MTP_string(mime), MTP_int(filesize), thumb, MTP_int(MTP::maindc()), MTP_vector(attributes)); } { QMutexLocker lock(loader->readyMutex()); loader->readyList().push_back(ReadyLocalMedia(type, file, filename, filesize, data, id, jpeg_id, peer, photo, photoThumbs, document, jpeg, ctrlShiftEnter)); } { QMutexLocker lock(loader->toPrepareMutex()); ToPrepareMedias &list(loader->toPrepareMedias()); list.pop_front(); } QTimer::singleShot(1, this, SLOT(prepareImages())); emit imageReady(); } } LocalImageLoaderPrivate::~LocalImageLoaderPrivate() { loader = 0; } LocalImageLoader::LocalImageLoader(QObject *parent) : QObject(parent), thread(0), priv(0) { } void LocalImageLoader::append(const QStringList &files, const PeerId &peer, ToPrepareMediaType t) { { QMutexLocker lock(toPrepareMutex()); for (QStringList::const_iterator i = files.cbegin(), e = files.cend(); i != e; ++i) { toPrepare.push_back(ToPrepareMedia(*i, peer, t, false)); } } if (!thread) { thread = new QThread(); priv = new LocalImageLoaderPrivate(MTP::authedId(), this, thread); thread->start(); } emit needToPrepare(); } PhotoId LocalImageLoader::append(const QByteArray &img, const PeerId &peer, ToPrepareMediaType t) { PhotoId result = 0; { QMutexLocker lock(toPrepareMutex()); toPrepare.push_back(ToPrepareMedia(img, peer, t, false)); result = toPrepare.back().id; } if (!thread) { thread = new QThread(); priv = new LocalImageLoaderPrivate(MTP::authedId(), this, thread); thread->start(); } emit needToPrepare(); return result; } PhotoId LocalImageLoader::append(const QImage &img, const PeerId &peer, ToPrepareMediaType t, bool ctrlShiftEnter) { PhotoId result = 0; { QMutexLocker lock(toPrepareMutex()); toPrepare.push_back(ToPrepareMedia(img, peer, t, ctrlShiftEnter)); result = toPrepare.back().id; } if (!thread) { thread = new QThread(); priv = new LocalImageLoaderPrivate(MTP::authedId(), this, thread); thread->start(); } emit needToPrepare(); return result; } PhotoId LocalImageLoader::append(const QString &file, const PeerId &peer, ToPrepareMediaType t) { PhotoId result = 0; { QMutexLocker lock(toPrepareMutex()); toPrepare.push_back(ToPrepareMedia(file, peer, t, false)); result = toPrepare.back().id; } if (!thread) { thread = new QThread(); priv = new LocalImageLoaderPrivate(MTP::authedId(), this, thread); thread->start(); } emit needToPrepare(); return result; } void LocalImageLoader::onImageReady() { { QMutexLocker lock(toPrepareMutex()); if (toPrepare.isEmpty()) { if (priv) priv->deleteLater(); priv = 0; if (thread) thread->deleteLater(); thread = 0; } } emit imageReady(); } void LocalImageLoader::onImageFailed(quint64 id) { { QMutexLocker lock(toPrepareMutex()); if (toPrepare.isEmpty()) { if (priv) priv->deleteLater(); priv = 0; if (thread) thread->deleteLater(); thread = 0; } } emit imageFailed(id); } QMutex *LocalImageLoader::readyMutex() { return &readyLock; } ReadyLocalMedias &LocalImageLoader::readyList() { return ready; } QMutex *LocalImageLoader::toPrepareMutex() { return &toPrepareLock; } ToPrepareMedias &LocalImageLoader::toPrepareMedias() { return toPrepare; } LocalImageLoader::~LocalImageLoader() { delete priv; delete thread; }