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