QNetworkAccessManager
and QEventLoop
, as well as some meta objects. We will upload an HTTP image in any format supported by Qt. Well, we will still handle redirects.Networking
class (I made it static, but it does not play a big role), and we create the NetworkingPrivate
class - for real work. // networking.h class Networking { public: static QImage httpGetImage(const QUrl& src); static void httpGetImageAsync(const QUrl& src, QObject * receiver, const char * slot); private: static NetworkingPrivate * networkingPrivate; static void init(); } // networking_p.h class NetworkingPrivate : public QObject { Q_OBJECT public: NetworkingPrivate(); ~NetworkingPrivate(); QImage httpGetImage(const QUrl& src) const; void httpGetImageAsync(const QUrl& src, QObject * receiver, const char * slot); public slots: void onFinished(QNetworkReply* reply); private: QNetworkAccessManager * nam; QEventLoop * loop; QMap<QNetworkReply*, QPair<QObject*, QPair<QUrl, const char *> > > requests; };
// myclass.h class MyClass: public QObject { // ... public slots: void loadImage(const QString & urlString); void onImageReady(const QUrl& url, const QImage & image); } // myclass.cpp void MyClass::loadImage(const QString & urlString) { Networking::httpGetImageAsync(QUrl(urlString), this, "onImageRead"); }
QNetworkAccessManager
we need to send http-requests, QEventLoop
- to wait for a response in the case of synchronous requests, and QMap<QNetworkReply*, QPair<QObject*, QPair<QUrl, const char *> > > requests
- to store all requests to know which picture to which object should be delivered after loading.Networking
class, as you guessed, only redirects calls to its private class). NetworkingPrivate::NetworkingPrivate() { nam = new QNetworkAccessManager(); loop = new QEventLoop(); connect(nam, SIGNAL(finished(QNetworkReply*)), loop, SLOT(quit())); connect(nam, SIGNAL(finished(QNetworkReply*)), SLOT(onFinished(QNetworkReply*))); } NetworkingPrivate::~NetworkingPrivate() { nam->deleteLater(); loop->deleteLater(); } QImage NetworkingPrivate::httpGetImage(const QUrl& src) const { QNetworkRequest request; request.setUrl(src); QNetworkReply * reply = nam->get(request); loop->exec(); QVariant redirectedUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); QUrl redirectedTo = redirectedUrl.toUrl(); if (redirectedTo.isValid()) { // guard from infinite redirect loop if (redirectedTo != reply->request().url()) { return httpGetImage(redirectedTo); } else { qWarning() << "[NetworkingPrivate] Infinite redirect loop at " + redirectedTo.toString(); return QImage(); } } else { QImage img; QImageReader reader(reply); if (reply->error() == QNetworkReply::NoError) reader.read(&img); else qWarning() << QString("[NetworkingPrivate] Reply error: %1").arg(reply->error()); reply->deleteLater(); return img; } } void NetworkingPrivate::httpGetImageAsync(const QUrl& src, QObject * receiver, const char * slot) { QNetworkRequest request; request.setUrl(src); QPair<QObject*, QPair<QUrl, const char *> > obj; obj.first = receiver; obj.second.first = src; obj.second.second = slot; QNetworkReply * reply = nam->get(request); requests.insert(reply, obj); } void NetworkingPrivate::onFinished(QNetworkReply* reply) { if (requests.contains(reply)) { QPair<QObject*, QPair<QUrl, const char *> > obj = requests.value(reply); QVariant redirectedUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); QUrl redirectedTo = redirectedUrl.toUrl(); if (redirectedTo.isValid()) { // guard from infinite redirect loop if (redirectedTo != reply->request().url()) { httpGetImageAsync(redirectedTo, obj.first, obj.second.second); } else { qWarning() << "[NetworkingPrivate] Infinite redirect loop at " + redirectedTo.toString(); } } else { QImage img; QImageReader reader(reply); if (reply->error() == QNetworkReply::NoError) reader.read(&img); else qWarning() << QString("[NetworkingPrivate] Reply error: %1").arg(reply->error()); if (obj.first && obj.second.second) QMetaObject::invokeMethod(obj.first, obj.second.second, Qt::DirectConnection, Q_ARG(QUrl, obj.second.first), Q_ARG(QImage, img)); } requests.remove(reply); reply->deleteLater(); } }
QEventLoop
, a QNetworkAccessManager
and connect the signal to complete the request with QEventLoop::quit()
and our onFinished
method.QImageReader
and read the data into the final QImage
, which we return.QMap
, and then run the request. And at the end of the request, we do the same thing as with a synchronous request (checking for redirect, its cyclicality and reading the picture), but the resulting QImage
passed to the target object using QMetaObject::invokeMethod
. As parameters - URL request and image.ImageManager
. And we will have the following methods in it: class ImageManager { public: static QImage normallyResized(const QImage & image, int maximumSideSize); static QImage grayscaled(const QImage & image); static QImage squared(const QImage & image, int size); static QImage roundSquared(const QImage & image, int size, int radius); static QImage addShadow(const QImage & image, QColor color, QPoint offset, bool canResize = false); static QImage colorized(const QImage & image, QColor color); static QImage opacitized(const QImage & image, double opacity = 0.5); static QImage addSpace(const QImage & image, int left, int top, int right, int bottom); static QImage rotatedImage(const QImage & image, qreal angle); static QColor resolveColor(const QString & name); };
QImage gray(image.size(), QImage::Format_ARGB32); gray.fill(Qt::transparent); static QVector<QRgb> monoTable; if (monoTable.isEmpty()) { for (int i = 0; i <= 255; i++) monoTable.append(qRgb(i, i, i)); } QPainter p(&gray); p.drawImage(0, 0, image.convertToFormat(QImage::Format_Indexed8, monoTable)); p.end(); return gray;
QImage img = image; if (!image.isNull()) { int pixels = img.width() * img.height(); if (pixels*(int)sizeof(QRgb) <= img.byteCount()) { QRgb *data = (QRgb *)img.bits(); for (int i = 0; i < pixels; i++) { int val = qGray(data[i]); data[i] = qRgba(val, val, val, qAlpha(data[i])); } } } return img;
QImage shapeImg(QSize(size, size), QImage::Format_ARGB32_Premultiplied); shapeImg.fill(Qt::transparent); QPainter sp(&shapeImg); sp.setRenderHint(QPainter::Antialiasing); sp.setPen(QPen(Qt::color1)); sp.setBrush(QBrush(Qt::color1)); sp.drawRoundedRect(QRect(0, 0, size, size), radius + 1, radius + 1); sp.end(); QImage roundSquaredImage(size, size, QImage::Format_ARGB32_Premultiplied); roundSquaredImage.fill(Qt::transparent); QPainter p(&roundSquaredImage); p.drawImage(0, 0, shapeImg); p.setCompositionMode(QPainter::CompositionMode_SourceIn); p.drawImage(0, 0, squared(image, size)); p.end(); return roundSquaredImage;
QPainter::CompositionMode_SourceIn
. Simple and tasteful, as they say. QSize shadowedSize = image.size(); if (canResize) { shadowedSize += QSize(qAbs(offset.x()), qAbs(offset.y())); } QImage shadowed(shadowedSize, QImage::Format_ARGB32_Premultiplied); shadowed.fill(Qt::transparent); QPainter p(&shadowed); QImage shadowImage(image.size(), QImage::Format_ARGB32_Premultiplied); shadowImage.fill(Qt::transparent); QPainter tmpPainter(&shadowImage); tmpPainter.setCompositionMode(QPainter::CompositionMode_Source); tmpPainter.drawPixmap(QPoint(0, 0), QPixmap::fromImage(image)); tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); tmpPainter.fillRect(shadowImage.rect(), color); tmpPainter.end(); QPoint shadowOffset = offset; if (canResize) { if (offset.x() < 0) shadowOffset.setX(0); if (offset.y() < 0) shadowOffset.setY(0); } p.drawImage(shadowOffset, shadowImage); QPoint originalOffset(0, 0); if (canResize) { if (offset.x() < 0) originalOffset.setX(qAbs(offset.x())); if (offset.y() < 0) originalOffset.setY(qAbs(offset.y())); } p.drawPixmap(originalOffset, QPixmap::fromImage(image)); p.end(); return shadowed;
QImage resultImage(image.size(), QImage::Format_ARGB32_Premultiplied); resultImage.fill(Qt::transparent); QPainter painter(&resultImage); painter.drawImage(0, 0, grayscaled(image)); painter.setCompositionMode(QPainter::CompositionMode_Screen); painter.fillRect(resultImage.rect(), color); painter.end(); resultImage.setAlphaChannel(image.alphaChannel()); return resultImage;
QPainter::setOpacity
. QImage resultImage(image.size(), QImage::Format_ARGB32); resultImage.fill(Qt::transparent); QPainter painter(&resultImage); painter.setOpacity(opacity); painter.drawImage(0, 0, image); painter.end(); resultImage.setAlphaChannel(image.alphaChannel()); return resultImage;
QImage rotated(image.size(), QImage::Format_ARGB32_Premultiplied); rotated.fill(Qt::transparent); QPainter p(&rotated); p.setRenderHint(QPainter::Antialiasing); p.setRenderHint(QPainter::SmoothPixmapTransform); qreal dx = image.size().width() / 2.0, dy = image.size().height() / 2.0; p.translate(dx, dy); p.rotate(angle); p.translate(-dx, -dy); p.drawImage(0, 0, image); p.end(); return rotated;
#RRGGBBAA
format, which I replenished with my function: QColor ImageManager::resolveColor(const QString & name) { QColor color; if (QColor::isValidColor(name)) color.setNamedColor(name); else { // trying to parse "#RRGGBBAA" color if (name.length() == 9) { QString solidColor = name.left(7); if (QColor::isValidColor(solidColor)) { color.setNamedColor(solidColor); int alpha = name.right(2).toInt(0, 16); color.setAlpha(alpha); } } } if (!color.isValid()) qWarning() << QString("[ImageManager::resolveColor] Can\'t parse color: %1").arg(name); return color; }
white
, transparent
, #ffa0ee
) are also well understood.Source: https://habr.com/ru/post/139265/
All Articles