1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 #include <private/qimagescale_p.h>
40 #include <private/qdrawhelper_p.h>
41 
42 #include "qimage.h"
43 #include "qcolor.h"
44 #include "qrgba64_p.h"
45 
46 #if QT_CONFIG(thread) && !defined(Q_OS_WASM)
47 #include "qsemaphore.h"
48 #include "qthreadpool.h"
49 #endif
50 
51 QT_BEGIN_NAMESPACE
52 
53 /*
54  * Copyright (C) 2004, 2005 Daniel M. Duley
55  *
56  * Redistribution and use in source and binary forms, with or without
57  * modification, are permitted provided that the following conditions
58  * are met:
59  *
60  * 1. Redistributions of source code must retain the above copyright
61  *    notice, this list of conditions and the following disclaimer.
62  * 2. Redistributions in binary form must reproduce the above copyright
63  *    notice, this list of conditions and the following disclaimer in the
64  *    documentation and/or other materials provided with the distribution.
65  *
66  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
67  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
68  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
69  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
70  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
71  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
72  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
73  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
74  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
75  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
76  *
77  */
78 
79 /* OTHER CREDITS:
80  *
81  * This is the normal smoothscale method, based on Imlib2's smoothscale.
82  *
83  * Originally I took the algorithm used in NetPBM and Qt and added MMX/3dnow
84  * optimizations. It ran in about 1/2 the time as Qt. Then I ported Imlib's
85  * C algorithm and it ran at about the same speed as my MMX optimized one...
86  * Finally I ported Imlib's MMX version and it ran in less than half the
87  * time as my MMX algorithm, (taking only a quarter of the time Qt does).
88  * After further optimization it seems to run at around 1/6th.
89  *
90  * Changes include formatting, namespaces and other C++'ings, removal of old
91  * #ifdef'ed code, and removal of unneeded border calculation code.
92  * Later the code has been refactored, an SSE4.1 optimizated path have been
93  * added instead of the removed MMX assembler, and scaling of clipped area
94  * removed, and an RGBA64 version written
95  *
96  * Imlib2 is (C) Carsten Haitzler and various contributors. The MMX code
97  * is by Willem Monsuwe <willem@stack.nl>. All other modifications are
98  * (C) Daniel M. Duley.
99  */
100 
101 
102 namespace QImageScale {
103     static const unsigned int** qimageCalcYPoints(const unsigned int *src, int sw, int sh, int dh);
104     static int* qimageCalcXPoints(int sw, int dw);
105     static int* qimageCalcApoints(int s, int d, int up);
106     static QImageScaleInfo* qimageFreeScaleInfo(QImageScaleInfo *isi);
107     static QImageScaleInfo *qimageCalcScaleInfo(const QImage &img, int sw, int sh, int dw, int dh, char aa);
108 }
109 
110 using namespace QImageScale;
111 
112 //
113 // Code ported from Imlib...
114 //
115 
qimageCalcYPoints(const unsigned int * src,int sw,int sh,int dh)116 static const unsigned int** QImageScale::qimageCalcYPoints(const unsigned int *src,
117                                                            int sw, int sh, int dh)
118 {
119     const unsigned int **p;
120     int j = 0, rv = 0;
121     qint64 val, inc;
122 
123     if (dh < 0) {
124         dh = -dh;
125         rv = 1;
126     }
127     p = new const unsigned int* [dh+1];
128 
129     int up = qAbs(dh) >= sh;
130     val = up ? 0x8000 * sh / dh - 0x8000 : 0;
131     inc = (((qint64)sh) << 16) / dh;
132     for (int i = 0; i < dh; i++) {
133         p[j++] = src + qMax(0LL, val >> 16) * sw;
134         val += inc;
135     }
136     if (rv) {
137         for (int i = dh / 2; --i >= 0; ) {
138             const unsigned int *tmp = p[i];
139             p[i] = p[dh - i - 1];
140             p[dh - i - 1] = tmp;
141         }
142     }
143     return(p);
144 }
145 
qimageCalcXPoints(int sw,int dw)146 static int* QImageScale::qimageCalcXPoints(int sw, int dw)
147 {
148     int *p, j = 0, rv = 0;
149     qint64 val, inc;
150 
151     if (dw < 0) {
152         dw = -dw;
153         rv = 1;
154     }
155     p = new int[dw+1];
156 
157     int up = qAbs(dw) >= sw;
158     val = up ? 0x8000 * sw / dw - 0x8000 : 0;
159     inc = (((qint64)sw) << 16) / dw;
160     for (int i = 0; i < dw; i++) {
161         p[j++] = qMax(0LL, val >> 16);
162         val += inc;
163     }
164 
165     if (rv) {
166         for (int i = dw / 2; --i >= 0; ) {
167             int tmp = p[i];
168             p[i] = p[dw - i - 1];
169             p[dw - i - 1] = tmp;
170         }
171     }
172    return p;
173 }
174 
qimageCalcApoints(int s,int d,int up)175 static int* QImageScale::qimageCalcApoints(int s, int d, int up)
176 {
177     int *p, j = 0, rv = 0;
178 
179     if (d < 0) {
180         rv = 1;
181         d = -d;
182     }
183     p = new int[d];
184 
185     if (up) {
186         /* scaling up */
187         qint64 val = 0x8000 * s / d - 0x8000;
188         qint64 inc = (((qint64)s) << 16) / d;
189         for (int i = 0; i < d; i++) {
190             int pos = val >> 16;
191             if (pos < 0)
192                 p[j++] = 0;
193             else if (pos >= (s - 1))
194                 p[j++] = 0;
195             else
196                 p[j++] = (val >> 8) - ((val >> 8) & 0xffffff00);
197             val += inc;
198         }
199     } else {
200         /* scaling down */
201         qint64 val = 0;
202         qint64 inc = (((qint64)s) << 16) / d;
203         int Cp = (((d << 14) + s - 1) / s);
204         for (int i = 0; i < d; i++) {
205             int ap = ((0x10000 - (val & 0xffff)) * Cp) >> 16;
206             p[j] = ap | (Cp << 16);
207             j++;
208             val += inc;
209         }
210     }
211     if (rv) {
212         int tmp;
213         for (int i = d / 2; --i >= 0; ) {
214             tmp = p[i];
215             p[i] = p[d - i - 1];
216             p[d - i - 1] = tmp;
217         }
218     }
219     return p;
220 }
221 
qimageFreeScaleInfo(QImageScaleInfo * isi)222 static QImageScaleInfo* QImageScale::qimageFreeScaleInfo(QImageScaleInfo *isi)
223 {
224     if (isi) {
225         delete[] isi->xpoints;
226         delete[] isi->ypoints;
227         delete[] isi->xapoints;
228         delete[] isi->yapoints;
229         delete isi;
230     }
231     return nullptr;
232 }
233 
qimageCalcScaleInfo(const QImage & img,int sw,int sh,int dw,int dh,char aa)234 static QImageScaleInfo* QImageScale::qimageCalcScaleInfo(const QImage &img,
235                                                          int sw, int sh,
236                                                          int dw, int dh, char aa)
237 {
238     QImageScaleInfo *isi;
239     int scw, sch;
240 
241     scw = dw * qlonglong(img.width()) / sw;
242     sch = dh * qlonglong(img.height()) / sh;
243 
244     isi = new QImageScaleInfo;
245     if (!isi)
246         return nullptr;
247     isi->sh = sh;
248     isi->sw = sw;
249 
250     isi->xup_yup = (qAbs(dw) >= sw) + ((qAbs(dh) >= sh) << 1);
251 
252     isi->xpoints = qimageCalcXPoints(img.width(), scw);
253     if (!isi->xpoints)
254         return qimageFreeScaleInfo(isi);
255     isi->ypoints = qimageCalcYPoints((const unsigned int *)img.scanLine(0),
256                                      img.bytesPerLine() / 4, img.height(), sch);
257     if (!isi->ypoints)
258         return qimageFreeScaleInfo(isi);
259     if (aa) {
260         isi->xapoints = qimageCalcApoints(img.width(), scw, isi->xup_yup & 1);
261         if (!isi->xapoints)
262             return qimageFreeScaleInfo(isi);
263         isi->yapoints = qimageCalcApoints(img.height(), sch, isi->xup_yup & 2);
264         if (!isi->yapoints)
265             return qimageFreeScaleInfo(isi);
266     }
267     return isi;
268 }
269 
270 
271 static void qt_qimageScaleAARGBA_up_x_down_y(QImageScaleInfo *isi, unsigned int *dest,
272                                              int dw, int dh, int dow, int sow);
273 
274 static void qt_qimageScaleAARGBA_down_x_up_y(QImageScaleInfo *isi, unsigned int *dest,
275                                              int dw, int dh, int dow, int sow);
276 
277 static void qt_qimageScaleAARGBA_down_xy(QImageScaleInfo *isi, unsigned int *dest,
278                                          int dw, int dh, int dow, int sow);
279 
280 #if defined(QT_COMPILER_SUPPORTS_SSE4_1)
281 template<bool RGB>
282 void qt_qimageScaleAARGBA_up_x_down_y_sse4(QImageScaleInfo *isi, unsigned int *dest,
283                                            int dw, int dh, int dow, int sow);
284 template<bool RGB>
285 void qt_qimageScaleAARGBA_down_x_up_y_sse4(QImageScaleInfo *isi, unsigned int *dest,
286                                            int dw, int dh, int dow, int sow);
287 template<bool RGB>
288 void qt_qimageScaleAARGBA_down_xy_sse4(QImageScaleInfo *isi, unsigned int *dest,
289                                        int dw, int dh, int dow, int sow);
290 #endif
291 
292 #if defined(__ARM_NEON__)
293 template<bool RGB>
294 void qt_qimageScaleAARGBA_up_x_down_y_neon(QImageScaleInfo *isi, unsigned int *dest,
295                                            int dw, int dh, int dow, int sow);
296 template<bool RGB>
297 void qt_qimageScaleAARGBA_down_x_up_y_neon(QImageScaleInfo *isi, unsigned int *dest,
298                                            int dw, int dh, int dow, int sow);
299 template<bool RGB>
300 void qt_qimageScaleAARGBA_down_xy_neon(QImageScaleInfo *isi, unsigned int *dest,
301                                        int dw, int dh, int dow, int sow);
302 #endif
303 
304 template<typename T>
multithread_pixels_function(QImageScaleInfo * isi,int dh,const T & scaleSection)305 static inline void multithread_pixels_function(QImageScaleInfo *isi, int dh, const T &scaleSection)
306 {
307 #if QT_CONFIG(thread) && !defined(Q_OS_WASM)
308     int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16);
309     segments = std::min(segments, dh);
310     QThreadPool *threadPool = QThreadPool::globalInstance();
311     if (segments > 1 && threadPool && !threadPool->contains(QThread::currentThread())) {
312         QSemaphore semaphore;
313         int y = 0;
314         for (int i = 0; i < segments; ++i) {
315             int yn = (dh - y) / (segments - i);
316             threadPool->start([&, y, yn]() {
317                 scaleSection(y, y + yn);
318                 semaphore.release(1);
319             });
320             y += yn;
321         }
322         semaphore.acquire(segments);
323         return;
324     }
325 #endif
326     scaleSection(0, dh);
327 }
328 
qt_qimageScaleAARGBA_up_xy(QImageScaleInfo * isi,unsigned int * dest,int dw,int dh,int dow,int sow)329 static void qt_qimageScaleAARGBA_up_xy(QImageScaleInfo *isi, unsigned int *dest,
330                                        int dw, int dh, int dow, int sow)
331 {
332     const unsigned int **ypoints = isi->ypoints;
333     int *xpoints = isi->xpoints;
334     int *xapoints = isi->xapoints;
335     int *yapoints = isi->yapoints;
336 
337     /* go through every scanline in the output buffer */
338     auto scaleSection = [&] (int yStart, int yEnd) {
339         for (int y = yStart; y < yEnd; ++y) {
340             /* calculate the source line we'll scan from */
341             const unsigned int *sptr = ypoints[y];
342             unsigned int *dptr = dest + (y * dow);
343             const int yap = yapoints[y];
344             if (yap > 0) {
345                 for (int x = 0; x < dw; x++) {
346                     const unsigned int *pix = sptr + xpoints[x];
347                     const int xap = xapoints[x];
348                     if (xap > 0)
349                         *dptr = interpolate_4_pixels(pix, pix + sow, xap, yap);
350                     else
351                         *dptr = INTERPOLATE_PIXEL_256(pix[0], 256 - yap, pix[sow], yap);
352                     dptr++;
353                 }
354             } else {
355                 for (int x = 0; x < dw; x++) {
356                     const unsigned int *pix = sptr + xpoints[x];
357                     const int xap = xapoints[x];
358                     if (xap > 0)
359                         *dptr = INTERPOLATE_PIXEL_256(pix[0], 256 - xap, pix[1], xap);
360                     else
361                         *dptr = pix[0];
362                     dptr++;
363                 }
364             }
365         }
366     };
367     multithread_pixels_function(isi, dh, scaleSection);
368 }
369 
370 /* scale by area sampling - with alpha */
qt_qimageScaleAARGBA(QImageScaleInfo * isi,unsigned int * dest,int dw,int dh,int dow,int sow)371 static void qt_qimageScaleAARGBA(QImageScaleInfo *isi, unsigned int *dest,
372                                  int dw, int dh, int dow, int sow)
373 {
374     /* scaling up both ways */
375     if (isi->xup_yup == 3) {
376         qt_qimageScaleAARGBA_up_xy(isi, dest, dw, dh, dow, sow);
377     }
378     /* if we're scaling down vertically */
379     else if (isi->xup_yup == 1) {
380 #ifdef QT_COMPILER_SUPPORTS_SSE4_1
381         if (qCpuHasFeature(SSE4_1))
382             qt_qimageScaleAARGBA_up_x_down_y_sse4<false>(isi, dest, dw, dh, dow, sow);
383         else
384 #elif defined(__ARM_NEON__)
385         if (qCpuHasFeature(NEON))
386             qt_qimageScaleAARGBA_up_x_down_y_neon<false>(isi, dest, dw, dh, dow, sow);
387         else
388 #endif
389         qt_qimageScaleAARGBA_up_x_down_y(isi, dest, dw, dh, dow, sow);
390     }
391     /* if we're scaling down horizontally */
392     else if (isi->xup_yup == 2) {
393 #ifdef QT_COMPILER_SUPPORTS_SSE4_1
394         if (qCpuHasFeature(SSE4_1))
395             qt_qimageScaleAARGBA_down_x_up_y_sse4<false>(isi, dest, dw, dh, dow, sow);
396         else
397 #elif defined(__ARM_NEON__)
398         if (qCpuHasFeature(NEON))
399             qt_qimageScaleAARGBA_down_x_up_y_neon<false>(isi, dest, dw, dh, dow, sow);
400         else
401 #endif
402         qt_qimageScaleAARGBA_down_x_up_y(isi, dest, dw, dh, dow, sow);
403     }
404     /* if we're scaling down horizontally & vertically */
405     else {
406 #ifdef QT_COMPILER_SUPPORTS_SSE4_1
407         if (qCpuHasFeature(SSE4_1))
408             qt_qimageScaleAARGBA_down_xy_sse4<false>(isi, dest, dw, dh, dow, sow);
409         else
410 #elif defined(__ARM_NEON__)
411         if (qCpuHasFeature(NEON))
412             qt_qimageScaleAARGBA_down_xy_neon<false>(isi, dest, dw, dh, dow, sow);
413         else
414 #endif
415         qt_qimageScaleAARGBA_down_xy(isi, dest, dw, dh, dow, sow);
416     }
417 }
418 
qt_qimageScaleAARGBA_helper(const unsigned int * pix,int xyap,int Cxy,int step,int & r,int & g,int & b,int & a)419 inline static void qt_qimageScaleAARGBA_helper(const unsigned int *pix, int xyap, int Cxy, int step, int &r, int &g, int &b, int &a)
420 {
421     r = qRed(*pix)   * xyap;
422     g = qGreen(*pix) * xyap;
423     b = qBlue(*pix)  * xyap;
424     a = qAlpha(*pix) * xyap;
425     int j;
426     for (j = (1 << 14) - xyap; j > Cxy; j -= Cxy) {
427         pix += step;
428         r += qRed(*pix)   * Cxy;
429         g += qGreen(*pix) * Cxy;
430         b += qBlue(*pix)  * Cxy;
431         a += qAlpha(*pix) * Cxy;
432     }
433     pix += step;
434     r += qRed(*pix)   * j;
435     g += qGreen(*pix) * j;
436     b += qBlue(*pix)  * j;
437     a += qAlpha(*pix) * j;
438 }
439 
qt_qimageScaleAARGBA_up_x_down_y(QImageScaleInfo * isi,unsigned int * dest,int dw,int dh,int dow,int sow)440 static void qt_qimageScaleAARGBA_up_x_down_y(QImageScaleInfo *isi, unsigned int *dest,
441                                              int dw, int dh, int dow, int sow)
442 {
443     const unsigned int **ypoints = isi->ypoints;
444     int *xpoints = isi->xpoints;
445     int *xapoints = isi->xapoints;
446     int *yapoints = isi->yapoints;
447 
448     /* go through every scanline in the output buffer */
449     auto scaleSection = [&] (int yStart, int yEnd) {
450         for (int y = yStart; y < yEnd; ++y) {
451             int Cy = yapoints[y] >> 16;
452             int yap = yapoints[y] & 0xffff;
453 
454             unsigned int *dptr = dest + (y * dow);
455             for (int x = 0; x < dw; x++) {
456                 const unsigned int *sptr = ypoints[y] + xpoints[x];
457                 int r, g, b, a;
458                 qt_qimageScaleAARGBA_helper(sptr, yap, Cy, sow, r, g, b, a);
459 
460                 int xap = xapoints[x];
461                 if (xap > 0) {
462                     int rr, gg, bb, aa;
463                     qt_qimageScaleAARGBA_helper(sptr + 1, yap, Cy, sow, rr, gg, bb, aa);
464 
465                     r = r * (256 - xap);
466                     g = g * (256 - xap);
467                     b = b * (256 - xap);
468                     a = a * (256 - xap);
469                     r = (r + (rr * xap)) >> 8;
470                     g = (g + (gg * xap)) >> 8;
471                     b = (b + (bb * xap)) >> 8;
472                     a = (a + (aa * xap)) >> 8;
473                 }
474                 *dptr++ = qRgba(r >> 14, g >> 14, b >> 14, a >> 14);
475             }
476         }
477     };
478     multithread_pixels_function(isi, dh, scaleSection);
479 }
480 
qt_qimageScaleAARGBA_down_x_up_y(QImageScaleInfo * isi,unsigned int * dest,int dw,int dh,int dow,int sow)481 static void qt_qimageScaleAARGBA_down_x_up_y(QImageScaleInfo *isi, unsigned int *dest,
482                                              int dw, int dh, int dow, int sow)
483 {
484     const unsigned int **ypoints = isi->ypoints;
485     int *xpoints = isi->xpoints;
486     int *xapoints = isi->xapoints;
487     int *yapoints = isi->yapoints;
488 
489     /* go through every scanline in the output buffer */
490     auto scaleSection = [&] (int yStart, int yEnd) {
491         for (int y = yStart; y < yEnd; ++y) {
492             unsigned int *dptr = dest + (y * dow);
493             for (int x = 0; x < dw; x++) {
494                 int Cx = xapoints[x] >> 16;
495                 int xap = xapoints[x] & 0xffff;
496 
497                 const unsigned int *sptr = ypoints[y] + xpoints[x];
498                 int r, g, b, a;
499                 qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, r, g, b, a);
500 
501                 int yap = yapoints[y];
502                 if (yap > 0) {
503                     int rr, gg, bb, aa;
504                     qt_qimageScaleAARGBA_helper(sptr + sow, xap, Cx, 1, rr, gg, bb, aa);
505 
506                     r = r * (256 - yap);
507                     g = g * (256 - yap);
508                     b = b * (256 - yap);
509                     a = a * (256 - yap);
510                     r = (r + (rr * yap)) >> 8;
511                     g = (g + (gg * yap)) >> 8;
512                     b = (b + (bb * yap)) >> 8;
513                     a = (a + (aa * yap)) >> 8;
514                 }
515                 *dptr = qRgba(r >> 14, g >> 14, b >> 14, a >> 14);
516                 dptr++;
517             }
518         }
519     };
520     multithread_pixels_function(isi, dh, scaleSection);
521 }
522 
qt_qimageScaleAARGBA_down_xy(QImageScaleInfo * isi,unsigned int * dest,int dw,int dh,int dow,int sow)523 static void qt_qimageScaleAARGBA_down_xy(QImageScaleInfo *isi, unsigned int *dest,
524                                          int dw, int dh, int dow, int sow)
525 {
526     const unsigned int **ypoints = isi->ypoints;
527     int *xpoints = isi->xpoints;
528     int *xapoints = isi->xapoints;
529     int *yapoints = isi->yapoints;
530 
531     auto scaleSection = [&] (int yStart, int yEnd) {
532         for (int y = yStart; y < yEnd; ++y) {
533             int Cy = (yapoints[y]) >> 16;
534             int yap = (yapoints[y]) & 0xffff;
535 
536             unsigned int *dptr = dest + (y * dow);
537             for (int x = 0; x < dw; x++) {
538                 int Cx = xapoints[x] >> 16;
539                 int xap = xapoints[x] & 0xffff;
540 
541                 const unsigned int *sptr = ypoints[y] + xpoints[x];
542                 int rx, gx, bx, ax;
543                 qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, rx, gx, bx, ax);
544 
545                 int r = ((rx>>4) * yap);
546                 int g = ((gx>>4) * yap);
547                 int b = ((bx>>4) * yap);
548                 int a = ((ax>>4) * yap);
549 
550                 int j;
551                 for (j = (1 << 14) - yap; j > Cy; j -= Cy) {
552                     sptr += sow;
553                     qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, rx, gx, bx, ax);
554                     r += ((rx>>4) * Cy);
555                     g += ((gx>>4) * Cy);
556                     b += ((bx>>4) * Cy);
557                     a += ((ax>>4) * Cy);
558                 }
559                 sptr += sow;
560                 qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, rx, gx, bx, ax);
561 
562                 r += ((rx>>4) * j);
563                 g += ((gx>>4) * j);
564                 b += ((bx>>4) * j);
565                 a += ((ax>>4) * j);
566 
567                 *dptr = qRgba(r >> 24, g >> 24, b >> 24, a >> 24);
568                 dptr++;
569             }
570         }
571     };
572     multithread_pixels_function(isi, dh, scaleSection);
573 }
574 
575 #if QT_CONFIG(raster_64bit)
576 static void qt_qimageScaleRgba64_up_x_down_y(QImageScaleInfo *isi, QRgba64 *dest,
577                                              int dw, int dh, int dow, int sow);
578 
579 static void qt_qimageScaleRgba64_down_x_up_y(QImageScaleInfo *isi, QRgba64 *dest,
580                                              int dw, int dh, int dow, int sow);
581 
582 static void qt_qimageScaleRgba64_down_xy(QImageScaleInfo *isi, QRgba64 *dest,
583                                          int dw, int dh, int dow, int sow);
584 
qt_qimageScaleRgba64_up_xy(QImageScaleInfo * isi,QRgba64 * dest,int dw,int dh,int dow,int sow)585 static void qt_qimageScaleRgba64_up_xy(QImageScaleInfo *isi, QRgba64 *dest,
586                                        int dw, int dh, int dow, int sow)
587 {
588     const QRgba64 **ypoints = (const QRgba64 **)isi->ypoints;
589     int *xpoints = isi->xpoints;
590     int *xapoints = isi->xapoints;
591     int *yapoints = isi->yapoints;
592 
593     auto scaleSection = [&] (int yStart, int yEnd) {
594         for (int y = yStart; y < yEnd; ++y) {
595             const QRgba64 *sptr = ypoints[y];
596             QRgba64 *dptr = dest + (y * dow);
597             const int yap = yapoints[y];
598             if (yap > 0) {
599                 for (int x = 0; x < dw; x++) {
600                     const QRgba64 *pix = sptr + xpoints[x];
601                     const int xap = xapoints[x];
602                     if (xap > 0)
603                         *dptr = interpolate_4_pixels_rgb64(pix, pix + sow, xap * 256, yap * 256);
604                     else
605                         *dptr = interpolate256(pix[0], 256 - yap, pix[sow], yap);
606                     dptr++;
607                 }
608             } else {
609                 for (int x = 0; x < dw; x++) {
610                     const QRgba64 *pix = sptr + xpoints[x];
611                     const int xap = xapoints[x];
612                     if (xap > 0)
613                         *dptr = interpolate256(pix[0], 256 - xap, pix[1], xap);
614                     else
615                         *dptr = pix[0];
616                     dptr++;
617                 }
618             }
619         }
620     };
621     multithread_pixels_function(isi, dh, scaleSection);
622 }
623 
qt_qimageScaleRgba64(QImageScaleInfo * isi,QRgba64 * dest,int dw,int dh,int dow,int sow)624 void qt_qimageScaleRgba64(QImageScaleInfo *isi, QRgba64 *dest,
625                           int dw, int dh, int dow, int sow)
626 {
627     if (isi->xup_yup == 3)
628         qt_qimageScaleRgba64_up_xy(isi, dest, dw, dh, dow, sow);
629     else if (isi->xup_yup == 1)
630         qt_qimageScaleRgba64_up_x_down_y(isi, dest, dw, dh, dow, sow);
631     else if (isi->xup_yup == 2)
632         qt_qimageScaleRgba64_down_x_up_y(isi, dest, dw, dh, dow, sow);
633     else
634         qt_qimageScaleRgba64_down_xy(isi, dest, dw, dh, dow, sow);
635 }
636 
qt_qimageScaleRgba64_helper(const QRgba64 * pix,int xyap,int Cxy,int step,qint64 & r,qint64 & g,qint64 & b,qint64 & a)637 inline static void qt_qimageScaleRgba64_helper(const QRgba64 *pix, int xyap, int Cxy, int step, qint64 &r, qint64 &g, qint64 &b, qint64 &a)
638 {
639     r = pix->red()   * xyap;
640     g = pix->green() * xyap;
641     b = pix->blue()  * xyap;
642     a = pix->alpha() * xyap;
643     int j;
644     for (j = (1 << 14) - xyap; j > Cxy; j -= Cxy ){
645         pix += step;
646         r += pix->red()   * Cxy;
647         g += pix->green() * Cxy;
648         b += pix->blue()  * Cxy;
649         a += pix->alpha() * Cxy;
650     }
651     pix += step;
652     r += pix->red()   * j;
653     g += pix->green() * j;
654     b += pix->blue()  * j;
655     a += pix->alpha() * j;
656 }
657 
qt_qimageScaleRgba64_up_x_down_y(QImageScaleInfo * isi,QRgba64 * dest,int dw,int dh,int dow,int sow)658 static void qt_qimageScaleRgba64_up_x_down_y(QImageScaleInfo *isi, QRgba64 *dest,
659                                              int dw, int dh, int dow, int sow)
660 {
661     const QRgba64 **ypoints = (const QRgba64 **)isi->ypoints;
662     int *xpoints = isi->xpoints;
663     int *xapoints = isi->xapoints;
664     int *yapoints = isi->yapoints;
665 
666     auto scaleSection = [&] (int yStart, int yEnd) {
667         for (int y = yStart; y < yEnd; ++y) {
668             int Cy = (yapoints[y]) >> 16;
669             int yap = (yapoints[y]) & 0xffff;
670 
671             QRgba64 *dptr = dest + (y * dow);
672             for (int x = 0; x < dw; x++) {
673                 const QRgba64 *sptr = ypoints[y] + xpoints[x];
674                 qint64 r, g, b, a;
675                 qt_qimageScaleRgba64_helper(sptr, yap, Cy, sow, r, g, b, a);
676 
677                 int xap = xapoints[x];
678                 if (xap > 0) {
679                     qint64 rr, gg, bb, aa;
680                     qt_qimageScaleRgba64_helper(sptr + 1, yap, Cy, sow, rr, gg, bb, aa);
681 
682                     r = r * (256 - xap);
683                     g = g * (256 - xap);
684                     b = b * (256 - xap);
685                     a = a * (256 - xap);
686                     r = (r + (rr * xap)) >> 8;
687                     g = (g + (gg * xap)) >> 8;
688                     b = (b + (bb * xap)) >> 8;
689                     a = (a + (aa * xap)) >> 8;
690                 }
691                 *dptr++ = qRgba64(r >> 14, g >> 14, b >> 14, a >> 14);
692             }
693         }
694     };
695     multithread_pixels_function(isi, dh, scaleSection);
696 }
697 
qt_qimageScaleRgba64_down_x_up_y(QImageScaleInfo * isi,QRgba64 * dest,int dw,int dh,int dow,int sow)698 static void qt_qimageScaleRgba64_down_x_up_y(QImageScaleInfo *isi, QRgba64 *dest,
699                                              int dw, int dh, int dow, int sow)
700 {
701     const QRgba64 **ypoints = (const QRgba64 **)isi->ypoints;
702     int *xpoints = isi->xpoints;
703     int *xapoints = isi->xapoints;
704     int *yapoints = isi->yapoints;
705 
706     auto scaleSection = [&] (int yStart, int yEnd) {
707         for (int y = yStart; y < yEnd; ++y) {
708             QRgba64 *dptr = dest + (y * dow);
709             for (int x = 0; x < dw; x++) {
710                 int Cx = xapoints[x] >> 16;
711                 int xap = xapoints[x] & 0xffff;
712 
713                 const QRgba64 *sptr = ypoints[y] + xpoints[x];
714                 qint64 r, g, b, a;
715                 qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, r, g, b, a);
716 
717                 int yap = yapoints[y];
718                 if (yap > 0) {
719                     qint64 rr, gg, bb, aa;
720                     qt_qimageScaleRgba64_helper(sptr + sow, xap, Cx, 1, rr, gg, bb, aa);
721 
722                     r = r * (256 - yap);
723                     g = g * (256 - yap);
724                     b = b * (256 - yap);
725                     a = a * (256 - yap);
726                     r = (r + (rr * yap)) >> 8;
727                     g = (g + (gg * yap)) >> 8;
728                     b = (b + (bb * yap)) >> 8;
729                     a = (a + (aa * yap)) >> 8;
730                 }
731                 *dptr = qRgba64(r >> 14, g >> 14, b >> 14, a >> 14);
732                 dptr++;
733             }
734         }
735     };
736     multithread_pixels_function(isi, dh, scaleSection);
737 }
738 
qt_qimageScaleRgba64_down_xy(QImageScaleInfo * isi,QRgba64 * dest,int dw,int dh,int dow,int sow)739 static void qt_qimageScaleRgba64_down_xy(QImageScaleInfo *isi, QRgba64 *dest,
740                                          int dw, int dh, int dow, int sow)
741 {
742     const QRgba64 **ypoints = (const QRgba64 **)isi->ypoints;
743     int *xpoints = isi->xpoints;
744     int *xapoints = isi->xapoints;
745     int *yapoints = isi->yapoints;
746 
747     auto scaleSection = [&] (int yStart, int yEnd) {
748         for (int y = yStart; y < yEnd; ++y) {
749             int Cy = (yapoints[y]) >> 16;
750             int yap = (yapoints[y]) & 0xffff;
751 
752             QRgba64 *dptr = dest + (y * dow);
753             for (int x = 0; x < dw; x++) {
754                 int Cx = xapoints[x] >> 16;
755                 int xap = xapoints[x] & 0xffff;
756 
757                 const QRgba64 *sptr = ypoints[y] + xpoints[x];
758                 qint64 rx, gx, bx, ax;
759                 qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, rx, gx, bx, ax);
760 
761                 qint64 r = rx * yap;
762                 qint64 g = gx * yap;
763                 qint64 b = bx * yap;
764                 qint64 a = ax * yap;
765                 int j;
766                 for (j = (1 << 14) - yap; j > Cy; j -= Cy) {
767                     sptr += sow;
768                     qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, rx, gx, bx, ax);
769                     r += rx * Cy;
770                     g += gx * Cy;
771                     b += bx * Cy;
772                     a += ax * Cy;
773                 }
774                 sptr += sow;
775                 qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, rx, gx, bx, ax);
776                 r += rx * j;
777                 g += gx * j;
778                 b += bx * j;
779                 a += ax * j;
780 
781                 *dptr = qRgba64(r >> 28, g >> 28, b >> 28, a >> 28);
782                 dptr++;
783             }
784         }
785     };
786     multithread_pixels_function(isi, dh, scaleSection);
787 }
788 #endif
789 
790 static void qt_qimageScaleAARGB_up_x_down_y(QImageScaleInfo *isi, unsigned int *dest,
791                                             int dw, int dh, int dow, int sow);
792 
793 static void qt_qimageScaleAARGB_down_x_up_y(QImageScaleInfo *isi, unsigned int *dest,
794                                             int dw, int dh, int dow, int sow);
795 
796 static void qt_qimageScaleAARGB_down_xy(QImageScaleInfo *isi, unsigned int *dest,
797                                         int dw, int dh, int dow, int sow);
798 
799 /* scale by area sampling - IGNORE the ALPHA byte*/
qt_qimageScaleAARGB(QImageScaleInfo * isi,unsigned int * dest,int dw,int dh,int dow,int sow)800 static void qt_qimageScaleAARGB(QImageScaleInfo *isi, unsigned int *dest,
801                                 int dw, int dh, int dow, int sow)
802 {
803     /* scaling up both ways */
804     if (isi->xup_yup == 3) {
805         qt_qimageScaleAARGBA_up_xy(isi, dest, dw, dh, dow, sow);
806     }
807     /* if we're scaling down vertically */
808     else if (isi->xup_yup == 1) {
809 #ifdef QT_COMPILER_SUPPORTS_SSE4_1
810         if (qCpuHasFeature(SSE4_1))
811             qt_qimageScaleAARGBA_up_x_down_y_sse4<true>(isi, dest, dw, dh, dow, sow);
812         else
813 #elif defined(__ARM_NEON__)
814         if (qCpuHasFeature(NEON))
815             qt_qimageScaleAARGBA_up_x_down_y_neon<true>(isi, dest, dw, dh, dow, sow);
816         else
817 #endif
818         qt_qimageScaleAARGB_up_x_down_y(isi, dest, dw, dh, dow, sow);
819     }
820     /* if we're scaling down horizontally */
821     else if (isi->xup_yup == 2) {
822 #ifdef QT_COMPILER_SUPPORTS_SSE4_1
823         if (qCpuHasFeature(SSE4_1))
824             qt_qimageScaleAARGBA_down_x_up_y_sse4<true>(isi, dest, dw, dh, dow, sow);
825         else
826 #elif defined(__ARM_NEON__)
827         if (qCpuHasFeature(NEON))
828             qt_qimageScaleAARGBA_down_x_up_y_neon<true>(isi, dest, dw, dh, dow, sow);
829         else
830 #endif
831         qt_qimageScaleAARGB_down_x_up_y(isi, dest, dw, dh, dow, sow);
832     }
833     /* if we're scaling down horizontally & vertically */
834     else {
835 #ifdef QT_COMPILER_SUPPORTS_SSE4_1
836         if (qCpuHasFeature(SSE4_1))
837             qt_qimageScaleAARGBA_down_xy_sse4<true>(isi, dest, dw, dh, dow, sow);
838         else
839 #elif defined(__ARM_NEON__)
840         if (qCpuHasFeature(NEON))
841             qt_qimageScaleAARGBA_down_xy_neon<true>(isi, dest, dw, dh, dow, sow);
842         else
843 #endif
844         qt_qimageScaleAARGB_down_xy(isi, dest, dw, dh, dow, sow);
845     }
846 }
847 
848 
qt_qimageScaleAARGB_helper(const unsigned int * pix,int xyap,int Cxy,int step,int & r,int & g,int & b)849 inline static void qt_qimageScaleAARGB_helper(const unsigned int *pix, int xyap, int Cxy, int step, int &r, int &g, int &b)
850 {
851     r = qRed(*pix)   * xyap;
852     g = qGreen(*pix) * xyap;
853     b = qBlue(*pix)  * xyap;
854     int j;
855     for (j = (1 << 14) - xyap; j > Cxy; j -= Cxy) {
856         pix += step;
857         r += qRed(*pix)   * Cxy;
858         g += qGreen(*pix) * Cxy;
859         b += qBlue(*pix)  * Cxy;
860     }
861     pix += step;
862     r += qRed(*pix)   * j;
863     g += qGreen(*pix) * j;
864     b += qBlue(*pix)  * j;
865 }
866 
qt_qimageScaleAARGB_up_x_down_y(QImageScaleInfo * isi,unsigned int * dest,int dw,int dh,int dow,int sow)867 static void qt_qimageScaleAARGB_up_x_down_y(QImageScaleInfo *isi, unsigned int *dest,
868                                             int dw, int dh, int dow, int sow)
869 {
870     const unsigned int **ypoints = isi->ypoints;
871     int *xpoints = isi->xpoints;
872     int *xapoints = isi->xapoints;
873     int *yapoints = isi->yapoints;
874 
875     /* go through every scanline in the output buffer */
876     auto scaleSection = [&] (int yStart, int yEnd) {
877         for (int y = yStart; y < yEnd; ++y) {
878             int Cy = yapoints[y] >> 16;
879             int yap = yapoints[y] & 0xffff;
880 
881             unsigned int *dptr = dest + (y * dow);
882             for (int x = 0; x < dw; x++) {
883                 const unsigned int *sptr = ypoints[y] + xpoints[x];
884                 int r, g, b;
885                 qt_qimageScaleAARGB_helper(sptr, yap, Cy, sow, r, g, b);
886 
887                 int xap = xapoints[x];
888                 if (xap > 0) {
889                     int rr, bb, gg;
890                     qt_qimageScaleAARGB_helper(sptr + 1, yap, Cy, sow, rr, gg, bb);
891 
892                     r = r * (256 - xap);
893                     g = g * (256 - xap);
894                     b = b * (256 - xap);
895                     r = (r + (rr * xap)) >> 8;
896                     g = (g + (gg * xap)) >> 8;
897                     b = (b + (bb * xap)) >> 8;
898                 }
899                 *dptr++ = qRgb(r >> 14, g >> 14, b >> 14);
900             }
901         }
902     };
903     multithread_pixels_function(isi, dh, scaleSection);
904 }
905 
qt_qimageScaleAARGB_down_x_up_y(QImageScaleInfo * isi,unsigned int * dest,int dw,int dh,int dow,int sow)906 static void qt_qimageScaleAARGB_down_x_up_y(QImageScaleInfo *isi, unsigned int *dest,
907                                             int dw, int dh, int dow, int sow)
908 {
909     const unsigned int **ypoints = isi->ypoints;
910     int *xpoints = isi->xpoints;
911     int *xapoints = isi->xapoints;
912     int *yapoints = isi->yapoints;
913 
914     /* go through every scanline in the output buffer */
915     auto scaleSection = [&] (int yStart, int yEnd) {
916         for (int y = yStart; y < yEnd; ++y) {
917             unsigned int *dptr = dest + (y * dow);
918             for (int x = 0; x < dw; x++) {
919                 int Cx = xapoints[x] >> 16;
920                 int xap = xapoints[x] & 0xffff;
921 
922                 const unsigned int *sptr = ypoints[y] + xpoints[x];
923                 int r, g, b;
924                 qt_qimageScaleAARGB_helper(sptr, xap, Cx, 1, r, g, b);
925 
926                 int yap = yapoints[y];
927                 if (yap > 0) {
928                     int rr, bb, gg;
929                     qt_qimageScaleAARGB_helper(sptr + sow, xap, Cx, 1, rr, gg, bb);
930 
931                     r = r * (256 - yap);
932                     g = g * (256 - yap);
933                     b = b * (256 - yap);
934                     r = (r + (rr * yap)) >> 8;
935                     g = (g + (gg * yap)) >> 8;
936                     b = (b + (bb * yap)) >> 8;
937                 }
938                 *dptr++ = qRgb(r >> 14, g >> 14, b >> 14);
939             }
940         }
941     };
942     multithread_pixels_function(isi, dh, scaleSection);
943 }
944 
qt_qimageScaleAARGB_down_xy(QImageScaleInfo * isi,unsigned int * dest,int dw,int dh,int dow,int sow)945 static void qt_qimageScaleAARGB_down_xy(QImageScaleInfo *isi, unsigned int *dest,
946                                         int dw, int dh, int dow, int sow)
947 {
948     const unsigned int **ypoints = isi->ypoints;
949     int *xpoints = isi->xpoints;
950     int *xapoints = isi->xapoints;
951     int *yapoints = isi->yapoints;
952 
953     auto scaleSection = [&] (int yStart, int yEnd) {
954         for (int y = yStart; y < yEnd; ++y) {
955             int Cy = yapoints[y] >> 16;
956             int yap = yapoints[y] & 0xffff;
957 
958             unsigned int *dptr = dest + (y * dow);
959             for (int x = 0; x < dw; x++) {
960                 int Cx = xapoints[x] >> 16;
961                 int xap = xapoints[x] & 0xffff;
962 
963                 const unsigned int *sptr = ypoints[y] + xpoints[x];
964                 int rx, gx, bx;
965                 qt_qimageScaleAARGB_helper(sptr, xap, Cx, 1, rx, gx, bx);
966 
967                 int r = (rx >> 4) * yap;
968                 int g = (gx >> 4) * yap;
969                 int b = (bx >> 4) * yap;
970 
971                 int j;
972                 for (j = (1 << 14) - yap; j > Cy; j -= Cy) {
973                     sptr += sow;
974                     qt_qimageScaleAARGB_helper(sptr, xap, Cx, 1, rx, gx, bx);
975 
976                     r += (rx >> 4) * Cy;
977                     g += (gx >> 4) * Cy;
978                     b += (bx >> 4) * Cy;
979                 }
980                 sptr += sow;
981                 qt_qimageScaleAARGB_helper(sptr, xap, Cx, 1, rx, gx, bx);
982 
983                 r += (rx >> 4) * j;
984                 g += (gx >> 4) * j;
985                 b += (bx >> 4) * j;
986 
987                 *dptr = qRgb(r >> 24, g >> 24, b >> 24);
988                 dptr++;
989             }
990         }
991     };
992     multithread_pixels_function(isi, dh, scaleSection);
993 }
994 
qSmoothScaleImage(const QImage & src,int dw,int dh)995 QImage qSmoothScaleImage(const QImage &src, int dw, int dh)
996 {
997     QImage buffer;
998     if (src.isNull() || dw <= 0 || dh <= 0)
999         return buffer;
1000 
1001     int w = src.width();
1002     int h = src.height();
1003     QImageScaleInfo *scaleinfo =
1004         qimageCalcScaleInfo(src, w, h, dw, dh, true);
1005     if (!scaleinfo)
1006         return buffer;
1007 
1008     buffer = QImage(dw, dh, src.format());
1009     if (buffer.isNull()) {
1010         qWarning("QImage: out of memory, returning null");
1011         qimageFreeScaleInfo(scaleinfo);
1012         return QImage();
1013     }
1014 
1015 #if QT_CONFIG(raster_64bit)
1016     if (src.depth() > 32)
1017         qt_qimageScaleRgba64(scaleinfo, (QRgba64 *)buffer.scanLine(0),
1018                              dw, dh, dw, src.bytesPerLine() / 8);
1019     else
1020 #endif
1021     if (src.hasAlphaChannel())
1022         qt_qimageScaleAARGBA(scaleinfo, (unsigned int *)buffer.scanLine(0),
1023                              dw, dh, dw, src.bytesPerLine() / 4);
1024     else
1025         qt_qimageScaleAARGB(scaleinfo, (unsigned int *)buffer.scanLine(0),
1026                             dw, dh, dw, src.bytesPerLine() / 4);
1027 
1028     qimageFreeScaleInfo(scaleinfo);
1029     return buffer;
1030 }
1031 
1032 QT_END_NAMESPACE
1033