diff --git a/src/properties/downloadedpiecesbar.cpp b/src/properties/downloadedpiecesbar.cpp new file mode 100644 index 000000000..469b66a7b --- /dev/null +++ b/src/properties/downloadedpiecesbar.cpp @@ -0,0 +1,235 @@ +/* + * Bittorrent Client using Qt4 and libtorrent. + * Copyright (C) 2006 Christophe Dumez + * + * 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. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + * + * Contact : chris@qbittorrent.org + */ + +#include "downloadedpiecesbar.h" + +//#include + +DownloadedPiecesBar::DownloadedPiecesBar(QWidget *parent): QWidget(parent) +{ + setFixedHeight(BAR_HEIGHT); + + bg_color = 0xffffff; + piece_color = 0x0000ff; + piece_color_dl = 0x00d000; + + updatePieceColors(); +} + +std::vector DownloadedPiecesBar::bitfieldToFloatVector(const libtorrent::bitfield &vecin, int reqSize) +{ + std::vector result(reqSize, 0.0); + + if (vecin.size() == 0) + return result; + + const float ratio = vecin.size() / (float)reqSize; + + // simple linear transformation algorithm + // for example: + // image.x(0) = pieces.x(0.0 >= x < 1.7) + // image.x(1) = pieces.x(1.7 >= x < 3.4) + + for (int x = 0; x < reqSize; ++x) { + + // don't use previously calculated value "ratio" here!!! + // float cannot save irrational number like 7/9, if this number will be rounded up by std::ceil + // give you x2 == pieces.size(), and index out of range: pieces[x2] + // this code is safe, so keep that in mind when you try optimize more. + // tested with size = 3000000ul + + // R - real + const float fromR = (x * vecin.size()) / (float)reqSize; + const float toR = ((x + 1) * vecin.size()) / (float)reqSize; + + // C - integer + int fromC = fromR;// std::floor not needed + int toC = std::ceil(toR); + + // position in pieces table + // libtorrent::bitfield::m_size is unsigned int(31 bits), so qlonglong is not needed + // tested with size = 3000000ul + int x2 = fromC; + + // little speed up for really big pieces table, 10K+ size + const int toCMinusOne = toC - 1; + + // value in returned vector + float value = 0; + + // case when calculated range is (15.2 >= x < 15.7) + if (x2 == toCMinusOne) { + if (vecin[x2]) { + value += toR - fromR; + } + ++x2; + } + // case when (15.2 >= x < 17.8) + else { + // subcase (15.2 >= x < 16) + if (x2 != fromR) { + if (vecin[x2]) { + value += 1.0 - (fromR - fromC); + } + ++x2; + } + + // subcase (16 >= x < 17) + for (; x2 < toCMinusOne; ++x2) { + if (vecin[x2]) { + value += 1.0; + } + } + + // subcase (17 >= x < 17.8) + if (x2 == toCMinusOne) { + if (vecin[x2]) { + value += 1.0 - (toC - toR); + } + ++x2; + } + } + + // normalization <0, 1> + value /= ratio; + + // float precision sometimes gives > 1, bacause in not possible to store irrational numbers + value = qMin(value, (float)1.0); + + result[x] = value; + } + + return result; +} + + +int DownloadedPiecesBar::mixTwoColors(int &rgb1, int &rgb2, float ratio) +{ + int r1 = qRed(rgb1); + int g1 = qGreen(rgb1); + int b1 = qBlue(rgb1); + + int r2 = qRed(rgb2); + int g2 = qGreen(rgb2); + int b2 = qBlue(rgb2); + + float ratio_n = 1.0 - ratio; + int r = (r1 * ratio_n) + (r2 * ratio); + int g = (g1 * ratio_n) + (g2 * ratio); + int b = (b1 * ratio_n) + (b2 * ratio); + + return qRgb(r, g, b); +} + +void DownloadedPiecesBar::updateImage() +{ +// qDebug() << "updateImage"; + QImage image2(width() - 2, 1, QImage::Format_RGB888); + + if (pieces.empty()) { + image2.fill(0xffffff); + image = image2; + update(); + return; + } + + std::vector scaled_pieces = bitfieldToFloatVector(pieces, image2.width()); + std::vector scaled_pieces_dl = bitfieldToFloatVector(pieces_dl, image2.width()); + + // filling image + for (unsigned int x = 0; x < scaled_pieces.size(); ++x) + { + float pieces2_val = scaled_pieces.at(x); + float pieces2_val_dl = scaled_pieces_dl.at(x); + if (pieces2_val_dl != 0) + { + float fill_ratio = pieces2_val + pieces2_val_dl; + float ratio = pieces2_val_dl / fill_ratio; + + int mixedColor = mixTwoColors(piece_color, piece_color_dl, ratio); + mixedColor = mixTwoColors(bg_color, mixedColor, fill_ratio); + + image2.setPixel(x, 0, mixedColor); + } + else + { + image2.setPixel(x, 0, piece_colors[pieces2_val * 255]); + } + } + image = image2; +} + +void DownloadedPiecesBar::setProgress(const libtorrent::bitfield &bf, const libtorrent::bitfield &bf_dl) +{ + pieces.assign(bf.bytes(), bf.size()); + pieces_dl.assign(bf_dl.bytes(), bf_dl.size()); + + updateImage(); + update(); +} + +void DownloadedPiecesBar::updatePieceColors() +{ + piece_colors = std::vector(256); + for (int i = 0; i < 256; ++i) { + float ratio = (i / 255.0); + piece_colors[i] = mixTwoColors(bg_color, piece_color, ratio); + } +} + +void DownloadedPiecesBar::clear() +{ + image = QImage(); + update(); +} + +void DownloadedPiecesBar::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + QRect imageRect(1, 1, width() - 2, height() - 2); + if (image.isNull()) + { + painter.setBrush(Qt::white); + painter.drawRect(imageRect); + } + else + { + if (image.width() != imageRect.width()) + updateImage(); + painter.drawImage(imageRect, image); + } + QPainterPath border; + border.addRect(0, 0, width() - 1, height() - 1); + + painter.setPen(palette().color(QPalette::Dark)); + painter.drawPath(border); +} + + diff --git a/src/properties/downloadedpiecesbar.h b/src/properties/downloadedpiecesbar.h index 8bb2bbf8d..d7b4b52a5 100644 --- a/src/properties/downloadedpiecesbar.h +++ b/src/properties/downloadedpiecesbar.h @@ -33,99 +33,51 @@ #include #include -#include -#include +#include +#include #include -#include #define BAR_HEIGHT 18 class DownloadedPiecesBar: public QWidget { - Q_OBJECT - Q_DISABLE_COPY(DownloadedPiecesBar) + Q_OBJECT + Q_DISABLE_COPY(DownloadedPiecesBar) private: - QPixmap pixmap; + QImage image; + // I used values, bacause it should be possible to change colors in runtime -public: - DownloadedPiecesBar(QWidget *parent): QWidget(parent) { - setFixedHeight(BAR_HEIGHT); - } + // background color + int bg_color; + // complete piece color + int piece_color; + // incomplete piece color + int piece_color_dl; + // buffered 256 levels gradient from bg_color to piece_color + std::vector piece_colors; - void setProgress(const libtorrent::bitfield &pieces, const libtorrent::bitfield &downloading_pieces) { - if(pieces.empty()) { - // Empty bar - QPixmap pix = QPixmap(1, 1); - pix.fill(); - pixmap = pix; - } else { - const qulonglong nb_pieces = pieces.size(); - // Reduce the number of pieces before creating the pixmap - // otherwise it can crash when there are too many pieces - const uint w = width(); - if(nb_pieces > w) { - const uint ratio = floor(nb_pieces/(double)w); - libtorrent::bitfield scaled_pieces(ceil(nb_pieces/(double)ratio), false); - libtorrent::bitfield scaled_downloading(ceil(nb_pieces/(double)ratio), false); - uint scaled_index = 0; - for(qulonglong i=0; i 20x faster + libtorrent::bitfield pieces; + libtorrent::bitfield pieces_dl; - void clear() { - pixmap = QPixmap(); - update(); - } + // scale bitfield vector to float vector + std::vector bitfieldToFloatVector(const libtorrent::bitfield &vecin, int reqSize); + // mix two colors by light model, ratio <0, 1> + int mixTwoColors(int &rgb1, int &rgb2, float ratio); + // draw new image and replace actual image + void updateImage(); -protected: - void paintEvent(QPaintEvent *) { - if(pixmap.isNull()) return; - QPainter painter(this); - painter.drawPixmap(rect(), pixmap); - } +public: + DownloadedPiecesBar(QWidget *parent); -private: - void updatePixmap(const libtorrent::bitfield &pieces, const libtorrent::bitfield &downloading_pieces) { - QPixmap pix = QPixmap(pieces.size(), 1); - //pix.fill(); - QPainter painter(&pix); - for(uint i=0; i