mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-01-27 07:47:03 -05:00
Merge pull request #1077 from hunbernd/feature/image-embed3
Make it possible to send images over chat lobby
This commit is contained in:
commit
1c2d17b5c1
@ -111,7 +111,7 @@ ChatWidget::ChatWidget(QWidget *parent) :
|
|||||||
ui->searchButton->setFixedSize(buttonSize);
|
ui->searchButton->setFixedSize(buttonSize);
|
||||||
ui->searchButton->setIconSize(iconSize);
|
ui->searchButton->setIconSize(iconSize);
|
||||||
ui->sendButton->setFixedHeight(iconHeight);
|
ui->sendButton->setFixedHeight(iconHeight);
|
||||||
ui->sendButton->setIconSize(iconSize);
|
ui->sendButton->setIconSize(iconSize);
|
||||||
|
|
||||||
//Initialize search
|
//Initialize search
|
||||||
iCharToStartSearch=Settings->getChatSearchCharToStartSearch();
|
iCharToStartSearch=Settings->getChatSearchCharToStartSearch();
|
||||||
@ -295,6 +295,7 @@ void ChatWidget::init(const ChatId &chat_id, const QString &title)
|
|||||||
this->title = title;
|
this->title = title;
|
||||||
|
|
||||||
ui->titleLabel->setText(RsHtml::plainText(title));
|
ui->titleLabel->setText(RsHtml::plainText(title));
|
||||||
|
ui->chatTextEdit->setMaxBytes(this->maxMessageSize() - 200);
|
||||||
|
|
||||||
RsPeerId ownId = rsPeers->getOwnId();
|
RsPeerId ownId = rsPeers->getOwnId();
|
||||||
setName(QString::fromUtf8(rsPeers->getPeerName(ownId).c_str()));
|
setName(QString::fromUtf8(rsPeers->getPeerName(ownId).c_str()));
|
||||||
@ -452,6 +453,25 @@ void ChatWidget::processSettings(bool load)
|
|||||||
Settings->endGroup();
|
Settings->endGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t ChatWidget::maxMessageSize()
|
||||||
|
{
|
||||||
|
uint32_t maxMessageSize = 0;
|
||||||
|
switch (chatType()) {
|
||||||
|
case CHATTYPE_UNKNOWN:
|
||||||
|
break;
|
||||||
|
case CHATTYPE_PRIVATE:
|
||||||
|
maxMessageSize = rsMsgs->getMaxMessageSecuritySize(RS_CHAT_TYPE_PRIVATE);
|
||||||
|
break;
|
||||||
|
case CHATTYPE_LOBBY:
|
||||||
|
maxMessageSize = rsMsgs->getMaxMessageSecuritySize(RS_CHAT_TYPE_LOBBY);
|
||||||
|
break;
|
||||||
|
case CHATTYPE_DISTANT:
|
||||||
|
maxMessageSize = rsMsgs->getMaxMessageSecuritySize(RS_CHAT_TYPE_DISTANT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return maxMessageSize;
|
||||||
|
}
|
||||||
|
|
||||||
bool ChatWidget::eventFilter(QObject *obj, QEvent *event)
|
bool ChatWidget::eventFilter(QObject *obj, QEvent *event)
|
||||||
{
|
{
|
||||||
if (obj == ui->textBrowser || obj == ui->textBrowser->viewport()
|
if (obj == ui->textBrowser || obj == ui->textBrowser->viewport()
|
||||||
@ -1169,20 +1189,7 @@ void ChatWidget::updateLenOfChatTextEdit()
|
|||||||
RsHtml::optimizeHtml(chatWidget, text);
|
RsHtml::optimizeHtml(chatWidget, text);
|
||||||
std::wstring msg = text.toStdWString();
|
std::wstring msg = text.toStdWString();
|
||||||
|
|
||||||
uint32_t maxMessageSize = 0;
|
uint32_t maxMessageSize = this->maxMessageSize();
|
||||||
switch (chatType()) {
|
|
||||||
case CHATTYPE_UNKNOWN:
|
|
||||||
break;
|
|
||||||
case CHATTYPE_PRIVATE:
|
|
||||||
maxMessageSize = rsMsgs->getMaxMessageSecuritySize(RS_CHAT_TYPE_PRIVATE);
|
|
||||||
break;
|
|
||||||
case CHATTYPE_LOBBY:
|
|
||||||
maxMessageSize = rsMsgs->getMaxMessageSecuritySize(RS_CHAT_TYPE_LOBBY);
|
|
||||||
break;
|
|
||||||
case CHATTYPE_DISTANT:
|
|
||||||
maxMessageSize = rsMsgs->getMaxMessageSecuritySize(RS_CHAT_TYPE_DISTANT);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
int charRemains = 0;
|
int charRemains = 0;
|
||||||
if (maxMessageSize > 0) {
|
if (maxMessageSize > 0) {
|
||||||
@ -1561,7 +1568,8 @@ void ChatWidget::addExtraPicture()
|
|||||||
QString file;
|
QString file;
|
||||||
if (misc::getOpenFileName(window(), RshareSettings::LASTDIR_IMAGES, tr("Load Picture File"), "Pictures (*.png *.xpm *.jpg *.jpeg)", file)) {
|
if (misc::getOpenFileName(window(), RshareSettings::LASTDIR_IMAGES, tr("Load Picture File"), "Pictures (*.png *.xpm *.jpg *.jpeg)", file)) {
|
||||||
QString encodedImage;
|
QString encodedImage;
|
||||||
if (RsHtml::makeEmbeddedImage(file, encodedImage, 640*480)) {
|
uint32_t maxMessageSize = this->maxMessageSize();
|
||||||
|
if (RsHtml::makeEmbeddedImage(file, encodedImage, 640*480, maxMessageSize - 200)) { //-200 for the html stuff
|
||||||
QTextDocumentFragment fragment = QTextDocumentFragment::fromHtml(encodedImage);
|
QTextDocumentFragment fragment = QTextDocumentFragment::fromHtml(encodedImage);
|
||||||
ui->chatTextEdit->textCursor().insertFragment(fragment);
|
ui->chatTextEdit->textCursor().insertFragment(fragment);
|
||||||
}
|
}
|
||||||
|
@ -198,6 +198,8 @@ private:
|
|||||||
void setColorAndFont(bool both);
|
void setColorAndFont(bool both);
|
||||||
void processSettings(bool load);
|
void processSettings(bool load);
|
||||||
|
|
||||||
|
uint32_t maxMessageSize();
|
||||||
|
|
||||||
void completeNickname(bool reverse);
|
void completeNickname(bool reverse);
|
||||||
QAbstractItemModel *modelFromPeers();
|
QAbstractItemModel *modelFromPeers();
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ void MimeTextEdit::insertFromMimeData(const QMimeData* source)
|
|||||||
QImage image = qvariant_cast<QImage>(source->imageData());
|
QImage image = qvariant_cast<QImage>(source->imageData());
|
||||||
if (image.isNull() == false) {
|
if (image.isNull() == false) {
|
||||||
QString encodedImage;
|
QString encodedImage;
|
||||||
if (RsHtml::makeEmbeddedImage(image, encodedImage, 640*480)) {
|
if (RsHtml::makeEmbeddedImage(image, encodedImage, 640*480, mMaxBytes)) {
|
||||||
QTextDocumentFragment fragment = QTextDocumentFragment::fromHtml(encodedImage);
|
QTextDocumentFragment fragment = QTextDocumentFragment::fromHtml(encodedImage);
|
||||||
textCursor().insertFragment(fragment);
|
textCursor().insertFragment(fragment);
|
||||||
return;
|
return;
|
||||||
|
@ -50,6 +50,8 @@ public:
|
|||||||
QColor textColorQuote() const { return highliter->textColorQuote();}
|
QColor textColorQuote() const { return highliter->textColorQuote();}
|
||||||
bool onlyPlainText() const {return mOnlyPlainText;}
|
bool onlyPlainText() const {return mOnlyPlainText;}
|
||||||
|
|
||||||
|
void setMaxBytes(int limit) {mMaxBytes = limit;}
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setTextColorQuote(QColor textColorQuote) { highliter->setTextColorQuote(textColorQuote);}
|
void setTextColorQuote(QColor textColorQuote) { highliter->setTextColorQuote(textColorQuote);}
|
||||||
void setOnlyPlainText(bool bOnlyPlainText) {mOnlyPlainText = bOnlyPlainText;}
|
void setOnlyPlainText(bool bOnlyPlainText) {mOnlyPlainText = bOnlyPlainText;}
|
||||||
@ -83,6 +85,7 @@ private:
|
|||||||
QList<QAction*> mContextMenuActions;
|
QList<QAction*> mContextMenuActions;
|
||||||
RsSyntaxHighlighter *highliter;
|
RsSyntaxHighlighter *highliter;
|
||||||
bool mOnlyPlainText;
|
bool mOnlyPlainText;
|
||||||
|
int mMaxBytes = -1; //limit content size, for pasting images
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MIMETEXTEDIT_H
|
#endif // MIMETEXTEDIT_H
|
||||||
|
@ -35,6 +35,8 @@
|
|||||||
#include "HandleRichText.h"
|
#include "HandleRichText.h"
|
||||||
#include "gui/RetroShareLink.h"
|
#include "gui/RetroShareLink.h"
|
||||||
#include "util/ObjectPainter.h"
|
#include "util/ObjectPainter.h"
|
||||||
|
#include "util/imageutil.h"
|
||||||
|
#include "util/rsscopetimer.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
@ -1107,7 +1109,7 @@ QString RsHtml::toHtml(QString text, bool realHtml)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Loads image and converts image to embedded image HTML fragment **/
|
/** Loads image and converts image to embedded image HTML fragment **/
|
||||||
bool RsHtml::makeEmbeddedImage(const QString &fileName, QString &embeddedImage, const int maxPixels)
|
bool RsHtml::makeEmbeddedImage(const QString &fileName, QString &embeddedImage, const int maxPixels, const int maxBytes)
|
||||||
{
|
{
|
||||||
QImage image;
|
QImage image;
|
||||||
|
|
||||||
@ -1115,54 +1117,15 @@ bool RsHtml::makeEmbeddedImage(const QString &fileName, QString &embeddedImage,
|
|||||||
fprintf (stderr, "RsHtml::makeEmbeddedImage() - image \"%s\" can't be load\n", fileName.toLatin1().constData());
|
fprintf (stderr, "RsHtml::makeEmbeddedImage() - image \"%s\" can't be load\n", fileName.toLatin1().constData());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return RsHtml::makeEmbeddedImage(image, embeddedImage, maxPixels);
|
return RsHtml::makeEmbeddedImage(image, embeddedImage, maxPixels, maxBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Converts image to embedded image HTML fragment **/
|
/** Converts image to embedded image HTML fragment **/
|
||||||
bool RsHtml::makeEmbeddedImage(const QImage &originalImage, QString &embeddedImage, const int maxPixels)
|
bool RsHtml::makeEmbeddedImage(const QImage &originalImage, QString &embeddedImage, const int maxPixels, const int maxBytes)
|
||||||
{
|
{
|
||||||
QByteArray bytearray;
|
RsScopeTimer s("Embed image");
|
||||||
QBuffer buffer(&bytearray);
|
QImage opt;
|
||||||
QImage resizedImage;
|
return ImageUtil::optimizeSize(embeddedImage, originalImage, opt, maxPixels, maxBytes);
|
||||||
const QImage *image = &originalImage;
|
|
||||||
|
|
||||||
if (maxPixels > 0) {
|
|
||||||
QSize imgSize = originalImage.size();
|
|
||||||
if ((imgSize.height() * imgSize.width()) > maxPixels) {
|
|
||||||
// image is too large - resize keeping aspect ratio
|
|
||||||
QSize newSize;
|
|
||||||
newSize.setWidth(int(qSqrt((maxPixels * imgSize.width()) / imgSize.height())));
|
|
||||||
newSize.setHeight(int((imgSize.height() * newSize.width()) / imgSize.width()));
|
|
||||||
|
|
||||||
// ask user
|
|
||||||
QMessageBox msgBox;
|
|
||||||
msgBox.setText(QString(QApplication::translate("RsHtml", "Image is oversized for transmission.\nReducing image to %1x%2 pixels?")).arg(newSize.width()).arg(newSize.height()));
|
|
||||||
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
|
|
||||||
msgBox.setDefaultButton(QMessageBox::Ok);
|
|
||||||
if (msgBox.exec() != QMessageBox::Ok) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
resizedImage = originalImage.scaled(newSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
|
||||||
image = &resizedImage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer.open(QIODevice::WriteOnly)) {
|
|
||||||
if (image->save(&buffer, "PNG")) {
|
|
||||||
QByteArray encodedByteArray = bytearray.toBase64();
|
|
||||||
|
|
||||||
embeddedImage = "<img src=\"data:image/png;base64,";
|
|
||||||
embeddedImage.append(encodedByteArray);
|
|
||||||
embeddedImage.append("\">");
|
|
||||||
} else {
|
|
||||||
//fprintf (stderr, "RsHtml::makeEmbeddedImage() - image can't be saved to buffer\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fprintf (stderr, "RsHtml::makeEmbeddedImage() - buffer can't be opened\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString RsHtml::plainText(const QString &text)
|
QString RsHtml::plainText(const QString &text)
|
||||||
|
@ -69,8 +69,8 @@ public:
|
|||||||
static void optimizeHtml(QString &text, unsigned int flag = 0, const QColor &backgroundColor = Qt::white, qreal desiredContrast = 1.0, int desiredMinimumFontSize = 10);
|
static void optimizeHtml(QString &text, unsigned int flag = 0, const QColor &backgroundColor = Qt::white, qreal desiredContrast = 1.0, int desiredMinimumFontSize = 10);
|
||||||
static QString toHtml(QString text, bool realHtml = true);
|
static QString toHtml(QString text, bool realHtml = true);
|
||||||
|
|
||||||
static bool makeEmbeddedImage(const QString &fileName, QString &embeddedImage, const int maxPixels);
|
static bool makeEmbeddedImage(const QString &fileName, QString &embeddedImage, const int maxPixels, const int maxBytes = -1);
|
||||||
static bool makeEmbeddedImage(const QImage &originalImage, QString &embeddedImage, const int maxPixels);
|
static bool makeEmbeddedImage(const QImage &originalImage, QString &embeddedImage, const int maxPixels, const int maxBytes = -1);
|
||||||
|
|
||||||
static QString plainText(const QString &text);
|
static QString plainText(const QString &text);
|
||||||
static QString plainText(const std::string &text);
|
static QString plainText(const std::string &text);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "imageutil.h"
|
#include "imageutil.h"
|
||||||
#include "util/misc.h"
|
#include "util/misc.h"
|
||||||
|
#include "util/rsscopetimer.h"
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
@ -8,6 +9,11 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QTextCursor>
|
#include <QTextCursor>
|
||||||
#include <QTextDocumentFragment>
|
#include <QTextDocumentFragment>
|
||||||
|
#include <QBuffer>
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <QSet>
|
||||||
|
#include <cmath>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
ImageUtil::ImageUtil() {}
|
ImageUtil::ImageUtil() {}
|
||||||
|
|
||||||
@ -42,3 +48,211 @@ void ImageUtil::extractImage(QWidget *window, QTextCursor cursor)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ImageUtil::optimizeSize(QString &html, const QImage& original, QImage &optimized, int maxPixels, int maxBytes)
|
||||||
|
{
|
||||||
|
//nothing to do if it fits into the limits
|
||||||
|
optimized = original;
|
||||||
|
if ((maxPixels <= 0) || (optimized.width()*optimized.height() <= maxPixels)) {
|
||||||
|
if(checkSize(html, optimized, maxBytes) <= maxBytes) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<QRgb> ct;
|
||||||
|
quantization(original, ct);
|
||||||
|
|
||||||
|
//Downscale the image to fit into maxPixels
|
||||||
|
double whratio = (qreal)original.width() / (qreal)original.height();
|
||||||
|
int maxwidth;
|
||||||
|
if(maxPixels > 0)
|
||||||
|
maxwidth = (int)sqrt((double)(maxPixels) * whratio);
|
||||||
|
else
|
||||||
|
maxwidth = original.width();
|
||||||
|
|
||||||
|
int minwidth = (int)sqrt(100.0 * whratio);
|
||||||
|
|
||||||
|
//if maxBytes not defined, do not reduce color space, just downscale
|
||||||
|
if(maxBytes <= 0) {
|
||||||
|
checkSize(html, optimized = original.scaledToWidth(maxwidth, Qt::SmoothTransformation), maxBytes);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Use binary search to find a suitable image size + linear regression to guess the file size
|
||||||
|
double maxsize = (double)checkSize(html, optimized = original.scaledToWidth(maxwidth, Qt::SmoothTransformation).convertToFormat(QImage::Format_Indexed8, ct), maxBytes);
|
||||||
|
if(maxsize <= maxBytes) return true; //success
|
||||||
|
double minsize = (double)checkSize(html, optimized = original.scaledToWidth(minwidth, Qt::SmoothTransformation).convertToFormat(QImage::Format_Indexed8, ct), maxBytes);
|
||||||
|
if(minsize > maxBytes) return false; //impossible
|
||||||
|
|
||||||
|
// std::cout << "maxS: " << maxsize << " minS: " << minsize << std::endl;
|
||||||
|
// std::cout << "maxW: " << maxwidth << " minW: " << minwidth << std::endl;
|
||||||
|
int region = 500;
|
||||||
|
bool success = false;
|
||||||
|
do {
|
||||||
|
double m = (maxsize - minsize) / ((double)maxwidth * (double)maxwidth / whratio - (double)minwidth * (double)minwidth / whratio);
|
||||||
|
double b = maxsize - m * ((double)maxwidth * (double)maxwidth / whratio);
|
||||||
|
double a = ((double)(maxBytes - region/2) - b) / m; //maxBytes - region/2 target the center of the accepted region
|
||||||
|
int nextwidth = (int)sqrt(a * whratio);
|
||||||
|
double nextsize = (double)checkSize(html, optimized = original.scaledToWidth(nextwidth, Qt::SmoothTransformation).convertToFormat(QImage::Format_Indexed8, ct), maxBytes);
|
||||||
|
if(nextsize <= maxBytes) {
|
||||||
|
minsize = nextsize;
|
||||||
|
minwidth = nextwidth;
|
||||||
|
if(nextsize >= (maxBytes - region)) //the file size is close anough to the limit
|
||||||
|
success = true;
|
||||||
|
} else {
|
||||||
|
maxsize = nextsize;
|
||||||
|
maxwidth = nextwidth;
|
||||||
|
}
|
||||||
|
// std::cout << "maxS: " << maxsize << " minS: " << minsize << std::endl;
|
||||||
|
// std::cout << "maxW: " << maxwidth << " minW: " << minwidth << std::endl;
|
||||||
|
} while(!success);
|
||||||
|
return true;
|
||||||
|
//html = html.arg(original.width());
|
||||||
|
//std::cout << html.toStdString() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ImageUtil::checkSize(QString &embeddedImage, const QImage &img, int maxBytes)
|
||||||
|
{
|
||||||
|
RsScopeTimer st("Check size");
|
||||||
|
|
||||||
|
QByteArray bytearray;
|
||||||
|
QBuffer buffer(&bytearray);
|
||||||
|
int size = 0;
|
||||||
|
|
||||||
|
//std::cout << QString("Trying image: format PNG, size %1x%2, colors %3\n").arg(img.width()).arg(img.height()).arg(img.colorCount()).toStdString();
|
||||||
|
if (buffer.open(QIODevice::WriteOnly)) {
|
||||||
|
if (img.save(&buffer, "PNG", 0)) {
|
||||||
|
size = bytearray.length() * 4/3;
|
||||||
|
if((maxBytes > 0) && (size > maxBytes)) // *4/3 for base64
|
||||||
|
{
|
||||||
|
//std::cout << QString("\tToo large, size: %1, limit: %2 bytes\n").arg(bytearray.length() * 4/3).arg(maxBytes).toStdString();
|
||||||
|
}else{
|
||||||
|
//std::cout << QString("\tOK, size: %1, limit: %2 bytes\n").arg(bytearray.length() * 4/3).arg(maxBytes).toStdString();
|
||||||
|
QByteArray encodedByteArray = bytearray.toBase64();
|
||||||
|
//embeddedImage = "<img width=\"%1\" src=\"data:image/png;base64,";
|
||||||
|
embeddedImage = "<img src=\"data:image/png;base64,";
|
||||||
|
embeddedImage.append(encodedByteArray);
|
||||||
|
embeddedImage.append("\">");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::cerr << "ImageUtil: image can't be saved to buffer" << std::endl;
|
||||||
|
}
|
||||||
|
buffer.close();
|
||||||
|
bytearray.clear();
|
||||||
|
} else {
|
||||||
|
std::cerr << "ImageUtil: buffer can't be opened" << std::endl;
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool redLessThan(const QRgb &c1, const QRgb &c2)
|
||||||
|
{
|
||||||
|
return qRed(c1) < qRed(c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool greenLessThan(const QRgb &c1, const QRgb &c2)
|
||||||
|
{
|
||||||
|
return qGreen(c1) < qGreen(c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool blueLessThan(const QRgb &c1, const QRgb &c2)
|
||||||
|
{
|
||||||
|
return qBlue(c1) < qBlue(c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
//median cut algoritmh
|
||||||
|
void ImageUtil::quantization(const QImage &img, QVector<QRgb> &palette)
|
||||||
|
{
|
||||||
|
int bits = 4; // bits/pixel
|
||||||
|
int samplesize = 100000; //only take this many color samples
|
||||||
|
|
||||||
|
RsScopeTimer st("Quantization");
|
||||||
|
QSet<QRgb> colors;
|
||||||
|
|
||||||
|
//collect color information
|
||||||
|
int imgsize = img.width()*img.height();
|
||||||
|
int width = img.width();
|
||||||
|
samplesize = qMin(samplesize, imgsize);
|
||||||
|
double sampledist = (double)imgsize / (double)samplesize;
|
||||||
|
for (double i = 0; i < imgsize; i += sampledist) {
|
||||||
|
QRgb pixel = img.pixel((int)i % width, (int)i / width);
|
||||||
|
colors.insert(pixel);
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QRgb> colorlist = colors.toList();
|
||||||
|
//don't do the algoritmh if we have less than 16 different colors
|
||||||
|
if(colorlist.size() <= (1 << bits)) {
|
||||||
|
for(int i = 0; i < colors.count(); ++i)
|
||||||
|
palette.append(colorlist[i]);
|
||||||
|
} else {
|
||||||
|
quantization(colorlist.begin(), colorlist.end(), bits, palette);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageUtil::quantization(QList<QRgb>::iterator begin, QList<QRgb>::iterator end, int depth, QVector<QRgb> &palette)
|
||||||
|
{
|
||||||
|
//the buckets are ready
|
||||||
|
if(depth == 0) {
|
||||||
|
avgbucket(begin, end, palette);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//nothing to do
|
||||||
|
int count = end - begin;
|
||||||
|
if(count == 1) {
|
||||||
|
palette.append(*begin);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//widest color channel
|
||||||
|
int rl = 255;
|
||||||
|
int gl = 255;
|
||||||
|
int bl = 255;
|
||||||
|
int rh = 0;
|
||||||
|
int gh = 0;
|
||||||
|
int bh = 0;
|
||||||
|
for(QList<QRgb>::iterator it = begin; it < end; ++it) {
|
||||||
|
rl = qMin(rl, qRed(*it));
|
||||||
|
gl = qMin(gl, qGreen(*it));
|
||||||
|
bl = qMin(bl, qBlue(*it));
|
||||||
|
rh = qMax(rh, qRed(*it));
|
||||||
|
gh = qMax(gh, qGreen(*it));
|
||||||
|
bh = qMax(bh, qBlue(*it));
|
||||||
|
}
|
||||||
|
int red = rh - rl;
|
||||||
|
int green = gh - gl;
|
||||||
|
int blue = bh - bl;
|
||||||
|
|
||||||
|
//order by the widest channel
|
||||||
|
if(red > green)
|
||||||
|
if(red > blue)
|
||||||
|
qSort(begin, end, redLessThan);
|
||||||
|
else
|
||||||
|
qSort(begin, end, blueLessThan);
|
||||||
|
else
|
||||||
|
if(green > blue)
|
||||||
|
qSort(begin, end, greenLessThan);
|
||||||
|
else
|
||||||
|
qSort(begin, end, blueLessThan);
|
||||||
|
|
||||||
|
//split into two buckets
|
||||||
|
QList<QRgb>::iterator split = begin + count / 2;
|
||||||
|
quantization(begin, split, depth - 1, palette);
|
||||||
|
quantization(split, end, depth - 1, palette);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageUtil::avgbucket(QList<QRgb>::iterator begin, QList<QRgb>::iterator end, QVector<QRgb> &palette)
|
||||||
|
{
|
||||||
|
int red = 0;
|
||||||
|
int green = 0;
|
||||||
|
int blue = 0;
|
||||||
|
int count = end - begin;
|
||||||
|
|
||||||
|
for(QList<QRgb>::iterator it = begin; it < end; ++it) {
|
||||||
|
red += qRed(*it);
|
||||||
|
green += qGreen(*it);
|
||||||
|
blue += qBlue(*it);
|
||||||
|
}
|
||||||
|
|
||||||
|
QRgb color = qRgb(red/count, green/count, blue/count);
|
||||||
|
palette.append(color);
|
||||||
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <QTextCursor>
|
#include <QTextCursor>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
#include <qiterator.h>
|
||||||
|
|
||||||
class ImageUtil
|
class ImageUtil
|
||||||
{
|
{
|
||||||
@ -10,6 +11,13 @@ public:
|
|||||||
ImageUtil();
|
ImageUtil();
|
||||||
|
|
||||||
static void extractImage(QWidget *window, QTextCursor cursor);
|
static void extractImage(QWidget *window, QTextCursor cursor);
|
||||||
|
static bool optimizeSize(QString &html, const QImage& original, QImage &optimized, int maxPixels = -1, int maxBytes = -1);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static int checkSize(QString& embeddedImage, const QImage& img, int maxBytes = -1);
|
||||||
|
static void quantization(const QImage& img, QVector<QRgb>& palette);
|
||||||
|
static void quantization(QList<QRgb>::iterator begin, QList<QRgb>::iterator end, int depth, QVector<QRgb>& palette);
|
||||||
|
static void avgbucket(QList<QRgb>::iterator begin, QList<QRgb>::iterator end, QVector<QRgb>& palette);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // IMAGEUTIL_H
|
#endif // IMAGEUTIL_H
|
||||||
|
Loading…
x
Reference in New Issue
Block a user