From e193a86eaafce8b20a1790f106155f1b25ea093e Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 15 Jul 2015 14:23:59 +0300 Subject: [PATCH] fixed Qt 5.5 image scale crash, fixed empty photo in mediaview crash --- Telegram/Resources/lang.strings | 3 +- Telegram/SourceFiles/history.cpp | 89 ++- Telegram/SourceFiles/history.h | 2 +- Telegram/SourceFiles/mediaview.cpp | 90 ++- Telegram/_qt_5_5_0_patch.diff | 16 + .../qtbase/src/gui/painting/qimagescale.cpp | 748 ++++++++++++++++++ 6 files changed, 895 insertions(+), 53 deletions(-) create mode 100644 Telegram/_qt_5_5_0_patch/qtbase/src/gui/painting/qimagescale.cpp diff --git a/Telegram/Resources/lang.strings b/Telegram/Resources/lang.strings index 9ac49a605..2a1e9f8fc 100644 --- a/Telegram/Resources/lang.strings +++ b/Telegram/Resources/lang.strings @@ -385,7 +385,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org "lng_sure_delete_and_exit" = "Are you sure, you want to delete all message history and leave «{group}»?\n\nThis action cannot be undone."; -"lng_message_empty" = "(empty)"; +"lng_message_empty" = "Empty Message"; +"lng_media_unsupported" = "Media Unsupported"; "lng_action_add_user" = "{from} added {user}"; "lng_action_kick_user" = "{from} kicked {user}"; diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 8b5a23d89..09d8c204a 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -622,18 +622,79 @@ HistoryItem *History::createItem(HistoryBlock *block, const MTPmessage &msg, boo result = new HistoryServiceMsg(this, block, msg.c_messageEmpty().vid.v, date(), lang(lng_message_empty)); break; - case mtpc_message: - if ((msg.c_message().has_fwd_date() && msg.c_message().vfwd_date.v > 0) || (msg.c_message().has_fwd_from_id() && msg.c_message().vfwd_from_id.v != 0)) { - result = new HistoryForwarded(this, block, msg.c_message()); - } else if (msg.c_message().has_reply_to_msg_id() && msg.c_message().vreply_to_msg_id.v > 0) { - result = new HistoryReply(this, block, msg.c_message()); + case mtpc_message: { + const MTPDmessage m(msg.c_message()); + int badMedia = 0; // 1 - unsupported, 2 - empty + switch (m.vmedia.type()) { + case mtpc_messageMediaEmpty: break; + case mtpc_messageMediaGeo: + switch (m.vmedia.c_messageMediaGeo().vgeo.type()) { + case mtpc_geoPoint: break; + case mtpc_geoPointEmpty: badMedia = 2; break; + default: badMedia = 1; break; + } + break; + case mtpc_messageMediaVenue: + switch (m.vmedia.c_messageMediaVenue().vgeo.type()) { + case mtpc_geoPoint: break; + case mtpc_geoPointEmpty: badMedia = 2; break; + default: badMedia = 1; break; + } + break; + case mtpc_messageMediaPhoto: + switch (m.vmedia.c_messageMediaPhoto().vphoto.type()) { + case mtpc_photo: break; + case mtpc_photoEmpty: badMedia = 2; break; + default: badMedia = 1; break; + } + break; + case mtpc_messageMediaVideo: + switch (m.vmedia.c_messageMediaVideo().vvideo.type()) { + case mtpc_video: break; + case mtpc_videoEmpty: badMedia = 2; break; + default: badMedia = 1; break; + } + break; + case mtpc_messageMediaAudio: + switch (m.vmedia.c_messageMediaAudio().vaudio.type()) { + case mtpc_audio: break; + case mtpc_audioEmpty: badMedia = 2; break; + default: badMedia = 1; break; + } + break; + case mtpc_messageMediaDocument: + switch (m.vmedia.c_messageMediaDocument().vdocument.type()) { + case mtpc_document: break; + case mtpc_documentEmpty: badMedia = 2; break; + default: badMedia = 1; break; + } + break; + case mtpc_messageMediaWebPage: + switch (m.vmedia.c_messageMediaWebPage().vwebpage.type()) { + case mtpc_webPage: + case mtpc_webPageEmpty: + case mtpc_webPagePending: break; + default: badMedia = 1; break; + } + break; + case mtpc_messageMediaUnsupported: + default: badMedia = 1; break; + } + if (badMedia) { + result = new HistoryServiceMsg(this, block, m.vid.v, date(m.vdate), lang((badMedia == 2) ? lng_message_empty : lng_media_unsupported), m.vflags.v, 0, m.vfrom_id.v); } else { - result = new HistoryMessage(this, block, msg.c_message()); + if ((m.has_fwd_date() && m.vfwd_date.v > 0) || (m.has_fwd_from_id() && m.vfwd_from_id.v != 0)) { + result = new HistoryForwarded(this, block, m); + } else if (m.has_reply_to_msg_id() && m.vreply_to_msg_id.v > 0) { + result = new HistoryReply(this, block, m); + } else { + result = new HistoryMessage(this, block, m); + } + if (m.has_reply_markup()) { + App::feedReplyMarkup(msgId, m.vreply_markup); + } } - if (msg.c_message().has_reply_markup()) { - App::feedReplyMarkup(msgId, msg.c_message().vreply_markup); - } - break; + } break; case mtpc_messageService: { const MTPDmessageService &d(msg.c_messageService()); @@ -4938,7 +4999,6 @@ void HistoryMessage::initTime() { void HistoryMessage::initMedia(const MTPMessageMedia &media, QString ¤tText) { switch (media.type()) { - case mtpc_messageMediaEmpty: initMediaFromText(currentText); break; case mtpc_messageMediaContact: { const MTPDmessageMediaContact &d(media.c_messageMediaContact()); _media = new HistoryContact(d.vuser_id.v, qs(d.vfirst_name), qs(d.vlast_name), qs(d.vphone_number)); @@ -4995,8 +5055,7 @@ void HistoryMessage::initMedia(const MTPMessageMedia &media, QString ¤tTex } break; } } break; - case mtpc_messageMediaUnsupported: - default: currentText += " (unsupported media)"; break; + default: initMediaFromText(currentText); break; }; if (_media) _media->regItem(this); } @@ -6020,8 +6079,8 @@ HistoryServiceMsg::HistoryServiceMsg(History *history, HistoryBlock *block, cons setMessageByAction(msg.vaction); } -HistoryServiceMsg::HistoryServiceMsg(History *history, HistoryBlock *block, MsgId msgId, QDateTime date, const QString &msg, int32 flags, HistoryMedia *media) : - HistoryItem(history, block, msgId, flags, date, 0) +HistoryServiceMsg::HistoryServiceMsg(History *history, HistoryBlock *block, MsgId msgId, QDateTime date, const QString &msg, int32 flags, HistoryMedia *media, int32 from) : + HistoryItem(history, block, msgId, flags, date, from) , _text(st::msgServiceFont, msg, _historySrvOptions, st::dlgMinWidth) , _media(media) { diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 50888bdb9..ae6c04193 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -1439,7 +1439,7 @@ class HistoryServiceMsg : public HistoryItem { public: HistoryServiceMsg(History *history, HistoryBlock *block, const MTPDmessageService &msg); - HistoryServiceMsg(History *history, HistoryBlock *block, MsgId msgId, QDateTime date, const QString &msg, int32 flags = 0, HistoryMedia *media = 0); + HistoryServiceMsg(History *history, HistoryBlock *block, MsgId msgId, QDateTime date, const QString &msg, int32 flags = 0, HistoryMedia *media = 0, int32 from = 0); void initDimensions(const HistoryItem *parent = 0); diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 2036a644f..d49a10105 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -225,8 +225,6 @@ void MediaView::updateDocSize() { } void MediaView::updateControls() { - if (!_photo && !_doc) return; - if (_doc && _current.isNull() && _currentGif.isNull()) { if (_doc->loader) { _docDownload.hide(); @@ -257,13 +255,20 @@ void MediaView::updateControls() { _docCancel.hide(); } - _saveVisible = ((_photo && _photo->full->loaded()) || (_doc && (!_doc->already(true).isEmpty() || (_current.isNull() && _currentGif.isNull())))); + _saveVisible = ((_photo && _photo->full->loaded()) || (_doc && (!_doc->already(true).isEmpty() || (_current.isNull() && _currentGif.isNull() && (_photo || _doc))))); _saveNav = myrtlrect(width() - st::mvIconSize.width() * 2, height() - st::mvIconSize.height(), st::mvIconSize.width(), st::mvIconSize.height()); _saveNavIcon = centersprite(_saveNav, st::mvSave); _moreNav = myrtlrect(width() - st::mvIconSize.width(), height() - st::mvIconSize.height(), st::mvIconSize.width(), st::mvIconSize.height()); _moreNavIcon = centersprite(_moreNav, st::mvMore); - QDateTime d(date(_photo ? _photo->date : _doc->date)), dNow(date(unixtime())); + QDateTime d, dNow(date(unixtime())); + if (_photo) { + d = date(_photo->date); + } else if (_doc) { + d = date(_doc->date); + } else if (HistoryItem *item = App::histItemById(_msgid)) { + d = item->date; + } if (d.date() == dNow.date()) { _dateText = lng_mediaview_today(lt_time, d.time().toString(cTimeFormat())); } else if (d.date().addDays(1) == dNow.date()) { @@ -280,12 +285,12 @@ void MediaView::updateControls() { _dateNav = myrtlrect(st::mvTextLeft, height() - st::mvTextTop, st::mvFont->m.width(_dateText), st::mvFont->height); } updateHeader(); - if (_photo) { + if (_photo || (_history && _overview == OverviewPhotos)) { _leftNavVisible = (_index > 0) || (_index == 0 && _history && _history->_overview[_overview].size() < _history->_overviewCount[_overview]); _rightNavVisible = (_index >= 0) && ( (_history && _index + 1 < _history->_overview[_overview].size()) || (_user && (_index + 1 < _user->photos.size() || _index + 1 < _user->photosCount))); - } else if (_doc) { + } else if (_history && _overview == OverviewDocuments) { _leftNavVisible = (_index > 0) || (_index == 0 && _history && _history->_overview[_overview].size() < _history->_overviewCount[_overview]); _rightNavVisible = (_index >= 0) && _history && (_index + 1 < _history->_overview[_overview].size()); } else { @@ -792,24 +797,30 @@ void MediaView::displayPhoto(PhotoData *photo, HistoryItem *item) { } } -void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { +void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { // empty messages shown as docs: doc can be NULL _doc = doc; + _photo = 0; _caption = Text(); - QString already = _doc->already(true); - if (_doc->sticker() && !_doc->sticker()->img->isNull() && _doc->sticker()->img->loaded()) { - _currentGif.stop(); - _current = _doc->sticker()->img->pix(); - } else if (!already.isEmpty()) { - QImageReader reader(already); - if (reader.canRead()) { - if (reader.supportsAnimation() && reader.imageCount() > 1) { - _currentGif.start(0, already); - _current = QPixmap(); + if (_doc) { + QString already = _doc->already(true); + if (_doc->sticker() && !_doc->sticker()->img->isNull() && _doc->sticker()->img->loaded()) { + _currentGif.stop(); + _current = _doc->sticker()->img->pix(); + } else if (!already.isEmpty()) { + QImageReader reader(already); + if (reader.canRead()) { + if (reader.supportsAnimation() && reader.imageCount() > 1) { + _currentGif.start(0, already); + _current = QPixmap(); + } else { + _currentGif.stop(); + QPixmap pix = QPixmap::fromImage(App::readImage(already, 0, false), Qt::ColorOnly); + _current = pix; + } } else { _currentGif.stop(); - QPixmap pix = QPixmap::fromImage(App::readImage(already, 0, false), Qt::ColorOnly); - _current = pix; + _current = QPixmap(); } } else { _currentGif.stop(); @@ -821,10 +832,10 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { } if (_current.isNull() && _currentGif.isNull()) { - if (_doc->thumb->isNull()) { + if (!_doc || _doc->thumb->isNull()) { style::sprite thumbs[] = { st::mvDocBlue, st::mvDocGreen, st::mvDocRed, st::mvDocYellow }; style::color colors[] = { st::mvDocBlueColor, st::mvDocGreenColor, st::mvDocRedColor, st::mvDocYellowColor }; - QString name = _doc->name.toLower(), mime = _doc->mime.toLower(); + QString name = _doc ? _doc->name.toLower() : QString(), mime = _doc ? _doc->mime.toLower() : QString(); if (name.endsWith(qstr(".doc")) || name.endsWith(qstr(".txt")) || name.endsWith(qstr(".psd")) || @@ -879,9 +890,9 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { int32 maxw = st::mvDocSize.width() - st::mvDocBlue.pxWidth() - st::mvDocPadding * 3; - _docName = _doc->name.isEmpty() ? lang(_doc->type == StickerDocument ? lng_in_dlg_sticker : lng_mediaview_doc_image) : _doc->name; + _docName = (!_doc || _doc->name.isEmpty()) ? lang(_doc ? (_doc->type == StickerDocument ? lng_in_dlg_sticker : lng_mediaview_doc_image) : lng_message_empty) : _doc->name; int32 lastDot = _docName.lastIndexOf('.'); - _docExt = (lastDot < 0 || lastDot + 2 > _docName.size()) ? _docName : _docName.mid(lastDot + 1); + _docExt = _doc ? ((lastDot < 0 || lastDot + 2 > _docName.size()) ? _docName : _docName.mid(lastDot + 1)) : QString(); _docNameWidth = st::mvDocNameFont->m.width(_docName); if (_docNameWidth > maxw) { _docName = st::mvDocNameFont->m.elidedText(_docName, Qt::ElideMiddle, maxw); @@ -898,7 +909,7 @@ void MediaView::displayDocument(DocumentData *doc, HistoryItem *item) { _docRadialFirst = _docRadialLast = _docRadialStart = 0; - float64 prg = _doc->loader ? _doc->loader->currentProgress() : 0; + float64 prg = (_doc && _doc->loader) ? _doc->loader->currentProgress() : 0; a_docRadial = anim::fvalue(prg, qMax(prg, 0.0001)); // _docSize is updated in updateControls() @@ -1073,17 +1084,19 @@ void MediaView::paintEvent(QPaintEvent *e) { } } } - } else if (_doc) { + } else { if (_docRect.intersects(r)) { p.fillRect(_docRect, st::mvDocBg->b); if (_docIconRect.intersects(r)) { icon = true; - if (_doc->thumb->isNull()) { - if (!_doc->already().isEmpty() && (!_docRadialStart || _docRadialOpacity < 1)) { + if (!_doc || _doc->thumb->isNull()) { + if ((!_doc || !_doc->already().isEmpty()) && (!_docRadialStart || _docRadialOpacity < 1)) { p.drawPixmap(_docIconRect.topLeft(), App::sprite(), _docIcon); p.setPen(st::mvDocExtColor->p); p.setFont(st::mvDocExtFont->f); - p.drawText(_docIconRect.x() + (_docIconRect.width() - _docExtWidth) / 2, _docIconRect.y() + st::mvDocExtTop + st::mvDocExtFont->ascent, _docExt); + if (!_docExt.isEmpty()) { + p.drawText(_docIconRect.x() + (_docIconRect.width() - _docExtWidth) / 2, _docIconRect.y() + st::mvDocExtTop + st::mvDocExtFont->ascent, _docExt); + } } else { p.fillRect(_docIconRect, _docIconColor->b); } @@ -1093,7 +1106,7 @@ void MediaView::paintEvent(QPaintEvent *e) { } float64 o = overLevel(OverIcon); - if (_docRadialStart > 0) { + if (_doc && _docRadialStart > 0) { if (_doc->already().isEmpty() && _docRadialOpacity < 1) { p.setOpacity((o * 1. + (1 - o) * st::radialDownloadOpacity) * (1 - _docRadialOpacity)); p.drawSpriteCenter(_docIconRect, st::radialDownload); @@ -1120,7 +1133,7 @@ void MediaView::paintEvent(QPaintEvent *e) { p.setOpacity(1); p.setRenderHint(QPainter::HighQualityAntialiasing, false); - } else if (_doc->already().isEmpty()) { + } else if (_doc && _doc->already().isEmpty()) { p.setOpacity((o * 1. + (1 - o) * st::radialDownloadOpacity)); p.drawSpriteCenter(_docIconRect, st::radialDownload); } @@ -1190,7 +1203,7 @@ void MediaView::paintEvent(QPaintEvent *e) { } // save button - if (_saveNavIcon.intersects(r)) { + if (_saveVisible && _saveNavIcon.intersects(r)) { float64 o = overLevel(OverSave); p.setOpacity((o * st::mvIconOverOpacity + (1 - o) * st::mvIconOpacity) * co); p.drawPixmap(_saveNavIcon.topLeft(), App::sprite(), st::mvSave); @@ -1364,7 +1377,7 @@ void MediaView::keyPressEvent(QKeyEvent *e) { } void MediaView::moveToNext(int32 delta) { - if (_index < 0 || (!_photo && !_doc) || (_overview == OverviewCount && !_user)) return; + if (_index < 0 || (_history && _overview != OverviewPhotos && _overview != OverviewDocuments) || (_overview == OverviewCount && !_user)) return; int32 newIndex = _index + delta; if (_history && _overview != OverviewCount) { @@ -1372,10 +1385,15 @@ void MediaView::moveToNext(int32 delta) { _index = newIndex; if (HistoryItem *item = App::histItemById(_history->_overview[_overview][_index])) { _msgid = item->id; - switch (item->getMedia()->type()) { - case MediaTypePhoto: displayPhoto(static_cast(item->getMedia())->photo(), item); preloadData(delta); break; - case MediaTypeDocument: displayDocument(static_cast(item->getMedia())->document(), item); preloadData(delta); break; - case MediaTypeSticker: displayDocument(static_cast(item->getMedia())->document(), item); preloadData(delta); break; + if (item->getMedia()) { + switch (item->getMedia()->type()) { + case MediaTypePhoto: displayPhoto(static_cast(item->getMedia())->photo(), item); preloadData(delta); break; + case MediaTypeDocument: displayDocument(static_cast(item->getMedia())->document(), item); preloadData(delta); break; + case MediaTypeSticker: displayDocument(static_cast(item->getMedia())->document(), item); preloadData(delta); break; + } + } else { + displayDocument(0, item); + preloadData(delta); } } } diff --git a/Telegram/_qt_5_5_0_patch.diff b/Telegram/_qt_5_5_0_patch.diff index edce3fe02..ea73098ad 100644 --- a/Telegram/_qt_5_5_0_patch.diff +++ b/Telegram/_qt_5_5_0_patch.diff @@ -126,6 +126,22 @@ index 8b2b988..2d1cdd9 100644 virtual void setFilter() = 0; virtual void selectNameFilter(const QString &filter) = 0; virtual QString selectedNameFilter() const = 0; +diff --git a/qtbase/src/gui/painting/qimagescale.cpp b/qtbase/src/gui/painting/qimagescale.cpp +index 9b4eabc..a3e0e04 100644 +--- a/qtbase/src/gui/painting/qimagescale.cpp ++++ b/qtbase/src/gui/painting/qimagescale.cpp +@@ -313,7 +313,10 @@ static void qt_qimageScaleAARGBA_up_xy(QImageScaleInfo *isi, unsigned int *dest, + for (int x = dxx; x < end; x++) { + const unsigned int *pix = sptr + xpoints[x]; + const int xap = xapoints[x]; +- *dptr = INTERPOLATE_PIXEL_256(pix[0], 256 - xap, pix[1], xap); ++ if (xap > 0) ++ *dptr = INTERPOLATE_PIXEL_256(pix[0], 256 - xap, pix[1], xap); ++ else ++ *dptr = pix[0]; + dptr++; + } + } diff --git a/qtbase/src/gui/painting/qpaintengine_p.h b/qtbase/src/gui/painting/qpaintengine_p.h index c58662e..468d671 100644 --- a/qtbase/src/gui/painting/qpaintengine_p.h diff --git a/Telegram/_qt_5_5_0_patch/qtbase/src/gui/painting/qimagescale.cpp b/Telegram/_qt_5_5_0_patch/qtbase/src/gui/painting/qimagescale.cpp new file mode 100644 index 000000000..a3e0e04c9 --- /dev/null +++ b/Telegram/_qt_5_5_0_patch/qtbase/src/gui/painting/qimagescale.cpp @@ -0,0 +1,748 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include + +#include "qimage.h" +#include "qcolor.h" + +QT_BEGIN_NAMESPACE + +/* + * Copyright (C) 2004, 2005 Daniel M. Duley + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* OTHER CREDITS: + * + * This is the normal smoothscale method, based on Imlib2's smoothscale. + * + * Originally I took the algorithm used in NetPBM and Qt and added MMX/3dnow + * optimizations. It ran in about 1/2 the time as Qt. Then I ported Imlib's + * C algorithm and it ran at about the same speed as my MMX optimized one... + * Finally I ported Imlib's MMX version and it ran in less than half the + * time as my MMX algorithm, (taking only a quarter of the time Qt does). + * After further optimization it seems to run at around 1/6th. + * + * Changes include formatting, namespaces and other C++'ings, removal of old + * #ifdef'ed code, and removal of unneeded border calculation code. + * + * Imlib2 is (C) Carsten Haitzler and various contributors. The MMX code + * is by Willem Monsuwe . All other modifications are + * (C) Daniel M. Duley. + */ + + +namespace QImageScale { + const unsigned int** qimageCalcYPoints(const unsigned int *src, int sw, int sh, int dh); + int* qimageCalcXPoints(int sw, int dw); + int* qimageCalcApoints(int s, int d, int up); + QImageScaleInfo* qimageFreeScaleInfo(QImageScaleInfo *isi); + QImageScaleInfo *qimageCalcScaleInfo(const QImage &img, int sw, int sh, + int dw, int dh, char aa); +} + +using namespace QImageScale; + +// +// Code ported from Imlib... +// + +const unsigned int** QImageScale::qimageCalcYPoints(const unsigned int *src, + int sw, int sh, int dh) +{ + const unsigned int **p; + int j = 0, rv = 0; + qint64 val, inc; + + if(dh < 0){ + dh = -dh; + rv = 1; + } + p = new const unsigned int* [dh+1]; + + int up = qAbs(dh) >= sh; + val = up ? 0x8000 * sh / dh - 0x8000 : 0; + inc = (((qint64)sh) << 16) / dh; + for (int i = 0; i < dh; i++) { + p[j++] = src + qMax(0LL, val >> 16) * sw; + val += inc; + } + if (rv) { + for (int i = dh / 2; --i >= 0; ) { + const unsigned int *tmp = p[i]; + p[i] = p[dh - i - 1]; + p[dh - i - 1] = tmp; + } + } + return(p); +} + +int* QImageScale::qimageCalcXPoints(int sw, int dw) +{ + int *p, j = 0, rv = 0; + qint64 val, inc; + + if(dw < 0){ + dw = -dw; + rv = 1; + } + p = new int[dw+1]; + + int up = qAbs(dw) >= sw; + val = up ? 0x8000 * sw / dw - 0x8000 : 0; + inc = (((qint64)sw) << 16) / dw; + for (int i = 0; i < dw; i++) { + p[j++] = qMax(0LL, val >> 16); + val += inc; + } + + if (rv) { + for (int i = dw / 2; --i >= 0; ) { + int tmp = p[i]; + p[i] = p[dw - i - 1]; + p[dw - i - 1] = tmp; + } + } + return(p); +} + +int* QImageScale::qimageCalcApoints(int s, int d, int up) +{ + int *p, j = 0, rv = 0; + + if(d < 0){ + rv = 1; + d = -d; + } + p = new int[d]; + + /* scaling up */ + if(up){ + qint64 val, inc; + + val = 0x8000 * s / d - 0x8000; + inc = (((qint64)s) << 16) / d; + for (int i = 0; i < d; i++) { + int pos = val >> 16; + if (pos < 0) + p[j++] = 0; + else if (pos >= (s - 1)) + p[j++] = 0; + else + p[j++] = (val >> 8) - ((val >> 8) & 0xffffff00); + val += inc; + } + } + /* scaling down */ + else { + qint64 val = 0; + qint64 inc = (((qint64)s) << 16) / d; + int Cp = (((d << 14) + s - 1) / s); + for (int i = 0; i < d; i++) { + int ap = ((0x10000 - (val & 0xffff)) * Cp) >> 16; + p[j] = ap | (Cp << 16); + j++; + val += inc; + } + } + if(rv){ + int tmp; + for (int i = d / 2; --i >= 0; ) { + tmp = p[i]; + p[i] = p[d - i - 1]; + p[d - i - 1] = tmp; + } + } + return p; +} + +QImageScaleInfo* QImageScale::qimageFreeScaleInfo(QImageScaleInfo *isi) +{ + if(isi){ + delete[] isi->xpoints; + delete[] isi->ypoints; + delete[] isi->xapoints; + delete[] isi->yapoints; + delete isi; + } + return 0; +} + +QImageScaleInfo* QImageScale::qimageCalcScaleInfo(const QImage &img, + int sw, int sh, + int dw, int dh, char aa) +{ + QImageScaleInfo *isi; + int scw, sch; + + scw = dw * qlonglong(img.width()) / sw; + sch = dh * qlonglong(img.height()) / sh; + + isi = new QImageScaleInfo; + if(!isi) + return 0; + memset(isi, 0, sizeof(QImageScaleInfo)); + + isi->xup_yup = (qAbs(dw) >= sw) + ((qAbs(dh) >= sh) << 1); + + isi->xpoints = qimageCalcXPoints(img.width(), scw); + if(!isi->xpoints) + return(qimageFreeScaleInfo(isi)); + isi->ypoints = qimageCalcYPoints((const unsigned int *)img.scanLine(0), + img.bytesPerLine() / 4, img.height(), sch); + if (!isi->ypoints) + return(qimageFreeScaleInfo(isi)); + if(aa) { + isi->xapoints = qimageCalcApoints(img.width(), scw, isi->xup_yup & 1); + if(!isi->xapoints) + return(qimageFreeScaleInfo(isi)); + isi->yapoints = qimageCalcApoints(img.height(), sch, isi->xup_yup & 2); + if(!isi->yapoints) + return(qimageFreeScaleInfo(isi)); + } + return(isi); +} + + +static void qt_qimageScaleAARGBA_up_x_down_y(QImageScaleInfo *isi, unsigned int *dest, + int dxx, int dyy, int dx, int dy, + int dw, int dh, int dow, int sow); + +static void qt_qimageScaleAARGBA_down_x_up_y(QImageScaleInfo *isi, unsigned int *dest, + int dxx, int dyy, int dx, int dy, + int dw, int dh, int dow, int sow); + +static void qt_qimageScaleAARGBA_down_xy(QImageScaleInfo *isi, unsigned int *dest, + int dxx, int dyy, int dx, int dy, int dw, + int dh, int dow, int sow); + +#if defined(QT_COMPILER_SUPPORTS_SSE4_1) +template +void qt_qimageScaleAARGBA_up_x_down_y_sse4(QImageScaleInfo *isi, unsigned int *dest, + int dxx, int dyy, int dx, int dy, + int dw, int dh, int dow, int sow); +template +void qt_qimageScaleAARGBA_down_x_up_y_sse4(QImageScaleInfo *isi, unsigned int *dest, + int dxx, int dyy, int dx, int dy, + int dw, int dh, int dow, int sow); +template +void qt_qimageScaleAARGBA_down_xy_sse4(QImageScaleInfo *isi, unsigned int *dest, + int dxx, int dyy, int dx, int dy, + int dw, int dh, int dow, int sow); +#endif + +static void qt_qimageScaleAARGBA_up_xy(QImageScaleInfo *isi, unsigned int *dest, + int dxx, int dyy, int dx, int dy, + int dw, int dh, int dow, int sow) +{ + const unsigned int **ypoints = isi->ypoints; + int *xpoints = isi->xpoints; + int *xapoints = isi->xapoints; + int *yapoints = isi->yapoints; + + int end = dxx + dw; + /* go through every scanline in the output buffer */ + for (int y = 0; y < dh; y++) { + /* calculate the source line we'll scan from */ + const unsigned int *sptr = ypoints[dyy + y]; + unsigned int *dptr = dest + dx + ((y + dy) * dow); + const int yap = yapoints[dyy + y]; + if (yap > 0) { + for (int x = dxx; x < end; x++) { + const unsigned int *pix = sptr + xpoints[x]; + const int xap = xapoints[x]; + if (xap > 0) + *dptr = interpolate_4_pixels(pix[0], pix[1], pix[sow], pix[sow + 1], xap, yap); + else + *dptr = INTERPOLATE_PIXEL_256(pix[0], 256 - yap, pix[sow], yap); + dptr++; + } + } else { + for (int x = dxx; x < end; x++) { + const unsigned int *pix = sptr + xpoints[x]; + const int xap = xapoints[x]; + if (xap > 0) + *dptr = INTERPOLATE_PIXEL_256(pix[0], 256 - xap, pix[1], xap); + else + *dptr = pix[0]; + dptr++; + } + } + } +} + +/* scale by area sampling */ +static void qt_qimageScaleAARGBA(QImageScaleInfo *isi, unsigned int *dest, + int dxx, int dyy, int dx, int dy, int dw, + int dh, int dow, int sow) +{ + /* scaling up both ways */ + if (isi->xup_yup == 3){ + qt_qimageScaleAARGBA_up_xy(isi, dest, dxx, dyy, dx, dy, dw, dh, dow, sow); + } + /* if we're scaling down vertically */ + else if (isi->xup_yup == 1) { +#ifdef QT_COMPILER_SUPPORTS_SSE4_1 + if (qCpuHasFeature(SSE4_1)) + qt_qimageScaleAARGBA_up_x_down_y_sse4(isi, dest, dxx, dyy, dx, dy, dw, dh, dow, sow); + else +#endif + qt_qimageScaleAARGBA_up_x_down_y(isi, dest, dxx, dyy, dx, dy, dw, dh, dow, sow); + } + /* if we're scaling down horizontally */ + else if (isi->xup_yup == 2) { +#ifdef QT_COMPILER_SUPPORTS_SSE4_1 + if (qCpuHasFeature(SSE4_1)) + qt_qimageScaleAARGBA_down_x_up_y_sse4(isi, dest, dxx, dyy, dx, dy, dw, dh, dow, sow); + else +#endif + qt_qimageScaleAARGBA_down_x_up_y(isi, dest, dxx, dyy, dx, dy, dw, dh, dow, sow); + } + /* if we're scaling down horizontally & vertically */ + else { +#ifdef QT_COMPILER_SUPPORTS_SSE4_1 + if (qCpuHasFeature(SSE4_1)) + qt_qimageScaleAARGBA_down_xy_sse4(isi, dest, dxx, dyy, dx, dy, dw, dh, dow, sow); + else +#endif + qt_qimageScaleAARGBA_down_xy(isi, dest, dxx, dyy, dx, dy, dw, dh, dow, sow); + } +} + +inline static void qt_qimageScaleAARGBA_helper(const unsigned int *pix, int xyap, int Cxy, int step, int &r, int &g, int &b, int &a) +{ + r = qRed(*pix) * xyap; + g = qGreen(*pix) * xyap; + b = qBlue(*pix) * xyap; + a = qAlpha(*pix) * xyap; + int j; + for (j = (1 << 14) - xyap; j > Cxy; j -= Cxy) { + pix += step; + r += qRed(*pix) * Cxy; + g += qGreen(*pix) * Cxy; + b += qBlue(*pix) * Cxy; + a += qAlpha(*pix) * Cxy; + } + pix += step; + r += qRed(*pix) * j; + g += qGreen(*pix) * j; + b += qBlue(*pix) * j; + a += qAlpha(*pix) * j; +} + +static void qt_qimageScaleAARGBA_up_x_down_y(QImageScaleInfo *isi, unsigned int *dest, + int dxx, int dyy, int dx, int dy, + int dw, int dh, int dow, int sow) +{ + const unsigned int **ypoints = isi->ypoints; + int *xpoints = isi->xpoints; + int *xapoints = isi->xapoints; + int *yapoints = isi->yapoints; + + int end = dxx + dw; + + /* go through every scanline in the output buffer */ + for (int y = 0; y < dh; y++) { + int Cy = (yapoints[dyy + y]) >> 16; + int yap = (yapoints[dyy + y]) & 0xffff; + + unsigned int *dptr = dest + dx + ((y + dy) * dow); + for (int x = dxx; x < end; x++) { + const unsigned int *sptr = ypoints[dyy + y] + xpoints[x]; + int r, g, b, a; + qt_qimageScaleAARGBA_helper(sptr, yap, Cy, sow, r, g, b, a); + + int xap = xapoints[x]; + if (xap > 0) { + int rr, gg, bb, aa; + qt_qimageScaleAARGBA_helper(sptr + 1, yap, Cy, sow, rr, gg, bb, aa); + + r = r * (256 - xap); + g = g * (256 - xap); + b = b * (256 - xap); + a = a * (256 - xap); + r = (r + (rr * xap)) >> 8; + g = (g + (gg * xap)) >> 8; + b = (b + (bb * xap)) >> 8; + a = (a + (aa * xap)) >> 8; + } + *dptr++ = qRgba(r >> 14, g >> 14, b >> 14, a >> 14); + } + } +} + +static void qt_qimageScaleAARGBA_down_x_up_y(QImageScaleInfo *isi, unsigned int *dest, + int dxx, int dyy, int dx, int dy, + int dw, int dh, int dow, int sow) +{ + const unsigned int **ypoints = isi->ypoints; + int *xpoints = isi->xpoints; + int *xapoints = isi->xapoints; + int *yapoints = isi->yapoints; + + int end = dxx + dw; + + /* go through every scanline in the output buffer */ + for (int y = 0; y < dh; y++) { + unsigned int *dptr = dest + dx + ((y + dy) * dow); + for (int x = dxx; x < end; x++) { + int Cx = xapoints[x] >> 16; + int xap = xapoints[x] & 0xffff; + + const unsigned int *sptr = ypoints[dyy + y] + xpoints[x]; + int r, g, b, a; + qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, r, g, b, a); + + int yap = yapoints[dyy + y]; + if (yap > 0) { + int rr, gg, bb, aa; + qt_qimageScaleAARGBA_helper(sptr + sow, xap, Cx, 1, rr, gg, bb, aa); + + r = r * (256 - yap); + g = g * (256 - yap); + b = b * (256 - yap); + a = a * (256 - yap); + r = (r + (rr * yap)) >> 8; + g = (g + (gg * yap)) >> 8; + b = (b + (bb * yap)) >> 8; + a = (a + (aa * yap)) >> 8; + } + *dptr = qRgba(r >> 14, g >> 14, b >> 14, a >> 14); + dptr++; + } + } +} + +static void qt_qimageScaleAARGBA_down_xy(QImageScaleInfo *isi, unsigned int *dest, + int dxx, int dyy, int dx, int dy, int dw, + int dh, int dow, int sow) +{ + const unsigned int **ypoints = isi->ypoints; + int *xpoints = isi->xpoints; + int *xapoints = isi->xapoints; + int *yapoints = isi->yapoints; + + int end = dxx + dw; + + for (int y = 0; y < dh; y++) { + int Cy = (yapoints[dyy + y]) >> 16; + int yap = (yapoints[dyy + y]) & 0xffff; + + unsigned int *dptr = dest + dx + ((y + dy) * dow); + for (int x = dxx; x < end; x++) { + int Cx = xapoints[x] >> 16; + int xap = xapoints[x] & 0xffff; + + const unsigned int *sptr = ypoints[dyy + y] + xpoints[x]; + int rx, gx, bx, ax; + qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); + + int r = ((rx>>4) * yap); + int g = ((gx>>4) * yap); + int b = ((bx>>4) * yap); + int a = ((ax>>4) * yap); + + int j; + for (j = (1 << 14) - yap; j > Cy; j -= Cy) { + sptr += sow; + qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); + r += ((rx>>4) * Cy); + g += ((gx>>4) * Cy); + b += ((bx>>4) * Cy); + a += ((ax>>4) * Cy); + } + sptr += sow; + qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); + + r += ((rx>>4) * j); + g += ((gx>>4) * j); + b += ((bx>>4) * j); + a += ((ax>>4) * j); + + *dptr = qRgba(r >> 24, g >> 24, b >> 24, a >> 24); + dptr++; + } + } +} + +static void qt_qimageScaleAARGB_up_x_down_y(QImageScaleInfo *isi, unsigned int *dest, + int dxx, int dyy, int dx, int dy, int dw, + int dh, int dow, int sow); + +static void qt_qimageScaleAARGB_down_x_up_y(QImageScaleInfo *isi, unsigned int *dest, + int dxx, int dyy, int dx, int dy, int dw, + int dh, int dow, int sow); + +static void qt_qimageScaleAARGB_down_xy(QImageScaleInfo *isi, unsigned int *dest, + int dxx, int dyy, int dx, int dy, int dw, + int dh, int dow, int sow); + +/* scale by area sampling - IGNORE the ALPHA byte*/ +static void qt_qimageScaleAARGB(QImageScaleInfo *isi, unsigned int *dest, + int dxx, int dyy, int dx, int dy, + int dw, int dh, int dow, int sow) +{ + /* scaling up both ways */ + if (isi->xup_yup == 3) { + qt_qimageScaleAARGBA_up_xy(isi, dest, dxx, dyy, dx, dy, dw, dh, dow, sow); + } + /* if we're scaling down vertically */ + else if (isi->xup_yup == 1) { +#ifdef QT_COMPILER_SUPPORTS_SSE4_1 + if (qCpuHasFeature(SSE4_1)) + qt_qimageScaleAARGBA_up_x_down_y_sse4(isi, dest, dxx, dyy, dx, dy, dw, dh, dow, sow); + else +#endif + qt_qimageScaleAARGB_up_x_down_y(isi, dest, dxx, dyy, dx, dy, dw, dh, dow, sow); + } + /* if we're scaling down horizontally */ + else if (isi->xup_yup == 2) { +#ifdef QT_COMPILER_SUPPORTS_SSE4_1 + if (qCpuHasFeature(SSE4_1)) + qt_qimageScaleAARGBA_down_x_up_y_sse4(isi, dest, dxx, dyy, dx, dy, dw, dh, dow, sow); + else +#endif + qt_qimageScaleAARGB_down_x_up_y(isi, dest, dxx, dyy, dx, dy, dw, dh, dow, sow); + } + /* if we're scaling down horizontally & vertically */ + else { +#ifdef QT_COMPILER_SUPPORTS_SSE4_1 + if (qCpuHasFeature(SSE4_1)) + qt_qimageScaleAARGBA_down_xy_sse4(isi, dest, dxx, dyy, dx, dy, dw, dh, dow, sow); + else +#endif + qt_qimageScaleAARGB_down_xy(isi, dest, dxx, dyy, dx, dy, dw, dh, dow, sow); + } +} + + +inline static void qt_qimageScaleAARGB_helper(const unsigned int *pix, int xyap, int Cxy, int step, int &r, int &g, int &b) +{ + r = qRed(*pix) * xyap; + g = qGreen(*pix) * xyap; + b = qBlue(*pix) * xyap; + int j; + for (j = (1 << 14) - xyap; j > Cxy; j -= Cxy) { + pix += step; + r += qRed(*pix) * Cxy; + g += qGreen(*pix) * Cxy; + b += qBlue(*pix) * Cxy; + } + pix += step; + r += qRed(*pix) * j; + g += qGreen(*pix) * j; + b += qBlue(*pix) * j; +} + +static void qt_qimageScaleAARGB_up_x_down_y(QImageScaleInfo *isi, unsigned int *dest, + int dxx, int dyy, int dx, int dy, int dw, + int dh, int dow, int sow) +{ + const unsigned int **ypoints = isi->ypoints; + int *xpoints = isi->xpoints; + int *xapoints = isi->xapoints; + int *yapoints = isi->yapoints; + + int end = dxx + dw; + + /* go through every scanline in the output buffer */ + for (int y = 0; y < dh; y++) { + int Cy = (yapoints[dyy + y]) >> 16; + int yap = (yapoints[dyy + y]) & 0xffff; + + unsigned int *dptr = dest + dx + ((y + dy) * dow); + for (int x = dxx; x < end; x++) { + const unsigned int *sptr = ypoints[dyy + y] + xpoints[x]; + int r, g, b; + qt_qimageScaleAARGB_helper(sptr, yap, Cy, sow, r, g, b); + + int xap = xapoints[x]; + if (xap > 0) { + int rr, bb, gg; + qt_qimageScaleAARGB_helper(sptr + 1, yap, Cy, sow, rr, gg, bb); + + r = r * (256 - xap); + g = g * (256 - xap); + b = b * (256 - xap); + r = (r + (rr * xap)) >> 8; + g = (g + (gg * xap)) >> 8; + b = (b + (bb * xap)) >> 8; + } + *dptr++ = qRgb(r >> 14, g >> 14, b >> 14); + } + } +} + +static void qt_qimageScaleAARGB_down_x_up_y(QImageScaleInfo *isi, unsigned int *dest, + int dxx, int dyy, int dx, int dy, int dw, + int dh, int dow, int sow) +{ + const unsigned int **ypoints = isi->ypoints; + int *xpoints = isi->xpoints; + int *xapoints = isi->xapoints; + int *yapoints = isi->yapoints; + + int end = dxx + dw; + + /* go through every scanline in the output buffer */ + for (int y = 0; y < dh; y++) { + unsigned int *dptr = dest + dx + ((y + dy) * dow); + for (int x = dxx; x < end; x++) { + int Cx = xapoints[x] >> 16; + int xap = xapoints[x] & 0xffff; + + const unsigned int *sptr = ypoints[dyy + y] + xpoints[x]; + int r, g, b; + qt_qimageScaleAARGB_helper(sptr, xap, Cx, 1, r, g, b); + + int yap = yapoints[dyy + y]; + if (yap > 0) { + int rr, bb, gg; + qt_qimageScaleAARGB_helper(sptr + sow, xap, Cx, 1, rr, gg, bb); + + r = r * (256 - yap); + g = g * (256 - yap); + b = b * (256 - yap); + r = (r + (rr * yap)) >> 8; + g = (g + (gg * yap)) >> 8; + b = (b + (bb * yap)) >> 8; + } + *dptr++ = qRgb(r >> 14, g >> 14, b >> 14); + } + } +} + +static void qt_qimageScaleAARGB_down_xy(QImageScaleInfo *isi, unsigned int *dest, + int dxx, int dyy, int dx, int dy, int dw, + int dh, int dow, int sow) +{ + const unsigned int **ypoints = isi->ypoints; + int *xpoints = isi->xpoints; + int *xapoints = isi->xapoints; + int *yapoints = isi->yapoints; + + int end = dxx + dw; + + for (int y = 0; y < dh; y++) { + int Cy = (yapoints[dyy + y]) >> 16; + int yap = (yapoints[dyy + y]) & 0xffff; + + unsigned int *dptr = dest + dx + ((y + dy) * dow); + for (int x = dxx; x < end; x++) { + int Cx = xapoints[x] >> 16; + int xap = xapoints[x] & 0xffff; + + const unsigned int *sptr = ypoints[dyy + y] + xpoints[x]; + int rx, gx, bx; + qt_qimageScaleAARGB_helper(sptr, xap, Cx, 1, rx, gx, bx); + + int r = (rx >> 4) * yap; + int g = (gx >> 4) * yap; + int b = (bx >> 4) * yap; + + int j; + for (j = (1 << 14) - yap; j > Cy; j -= Cy) { + sptr += sow; + qt_qimageScaleAARGB_helper(sptr, xap, Cx, 1, rx, gx, bx); + + r += (rx >> 4) * Cy; + g += (gx >> 4) * Cy; + b += (bx >> 4) * Cy; + } + sptr += sow; + qt_qimageScaleAARGB_helper(sptr, xap, Cx, 1, rx, gx, bx); + + r += (rx >> 4) * j; + g += (gx >> 4) * j; + b += (bx >> 4) * j; + + *dptr = qRgb(r >> 24, g >> 24, b >> 24); + dptr++; + } + } +} + +QImage qSmoothScaleImage(const QImage &src, int dw, int dh) +{ + QImage buffer; + if (src.isNull() || dw <= 0 || dh <= 0) + return buffer; + + int w = src.width(); + int h = src.height(); + QImageScaleInfo *scaleinfo = + qimageCalcScaleInfo(src, w, h, dw, dh, true); + if (!scaleinfo) + return buffer; + + buffer = QImage(dw, dh, src.format()); + if (buffer.isNull()) { + qWarning("QImage: out of memory, returning null"); + qimageFreeScaleInfo(scaleinfo); + return QImage(); + } + + if (src.hasAlphaChannel()) + qt_qimageScaleAARGBA(scaleinfo, (unsigned int *)buffer.scanLine(0), + 0, 0, 0, 0, dw, dh, dw, src.bytesPerLine() / 4); + else + qt_qimageScaleAARGB(scaleinfo, (unsigned int *)buffer.scanLine(0), + 0, 0, 0, 0, dw, dh, dw, src.bytesPerLine() / 4); + + qimageFreeScaleInfo(scaleinfo); + return buffer; +} + +QT_END_NAMESPACE