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
51QT_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
102namespace 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
110using namespace QImageScale;
111
112//
113// Code ported from Imlib...
114//
115
116static 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(t: 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(a: 0LL, b: 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
146static 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(t: 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(a: 0LL, b: 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
175static 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
222static 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
234static 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(t: dw) >= sw) + ((qAbs(t: dh) >= sh) << 1);
251
252 isi->xpoints = qimageCalcXPoints(sw: img.width(), dw: scw);
253 if (!isi->xpoints)
254 return qimageFreeScaleInfo(isi);
255 isi->ypoints = qimageCalcYPoints(src: (const unsigned int *)img.scanLine(0),
256 sw: img.bytesPerLine() / 4, sh: img.height(), dh: sch);
257 if (!isi->ypoints)
258 return qimageFreeScaleInfo(isi);
259 if (aa) {
260 isi->xapoints = qimageCalcApoints(s: img.width(), d: scw, up: isi->xup_yup & 1);
261 if (!isi->xapoints)
262 return qimageFreeScaleInfo(isi);
263 isi->yapoints = qimageCalcApoints(s: img.height(), d: sch, up: isi->xup_yup & 2);
264 if (!isi->yapoints)
265 return qimageFreeScaleInfo(isi);
266 }
267 return isi;
268}
269
270
271static void qt_qimageScaleAARGBA_up_x_down_y(QImageScaleInfo *isi, unsigned int *dest,
272 int dw, int dh, int dow, int sow);
273
274static void qt_qimageScaleAARGBA_down_x_up_y(QImageScaleInfo *isi, unsigned int *dest,
275 int dw, int dh, int dow, int sow);
276
277static 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)
281template<bool RGB>
282void qt_qimageScaleAARGBA_up_x_down_y_sse4(QImageScaleInfo *isi, unsigned int *dest,
283 int dw, int dh, int dow, int sow);
284template<bool RGB>
285void qt_qimageScaleAARGBA_down_x_up_y_sse4(QImageScaleInfo *isi, unsigned int *dest,
286 int dw, int dh, int dow, int sow);
287template<bool RGB>
288void 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__)
293template<bool RGB>
294void qt_qimageScaleAARGBA_up_x_down_y_neon(QImageScaleInfo *isi, unsigned int *dest,
295 int dw, int dh, int dow, int sow);
296template<bool RGB>
297void qt_qimageScaleAARGBA_down_x_up_y_neon(QImageScaleInfo *isi, unsigned int *dest,
298 int dw, int dh, int dow, int sow);
299template<bool RGB>
300void qt_qimageScaleAARGBA_down_xy_neon(QImageScaleInfo *isi, unsigned int *dest,
301 int dw, int dh, int dow, int sow);
302#endif
303
304template<typename T>
305static 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(a: segments, b: dh);
310 QThreadPool *threadPool = QThreadPool::globalInstance();
311 if (segments > 1 && threadPool && !threadPool->contains(thread: 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(n: 1);
319 });
320 y += yn;
321 }
322 semaphore.acquire(n: segments);
323 return;
324 }
325#endif
326 scaleSection(0, dh);
327}
328
329static 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(t: pix, b: pix + sow, distx: xap, disty: yap);
350 else
351 *dptr = INTERPOLATE_PIXEL_256(x: pix[0], a: 256 - yap, y: pix[sow], b: 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(x: pix[0], a: 256 - xap, y: pix[1], b: 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 */
371static 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
419inline 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(rgb: *pix) * xyap;
422 g = qGreen(rgb: *pix) * xyap;
423 b = qBlue(rgb: *pix) * xyap;
424 a = qAlpha(rgb: *pix) * xyap;
425 int j;
426 for (j = (1 << 14) - xyap; j > Cxy; j -= Cxy) {
427 pix += step;
428 r += qRed(rgb: *pix) * Cxy;
429 g += qGreen(rgb: *pix) * Cxy;
430 b += qBlue(rgb: *pix) * Cxy;
431 a += qAlpha(rgb: *pix) * Cxy;
432 }
433 pix += step;
434 r += qRed(rgb: *pix) * j;
435 g += qGreen(rgb: *pix) * j;
436 b += qBlue(rgb: *pix) * j;
437 a += qAlpha(rgb: *pix) * j;
438}
439
440static 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(pix: sptr, xyap: yap, Cxy: Cy, step: 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(pix: sptr + 1, xyap: yap, Cxy: Cy, step: sow, r&: rr, g&: gg, b&: bb, a&: 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: r >> 14, g: g >> 14, b: b >> 14, a: a >> 14);
475 }
476 }
477 };
478 multithread_pixels_function(isi, dh, scaleSection);
479}
480
481static 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(pix: sptr, xyap: xap, Cxy: Cx, step: 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(pix: sptr + sow, xyap: xap, Cxy: Cx, step: 1, r&: rr, g&: gg, b&: bb, a&: 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: r >> 14, g: g >> 14, b: b >> 14, a: a >> 14);
516 dptr++;
517 }
518 }
519 };
520 multithread_pixels_function(isi, dh, scaleSection);
521}
522
523static 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(pix: sptr, xyap: xap, Cxy: Cx, step: 1, r&: rx, g&: gx, b&: bx, a&: 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(pix: sptr, xyap: xap, Cxy: Cx, step: 1, r&: rx, g&: gx, b&: bx, a&: 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(pix: sptr, xyap: xap, Cxy: Cx, step: 1, r&: rx, g&: gx, b&: bx, a&: 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: r >> 24, g: g >> 24, b: b >> 24, a: a >> 24);
568 dptr++;
569 }
570 }
571 };
572 multithread_pixels_function(isi, dh, scaleSection);
573}
574
575#if QT_CONFIG(raster_64bit)
576static void qt_qimageScaleRgba64_up_x_down_y(QImageScaleInfo *isi, QRgba64 *dest,
577 int dw, int dh, int dow, int sow);
578
579static void qt_qimageScaleRgba64_down_x_up_y(QImageScaleInfo *isi, QRgba64 *dest,
580 int dw, int dh, int dow, int sow);
581
582static void qt_qimageScaleRgba64_down_xy(QImageScaleInfo *isi, QRgba64 *dest,
583 int dw, int dh, int dow, int sow);
584
585static 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(t: pix, b: pix + sow, distx: xap * 256, disty: yap * 256);
604 else
605 *dptr = interpolate256(x: pix[0], alpha1: 256 - yap, y: pix[sow], alpha2: 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(x: pix[0], alpha1: 256 - xap, y: pix[1], alpha2: xap);
614 else
615 *dptr = pix[0];
616 dptr++;
617 }
618 }
619 }
620 };
621 multithread_pixels_function(isi, dh, scaleSection);
622}
623
624void 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
637inline 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
658static 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(pix: sptr, xyap: yap, Cxy: Cy, step: 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(pix: sptr + 1, xyap: yap, Cxy: Cy, step: sow, r&: rr, g&: gg, b&: bb, a&: 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: r >> 14, g: g >> 14, b: b >> 14, a: a >> 14);
692 }
693 }
694 };
695 multithread_pixels_function(isi, dh, scaleSection);
696}
697
698static 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(pix: sptr, xyap: xap, Cxy: Cx, step: 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(pix: sptr + sow, xyap: xap, Cxy: Cx, step: 1, r&: rr, g&: gg, b&: bb, a&: 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: r >> 14, g: g >> 14, b: b >> 14, a: a >> 14);
732 dptr++;
733 }
734 }
735 };
736 multithread_pixels_function(isi, dh, scaleSection);
737}
738
739static 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(pix: sptr, xyap: xap, Cxy: Cx, step: 1, r&: rx, g&: gx, b&: bx, a&: 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(pix: sptr, xyap: xap, Cxy: Cx, step: 1, r&: rx, g&: gx, b&: bx, a&: 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(pix: sptr, xyap: xap, Cxy: Cx, step: 1, r&: rx, g&: gx, b&: bx, a&: ax);
776 r += rx * j;
777 g += gx * j;
778 b += bx * j;
779 a += ax * j;
780
781 *dptr = qRgba64(r: r >> 28, g: g >> 28, b: b >> 28, a: a >> 28);
782 dptr++;
783 }
784 }
785 };
786 multithread_pixels_function(isi, dh, scaleSection);
787}
788#endif
789
790static void qt_qimageScaleAARGB_up_x_down_y(QImageScaleInfo *isi, unsigned int *dest,
791 int dw, int dh, int dow, int sow);
792
793static void qt_qimageScaleAARGB_down_x_up_y(QImageScaleInfo *isi, unsigned int *dest,
794 int dw, int dh, int dow, int sow);
795
796static 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*/
800static 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
849inline 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(rgb: *pix) * xyap;
852 g = qGreen(rgb: *pix) * xyap;
853 b = qBlue(rgb: *pix) * xyap;
854 int j;
855 for (j = (1 << 14) - xyap; j > Cxy; j -= Cxy) {
856 pix += step;
857 r += qRed(rgb: *pix) * Cxy;
858 g += qGreen(rgb: *pix) * Cxy;
859 b += qBlue(rgb: *pix) * Cxy;
860 }
861 pix += step;
862 r += qRed(rgb: *pix) * j;
863 g += qGreen(rgb: *pix) * j;
864 b += qBlue(rgb: *pix) * j;
865}
866
867static 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(pix: sptr, xyap: yap, Cxy: Cy, step: sow, r, g, b);
886
887 int xap = xapoints[x];
888 if (xap > 0) {
889 int rr, bb, gg;
890 qt_qimageScaleAARGB_helper(pix: sptr + 1, xyap: yap, Cxy: Cy, step: sow, r&: rr, g&: gg, b&: 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: r >> 14, g: g >> 14, b: b >> 14);
900 }
901 }
902 };
903 multithread_pixels_function(isi, dh, scaleSection);
904}
905
906static 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(pix: sptr, xyap: xap, Cxy: Cx, step: 1, r, g, b);
925
926 int yap = yapoints[y];
927 if (yap > 0) {
928 int rr, bb, gg;
929 qt_qimageScaleAARGB_helper(pix: sptr + sow, xyap: xap, Cxy: Cx, step: 1, r&: rr, g&: gg, b&: 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: r >> 14, g: g >> 14, b: b >> 14);
939 }
940 }
941 };
942 multithread_pixels_function(isi, dh, scaleSection);
943}
944
945static 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(pix: sptr, xyap: xap, Cxy: Cx, step: 1, r&: rx, g&: gx, b&: 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(pix: sptr, xyap: xap, Cxy: Cx, step: 1, r&: rx, g&: gx, b&: 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(pix: sptr, xyap: xap, Cxy: Cx, step: 1, r&: rx, g&: gx, b&: bx);
982
983 r += (rx >> 4) * j;
984 g += (gx >> 4) * j;
985 b += (bx >> 4) * j;
986
987 *dptr = qRgb(r: r >> 24, g: g >> 24, b: b >> 24);
988 dptr++;
989 }
990 }
991 };
992 multithread_pixels_function(isi, dh, scaleSection);
993}
994
995QImage 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(img: src, sw: w, sh: h, dw, dh, aa: true);
1005 if (!scaleinfo)
1006 return buffer;
1007
1008 buffer = QImage(dw, dh, src.format());
1009 if (buffer.isNull()) {
1010 qWarning(msg: "QImage: out of memory, returning null");
1011 qimageFreeScaleInfo(isi: scaleinfo);
1012 return QImage();
1013 }
1014
1015#if QT_CONFIG(raster_64bit)
1016 if (src.depth() > 32)
1017 qt_qimageScaleRgba64(isi: scaleinfo, dest: (QRgba64 *)buffer.scanLine(0),
1018 dw, dh, dow: dw, sow: src.bytesPerLine() / 8);
1019 else
1020#endif
1021 if (src.hasAlphaChannel())
1022 qt_qimageScaleAARGBA(isi: scaleinfo, dest: (unsigned int *)buffer.scanLine(0),
1023 dw, dh, dow: dw, sow: src.bytesPerLine() / 4);
1024 else
1025 qt_qimageScaleAARGB(isi: scaleinfo, dest: (unsigned int *)buffer.scanLine(0),
1026 dw, dh, dow: dw, sow: src.bytesPerLine() / 4);
1027
1028 qimageFreeScaleInfo(isi: scaleinfo);
1029 return buffer;
1030}
1031
1032QT_END_NAMESPACE
1033

source code of qtbase/src/gui/painting/qimagescale.cpp