blob: 0d7205b4831ba0515de1f2189072b636ba73dcda [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** 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 https://www.qt.io/terms-conditions. For further
** information use the contact form at https://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 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <private/qimagescale_p.h>
#include <private/qdrawhelper_p.h>
#include "qimage.h"
#include "qcolor.h"
#include "qrgba64_p.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.
* Later the code has been refactored, an SSE4.1 optimizated path have been
* added instead of the removed MMX assembler, and scaling of clipped area
* removed, and an RGBA64 version written
*
* 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 {
static const unsigned int** qimageCalcYPoints(const unsigned int *src, int sw, int sh, int dh);
static int* qimageCalcXPoints(int sw, int dw);
static int* qimageCalcApoints(int s, int d, int up);
static QImageScaleInfo* qimageFreeScaleInfo(QImageScaleInfo *isi);
static QImageScaleInfo *qimageCalcScaleInfo(const QImage &img, int sw, int sh, int dw, int dh, char aa);
}
using namespace QImageScale;
//
// Code ported from Imlib...
//
static 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);
}
static 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;
}
static 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];
if (up) {
/* scaling up */
qint64 val = 0x8000 * s / d - 0x8000;
qint64 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;
}
} else {
/* scaling down */
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;
}
static QImageScaleInfo* QImageScale::qimageFreeScaleInfo(QImageScaleInfo *isi)
{
if (isi) {
delete[] isi->xpoints;
delete[] isi->ypoints;
delete[] isi->xapoints;
delete[] isi->yapoints;
delete isi;
}
return 0;
}
static 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;
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 dw, int dh, int dow, int sow);
static void qt_qimageScaleAARGBA_down_x_up_y(QImageScaleInfo *isi, unsigned int *dest,
int dw, int dh, int dow, int sow);
static void qt_qimageScaleAARGBA_down_xy(QImageScaleInfo *isi, unsigned int *dest,
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 dw, int dh, int dow, int sow);
template<bool RGB>
void qt_qimageScaleAARGBA_down_x_up_y_sse4(QImageScaleInfo *isi, unsigned int *dest,
int dw, int dh, int dow, int sow);
template<bool RGB>
void qt_qimageScaleAARGBA_down_xy_sse4(QImageScaleInfo *isi, unsigned int *dest,
int dw, int dh, int dow, int sow);
#endif
#if defined(__ARM_NEON__)
template<bool RGB>
void qt_qimageScaleAARGBA_up_x_down_y_neon(QImageScaleInfo *isi, unsigned int *dest,
int dw, int dh, int dow, int sow);
template<bool RGB>
void qt_qimageScaleAARGBA_down_x_up_y_neon(QImageScaleInfo *isi, unsigned int *dest,
int dw, int dh, int dow, int sow);
template<bool RGB>
void qt_qimageScaleAARGBA_down_xy_neon(QImageScaleInfo *isi, unsigned int *dest,
int dw, int dh, int dow, int sow);
#endif
static void qt_qimageScaleAARGBA_up_xy(QImageScaleInfo *isi, unsigned int *dest,
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;
/* 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[y];
unsigned int *dptr = dest + (y * dow);
const int yap = yapoints[y];
if (yap > 0) {
for (int x = 0; x < dw; x++) {
const unsigned int *pix = sptr + xpoints[x];
const int xap = xapoints[x];
if (xap > 0)
*dptr = interpolate_4_pixels(pix, pix + sow, xap, yap);
else
*dptr = INTERPOLATE_PIXEL_256(pix[0], 256 - yap, pix[sow], yap);
dptr++;
}
} else {
for (int x = 0; x < dw; 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 - with alpha */
static void qt_qimageScaleAARGBA(QImageScaleInfo *isi, unsigned int *dest,
int dw, int dh, int dow, int sow)
{
/* scaling up both ways */
if (isi->xup_yup == 3) {
qt_qimageScaleAARGBA_up_xy(isi, dest, 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, dw, dh, dow, sow);
else
#elif defined(__ARM_NEON__)
if (qCpuHasFeature(NEON))
qt_qimageScaleAARGBA_up_x_down_y_neon<false>(isi, dest, dw, dh, dow, sow);
else
#endif
qt_qimageScaleAARGBA_up_x_down_y(isi, dest, 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, dw, dh, dow, sow);
else
#elif defined(__ARM_NEON__)
if (qCpuHasFeature(NEON))
qt_qimageScaleAARGBA_down_x_up_y_neon<false>(isi, dest, dw, dh, dow, sow);
else
#endif
qt_qimageScaleAARGBA_down_x_up_y(isi, dest, 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, dw, dh, dow, sow);
else
#elif defined(__ARM_NEON__)
if (qCpuHasFeature(NEON))
qt_qimageScaleAARGBA_down_xy_neon<false>(isi, dest, dw, dh, dow, sow);
else
#endif
qt_qimageScaleAARGBA_down_xy(isi, dest, 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 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;
/* go through every scanline in the output buffer */
for (int y = 0; y < dh; y++) {
int Cy = yapoints[y] >> 16;
int yap = yapoints[y] & 0xffff;
unsigned int *dptr = dest + (y * dow);
for (int x = 0; x < dw; x++) {
const unsigned int *sptr = ypoints[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 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;
/* go through every scanline in the output buffer */
for (int y = 0; y < dh; y++) {
unsigned int *dptr = dest + (y * dow);
for (int x = 0; x < dw; x++) {
int Cx = xapoints[x] >> 16;
int xap = xapoints[x] & 0xffff;
const unsigned int *sptr = ypoints[y] + xpoints[x];
int r, g, b, a;
qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, r, g, b, a);
int yap = yapoints[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 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;
for (int y = 0; y < dh; y++) {
int Cy = (yapoints[y]) >> 16;
int yap = (yapoints[y]) & 0xffff;
unsigned int *dptr = dest + (y * dow);
for (int x = 0; x < dw; x++) {
int Cx = xapoints[x] >> 16;
int xap = xapoints[x] & 0xffff;
const unsigned int *sptr = ypoints[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++;
}
}
}
#if QT_CONFIG(raster_64bit)
static void qt_qimageScaleRgba64_up_x_down_y(QImageScaleInfo *isi, QRgba64 *dest,
int dw, int dh, int dow, int sow);
static void qt_qimageScaleRgba64_down_x_up_y(QImageScaleInfo *isi, QRgba64 *dest,
int dw, int dh, int dow, int sow);
static void qt_qimageScaleRgba64_down_xy(QImageScaleInfo *isi, QRgba64 *dest,
int dw, int dh, int dow, int sow);
static void qt_qimageScaleRgba64_up_xy(QImageScaleInfo *isi, QRgba64 *dest,
int dw, int dh, int dow, int sow)
{
const QRgba64 **ypoints = (const QRgba64 **)isi->ypoints;
int *xpoints = isi->xpoints;
int *xapoints = isi->xapoints;
int *yapoints = isi->yapoints;
for (int y = 0; y < dh; y++) {
const QRgba64 *sptr = ypoints[y];
QRgba64 *dptr = dest + (y * dow);
const int yap = yapoints[y];
if (yap > 0) {
for (int x = 0; x < dw; x++) {
const QRgba64 *pix = sptr + xpoints[x];
const int xap = xapoints[x];
if (xap > 0)
*dptr = interpolate_4_pixels_rgb64(pix, pix + sow, xap * 256, yap * 256);
else
*dptr = interpolate256(pix[0], 256 - yap, pix[sow], yap);
dptr++;
}
} else {
for (int x = 0; x < dw; x++) {
const QRgba64 *pix = sptr + xpoints[x];
const int xap = xapoints[x];
if (xap > 0)
*dptr = interpolate256(pix[0], 256 - xap, pix[1], xap);
else
*dptr = pix[0];
dptr++;
}
}
}
}
void qt_qimageScaleRgba64(QImageScaleInfo *isi, QRgba64 *dest,
int dw, int dh, int dow, int sow)
{
if (isi->xup_yup == 3)
qt_qimageScaleRgba64_up_xy(isi, dest, dw, dh, dow, sow);
else if (isi->xup_yup == 1)
qt_qimageScaleRgba64_up_x_down_y(isi, dest, dw, dh, dow, sow);
else if (isi->xup_yup == 2)
qt_qimageScaleRgba64_down_x_up_y(isi, dest, dw, dh, dow, sow);
else
qt_qimageScaleRgba64_down_xy(isi, dest, dw, dh, dow, sow);
}
inline static void qt_qimageScaleRgba64_helper(const QRgba64 *pix, int xyap, int Cxy, int step, qint64 &r, qint64 &g, qint64 &b, qint64 &a)
{
r = pix->red() * xyap;
g = pix->green() * xyap;
b = pix->blue() * xyap;
a = pix->alpha() * xyap;
int j;
for (j = (1 << 14) - xyap; j > Cxy; j -= Cxy ){
pix += step;
r += pix->red() * Cxy;
g += pix->green() * Cxy;
b += pix->blue() * Cxy;
a += pix->alpha() * Cxy;
}
pix += step;
r += pix->red() * j;
g += pix->green() * j;
b += pix->blue() * j;
a += pix->alpha() * j;
}
static void qt_qimageScaleRgba64_up_x_down_y(QImageScaleInfo *isi, QRgba64 *dest,
int dw, int dh, int dow, int sow)
{
const QRgba64 **ypoints = (const QRgba64 **)isi->ypoints;
int *xpoints = isi->xpoints;
int *xapoints = isi->xapoints;
int *yapoints = isi->yapoints;
for (int y = 0; y < dh; y++) {
int Cy = (yapoints[y]) >> 16;
int yap = (yapoints[y]) & 0xffff;
QRgba64 *dptr = dest + (y * dow);
for (int x = 0; x < dw; x++) {
const QRgba64 *sptr = ypoints[y] + xpoints[x];
qint64 r, g, b, a;
qt_qimageScaleRgba64_helper(sptr, yap, Cy, sow, r, g, b, a);
int xap = xapoints[x];
if (xap > 0) {
qint64 rr, gg, bb, aa;
qt_qimageScaleRgba64_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++ = qRgba64(r >> 14, g >> 14, b >> 14, a >> 14);
}
}
}
static void qt_qimageScaleRgba64_down_x_up_y(QImageScaleInfo *isi, QRgba64 *dest,
int dw, int dh, int dow, int sow)
{
const QRgba64 **ypoints = (const QRgba64 **)isi->ypoints;
int *xpoints = isi->xpoints;
int *xapoints = isi->xapoints;
int *yapoints = isi->yapoints;
for (int y = 0; y < dh; y++) {
QRgba64 *dptr = dest + (y * dow);
for (int x = 0; x < dw; x++) {
int Cx = xapoints[x] >> 16;
int xap = xapoints[x] & 0xffff;
const QRgba64 *sptr = ypoints[y] + xpoints[x];
qint64 r, g, b, a;
qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, r, g, b, a);
int yap = yapoints[y];
if (yap > 0) {
qint64 rr, gg, bb, aa;
qt_qimageScaleRgba64_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 = qRgba64(r >> 14, g >> 14, b >> 14, a >> 14);
dptr++;
}
}
}
static void qt_qimageScaleRgba64_down_xy(QImageScaleInfo *isi, QRgba64 *dest,
int dw, int dh, int dow, int sow)
{
const QRgba64 **ypoints = (const QRgba64 **)isi->ypoints;
int *xpoints = isi->xpoints;
int *xapoints = isi->xapoints;
int *yapoints = isi->yapoints;
for (int y = 0; y < dh; y++) {
int Cy = (yapoints[y]) >> 16;
int yap = (yapoints[y]) & 0xffff;
QRgba64 *dptr = dest + (y * dow);
for (int x = 0; x < dw; x++) {
int Cx = xapoints[x] >> 16;
int xap = xapoints[x] & 0xffff;
const QRgba64 *sptr = ypoints[y] + xpoints[x];
qint64 rx, gx, bx, ax;
qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, rx, gx, bx, ax);
qint64 r = rx * yap;
qint64 g = gx * yap;
qint64 b = bx * yap;
qint64 a = ax * yap;
int j;
for (j = (1 << 14) - yap; j > Cy; j -= Cy) {
sptr += sow;
qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, rx, gx, bx, ax);
r += rx * Cy;
g += gx * Cy;
b += bx * Cy;
a += ax * Cy;
}
sptr += sow;
qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, rx, gx, bx, ax);
r += rx * j;
g += gx * j;
b += bx * j;
a += ax * j;
*dptr = qRgba64(r >> 28, g >> 28, b >> 28, a >> 28);
dptr++;
}
}
}
#endif
static void qt_qimageScaleAARGB_up_x_down_y(QImageScaleInfo *isi, unsigned int *dest,
int dw, int dh, int dow, int sow);
static void qt_qimageScaleAARGB_down_x_up_y(QImageScaleInfo *isi, unsigned int *dest,
int dw, int dh, int dow, int sow);
static void qt_qimageScaleAARGB_down_xy(QImageScaleInfo *isi, unsigned int *dest,
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 dw, int dh, int dow, int sow)
{
/* scaling up both ways */
if (isi->xup_yup == 3) {
qt_qimageScaleAARGBA_up_xy(isi, dest, 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, dw, dh, dow, sow);
else
#elif defined(__ARM_NEON__)
if (qCpuHasFeature(NEON))
qt_qimageScaleAARGBA_up_x_down_y_neon<true>(isi, dest, dw, dh, dow, sow);
else
#endif
qt_qimageScaleAARGB_up_x_down_y(isi, dest, 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, dw, dh, dow, sow);
else
#elif defined(__ARM_NEON__)
if (qCpuHasFeature(NEON))
qt_qimageScaleAARGBA_down_x_up_y_neon<true>(isi, dest, dw, dh, dow, sow);
else
#endif
qt_qimageScaleAARGB_down_x_up_y(isi, dest, 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, dw, dh, dow, sow);
else
#elif defined(__ARM_NEON__)
if (qCpuHasFeature(NEON))
qt_qimageScaleAARGBA_down_xy_neon<true>(isi, dest, dw, dh, dow, sow);
else
#endif
qt_qimageScaleAARGB_down_xy(isi, dest, 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 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;
/* go through every scanline in the output buffer */
for (int y = 0; y < dh; y++) {
int Cy = yapoints[y] >> 16;
int yap = yapoints[y] & 0xffff;
unsigned int *dptr = dest + (y * dow);
for (int x = 0; x < dw; x++) {
const unsigned int *sptr = ypoints[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 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;
/* go through every scanline in the output buffer */
for (int y = 0; y < dh; y++) {
unsigned int *dptr = dest + (y * dow);
for (int x = 0; x < dw; x++) {
int Cx = xapoints[x] >> 16;
int xap = xapoints[x] & 0xffff;
const unsigned int *sptr = ypoints[y] + xpoints[x];
int r, g, b;
qt_qimageScaleAARGB_helper(sptr, xap, Cx, 1, r, g, b);
int yap = yapoints[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 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;
for (int y = 0; y < dh; y++) {
int Cy = yapoints[y] >> 16;
int yap = yapoints[y] & 0xffff;
unsigned int *dptr = dest + (y * dow);
for (int x = 0; x < dw; x++) {
int Cx = xapoints[x] >> 16;
int xap = xapoints[x] & 0xffff;
const unsigned int *sptr = ypoints[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 QT_CONFIG(raster_64bit)
if (src.depth() > 32)
qt_qimageScaleRgba64(scaleinfo, (QRgba64 *)buffer.scanLine(0),
dw, dh, dw, src.bytesPerLine() / 8);
else
#endif
if (src.hasAlphaChannel())
qt_qimageScaleAARGBA(scaleinfo, (unsigned int *)buffer.scanLine(0),
dw, dh, dw, src.bytesPerLine() / 4);
else
qt_qimageScaleAARGB(scaleinfo, (unsigned int *)buffer.scanLine(0),
dw, dh, dw, src.bytesPerLine() / 4);
qimageFreeScaleInfo(scaleinfo);
return buffer;
}
QT_END_NAMESPACE