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