1 | /****************************************************************************
|
---|
2 | **
|
---|
3 | ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
---|
4 | ** All rights reserved.
|
---|
5 | ** Contact: Nokia Corporation ([email protected])
|
---|
6 | **
|
---|
7 | ** This file is part of the examples of the Qt Toolkit.
|
---|
8 | **
|
---|
9 | ** $QT_BEGIN_LICENSE:BSD$
|
---|
10 | ** You may use this file under the terms of the BSD license as follows:
|
---|
11 | **
|
---|
12 | ** "Redistribution and use in source and binary forms, with or without
|
---|
13 | ** modification, are permitted provided that the following conditions are
|
---|
14 | ** met:
|
---|
15 | ** * Redistributions of source code must retain the above copyright
|
---|
16 | ** notice, this list of conditions and the following disclaimer.
|
---|
17 | ** * Redistributions in binary form must reproduce the above copyright
|
---|
18 | ** notice, this list of conditions and the following disclaimer in
|
---|
19 | ** the documentation and/or other materials provided with the
|
---|
20 | ** distribution.
|
---|
21 | ** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
|
---|
22 | ** the names of its contributors may be used to endorse or promote
|
---|
23 | ** products derived from this software without specific prior written
|
---|
24 | ** permission.
|
---|
25 | **
|
---|
26 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
---|
27 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
---|
28 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
---|
29 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
---|
30 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
---|
31 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
---|
32 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
---|
33 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
---|
34 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
---|
35 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
---|
36 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
---|
37 | ** $QT_END_LICENSE$
|
---|
38 | **
|
---|
39 | ****************************************************************************/
|
---|
40 |
|
---|
41 |
|
---|
42 | #include <QNetworkReply>
|
---|
43 | #include <QNetworkRequest>
|
---|
44 | #include <QNetworkAccessManager>
|
---|
45 | #include <QNetworkDiskCache>
|
---|
46 | #include "imageanalyzer.h"
|
---|
47 |
|
---|
48 | /*!
|
---|
49 | * This class operates as follows:
|
---|
50 | * Parent calls the slot startAnalysis which shoves a list of QStrings into URLQueue and then calls fetchURLs.
|
---|
51 | * FetchURLs sends out HTTP GETs for each image it can't get out of the cache.
|
---|
52 | * As the responses come in, handleReply tries to create an image out of each and pushes those images into imageQueue.
|
---|
53 | * On the last (detected by no outstandingFetches and URLQueue.isEmpty()) call to queueImage (from handleReply)
|
---|
54 | * a thread is forked to process all the images. When it finishes, it emits a finished signal that is received
|
---|
55 | * by our JavaScript code.
|
---|
56 | */
|
---|
57 |
|
---|
58 | //! [ ImageAnalyzer - Constructor ]
|
---|
59 | ImageAnalyzer::ImageAnalyzer(QNetworkDiskCache* netcache, QObject* parent)
|
---|
60 | : QObject(parent), m_cache(netcache), m_outstandingFetches(0)
|
---|
61 | {
|
---|
62 | /* ImageAnalyzer only wants to receive http responses
|
---|
63 | for requests that it makes, so that's why it has its own
|
---|
64 | QNetworkAccessManager. */
|
---|
65 | m_network = new QNetworkAccessManager(this);
|
---|
66 | m_watcher = new QFutureWatcher<QRgb>(this);
|
---|
67 | /* We want to share a cache with the web browser,
|
---|
68 | in case it has some images we want: */
|
---|
69 | m_network->setCache(m_cache);
|
---|
70 |
|
---|
71 | QObject::connect(m_network, SIGNAL(finished(QNetworkReply*)),
|
---|
72 | this, SLOT(handleReply(QNetworkReply*)));
|
---|
73 | QObject::connect(m_watcher, SIGNAL(finished()),
|
---|
74 | this, SLOT(doneProcessing()));
|
---|
75 | QObject::connect(m_watcher, SIGNAL(progressValueChanged(int)),
|
---|
76 | this, SLOT(progressStatus(int)));
|
---|
77 | }
|
---|
78 | //! [ ImageAnalyzer - Constructor ]
|
---|
79 | ImageAnalyzer::~ImageAnalyzer()
|
---|
80 | {
|
---|
81 | delete(m_watcher);
|
---|
82 | }
|
---|
83 |
|
---|
84 |
|
---|
85 | QRgb ImageAnalyzer::lastResults()
|
---|
86 | {
|
---|
87 | int rTot = 0;
|
---|
88 | int bTot = 0;
|
---|
89 | int gTot = 0;
|
---|
90 | int count = m_watcher->future().results().size();
|
---|
91 | foreach(const QRgb & triplet, m_watcher->future().results())
|
---|
92 | {
|
---|
93 | rTot += qRed(triplet);
|
---|
94 | bTot += qBlue(triplet);
|
---|
95 | gTot += qGreen(triplet);
|
---|
96 | }
|
---|
97 | return qRgb(rTot/count, bTot/count, gTot/count);
|
---|
98 | }
|
---|
99 |
|
---|
100 | float ImageAnalyzer::lastRed() { return qRed(lastResults())/2.55; }
|
---|
101 | float ImageAnalyzer::lastGreen() { return qGreen(lastResults())/2.55; }
|
---|
102 | float ImageAnalyzer::lastBlue() { return qBlue(lastResults())/2.55; }
|
---|
103 |
|
---|
104 | void ImageAnalyzer::progressStatus(int newstat)
|
---|
105 | {
|
---|
106 | emit updateProgress(newstat, m_watcher->progressMaximum());
|
---|
107 | }
|
---|
108 |
|
---|
109 |
|
---|
110 | bool ImageAnalyzer::isBusy()
|
---|
111 | {
|
---|
112 | return m_watcher->isRunning();
|
---|
113 | }
|
---|
114 |
|
---|
115 |
|
---|
116 | //! [ ImageAnalyzer - startAnalysis ]
|
---|
117 | void ImageAnalyzer::startAnalysis(const QStringList & urls)
|
---|
118 | {
|
---|
119 | m_URLQueue = urls;
|
---|
120 | fetchURLs();
|
---|
121 | }
|
---|
122 | //! [ ImageAnalyzer - startAnalysis ]
|
---|
123 |
|
---|
124 | /*!
|
---|
125 | * Analyzes the entire queue - just starts all our http GETs.
|
---|
126 | */
|
---|
127 | //! [ ImageAnalyzer - fetchURLs ]
|
---|
128 | void ImageAnalyzer::fetchURLs()
|
---|
129 | {
|
---|
130 | while (!m_URLQueue.isEmpty())
|
---|
131 | {
|
---|
132 | QString url = m_URLQueue.takeFirst();
|
---|
133 | QUrl URL = QUrl(url);
|
---|
134 | QIODevice * pData = m_cache->data(URL);
|
---|
135 | // Is image already loaded in cache?
|
---|
136 | if (pData == 0) {
|
---|
137 | // HTTP Get image over network.
|
---|
138 | m_outstandingFetches++;
|
---|
139 | QNetworkRequest request = QNetworkRequest(URL);
|
---|
140 | request.setRawHeader("User-Agent", "Nokia - Custom QT app");
|
---|
141 | m_network->get(request);
|
---|
142 | } else {
|
---|
143 | // Get image from cache
|
---|
144 | QImage image;
|
---|
145 | image.load(pData, 0);
|
---|
146 | if (!image.isNull())
|
---|
147 | queueImage(image);
|
---|
148 | delete(pData);
|
---|
149 | }
|
---|
150 | }
|
---|
151 | }
|
---|
152 | //! [ ImageAnalyzer - fetchURLs ]
|
---|
153 | /*
|
---|
154 | * Slot to handle the incoming responses from our http GETs
|
---|
155 | */
|
---|
156 | //! [ ImageAnalyzer - handleReply ]
|
---|
157 | void ImageAnalyzer::handleReply(QNetworkReply * pReply)
|
---|
158 | {
|
---|
159 | m_outstandingFetches--;
|
---|
160 | if (pReply->error()) {
|
---|
161 | qDebug() << "Error code" << pReply->error();
|
---|
162 | qDebug() << "Http code" << pReply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
|
---|
163 | return;
|
---|
164 | }
|
---|
165 | QImage image;
|
---|
166 | image.load(pReply, 0);
|
---|
167 | pReply->deleteLater();
|
---|
168 | if (image.isNull()) {
|
---|
169 | qDebug() << "bad image";
|
---|
170 | qDebug() << pReply->rawHeaderList();
|
---|
171 | foreach(QByteArray element, pReply->rawHeaderList()) {
|
---|
172 | qDebug() << element << " = " << pReply->rawHeader(element);
|
---|
173 | }
|
---|
174 | return;
|
---|
175 | }
|
---|
176 | queueImage(image);
|
---|
177 | }
|
---|
178 | //! [ ImageAnalyzer - handleReply ]
|
---|
179 |
|
---|
180 | void ImageAnalyzer::doneProcessing()
|
---|
181 | {
|
---|
182 | m_imageQueue = QList<QImage>();
|
---|
183 | emit finishedAnalysis();
|
---|
184 | }
|
---|
185 | //! [ ImageAnalyzer - queueImage ]
|
---|
186 | void ImageAnalyzer::queueImage(QImage img)
|
---|
187 | {
|
---|
188 | if (!img.isNull())
|
---|
189 | m_imageQueue << img;
|
---|
190 |
|
---|
191 | if (m_outstandingFetches == 0 && m_URLQueue.isEmpty()) {
|
---|
192 | m_watcher->setFuture(QtConcurrent::mapped(m_imageQueue, averageRGB));
|
---|
193 | }
|
---|
194 | }
|
---|
195 | //! [ ImageAnalyzer - queueImage ]
|
---|
196 |
|
---|
197 | //! [ ImageAnalyzer - averageRGB ]
|
---|
198 | QRgb averageRGB(const QImage &img)
|
---|
199 | {
|
---|
200 | int pixelCount = img.width() * img.height();
|
---|
201 | int rAvg, gAvg, bAvg;
|
---|
202 |
|
---|
203 | // We waste some time here:
|
---|
204 | for (int timeWaster=0; timeWaster < 100; timeWaster++) {
|
---|
205 | quint64 rTot = 0;
|
---|
206 | quint64 gTot = 0;
|
---|
207 | quint64 bTot = 0;
|
---|
208 | for (int i=0; i < img.width(); i++) {
|
---|
209 | for (int j=0; j < img.height(); j++) {
|
---|
210 | QRgb pixel = img.pixel(i,j);
|
---|
211 | rTot += qRed(pixel);
|
---|
212 | gTot += qGreen(pixel);
|
---|
213 | bTot += qBlue(pixel);
|
---|
214 | }
|
---|
215 | }
|
---|
216 | rAvg = (rTot)/(pixelCount);
|
---|
217 | gAvg = (gTot)/(pixelCount);
|
---|
218 | bAvg = (bTot)/(pixelCount);
|
---|
219 | }
|
---|
220 | return qRgb(rAvg, gAvg, bAvg);
|
---|
221 | }
|
---|
222 | //! [ ImageAnalyzer - averageRGB ]
|
---|