fixed Qt 5.5 image scale crash, fixed empty photo in mediaview crash

This commit is contained in:
John Preston 2015-07-15 14:23:59 +03:00
parent d5afafcdd0
commit e193a86eaa
6 changed files with 895 additions and 53 deletions

View file

@ -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}";

View file

@ -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 &currentText) {
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 &currentTex
} 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)
{

View file

@ -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);

View file

@ -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<HistoryPhoto*>(item->getMedia())->photo(), item); preloadData(delta); break;
case MediaTypeDocument: displayDocument(static_cast<HistoryDocument*>(item->getMedia())->document(), item); preloadData(delta); break;
case MediaTypeSticker: displayDocument(static_cast<HistorySticker*>(item->getMedia())->document(), item); preloadData(delta); break;
if (item->getMedia()) {
switch (item->getMedia()->type()) {
case MediaTypePhoto: displayPhoto(static_cast<HistoryPhoto*>(item->getMedia())->photo(), item); preloadData(delta); break;
case MediaTypeDocument: displayDocument(static_cast<HistoryDocument*>(item->getMedia())->document(), item); preloadData(delta); break;
case MediaTypeSticker: displayDocument(static_cast<HistorySticker*>(item->getMedia())->document(), item); preloadData(delta); break;
}
} else {
displayDocument(0, item);
preloadData(delta);
}
}
}

View file

@ -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

View file

@ -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 <private/qimagescale_p.h>
#include <private/qdrawhelper_p.h>
#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 <willem@stack.nl>. 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<bool RGB>
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<bool RGB>
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<bool RGB>
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<false>(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<false>(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<false>(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<true>(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<true>(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<true>(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