1// Copyright (C) 2022 The Qt Company Ltd.
2// Copyright (C) 2018 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qdrawhelper_p.h"
6
7#include <qstylehints.h>
8#include <qguiapplication.h>
9#include <qatomic.h>
10#include <private/qcolortransform_p.h>
11#include <private/qcolortrclut_p.h>
12#include <private/qdrawhelper_p.h>
13#include <private/qdrawhelper_x86_p.h>
14#include <private/qdrawingprimitive_sse2_p.h>
15#include <private/qdrawhelper_loongarch64_p.h>
16#include <private/qdrawingprimitive_lsx_p.h>
17#include <private/qdrawhelper_neon_p.h>
18#if defined(QT_COMPILER_SUPPORTS_MIPS_DSP) || defined(QT_COMPILER_SUPPORTS_MIPS_DSPR2)
19#include <private/qdrawhelper_mips_dsp_p.h>
20#endif
21#include <private/qguiapplication_p.h>
22#include <private/qpaintengine_raster_p.h>
23#include <private/qpainter_p.h>
24#include <private/qpixellayout_p.h>
25#include <private/qrgba64_p.h>
26#include <qendian.h>
27#include <qloggingcategory.h>
28#include <qmath.h>
29
30#if QT_CONFIG(qtgui_threadpool)
31#include <qsemaphore.h>
32#include <qthreadpool.h>
33#include <private/qthreadpool_p.h>
34#endif
35
36QT_BEGIN_NAMESPACE
37
38#if QT_CONFIG(raster_64bit) || QT_CONFIG(raster_fp)
39Q_STATIC_LOGGING_CATEGORY(lcQtGuiDrawHelper, "qt.gui.drawhelper")
40#endif
41
42#define MASK(src, a) src = BYTE_MUL(src, a)
43
44/*
45 constants and structures
46*/
47
48constexpr int fixed_scale = 1 << 16;
49constexpr int half_point = 1 << 15;
50
51template <QPixelLayout::BPP bpp> static
52inline uint QT_FASTCALL fetch1Pixel(const uchar *, int)
53{
54 Q_UNREACHABLE_RETURN(0);
55}
56
57template <>
58inline uint QT_FASTCALL fetch1Pixel<QPixelLayout::BPP1LSB>(const uchar *src, int index)
59{
60 return (src[index >> 3] >> (index & 7)) & 1;
61}
62
63template <>
64inline uint QT_FASTCALL fetch1Pixel<QPixelLayout::BPP1MSB>(const uchar *src, int index)
65{
66 return (src[index >> 3] >> (~index & 7)) & 1;
67}
68
69template <>
70inline uint QT_FASTCALL fetch1Pixel<QPixelLayout::BPP8>(const uchar *src, int index)
71{
72 return src[index];
73}
74
75template <>
76inline uint QT_FASTCALL fetch1Pixel<QPixelLayout::BPP16>(const uchar *src, int index)
77{
78 return reinterpret_cast<const quint16 *>(src)[index];
79}
80
81template <>
82inline uint QT_FASTCALL fetch1Pixel<QPixelLayout::BPP24>(const uchar *src, int index)
83{
84 return reinterpret_cast<const quint24 *>(src)[index];
85}
86
87template <>
88inline uint QT_FASTCALL fetch1Pixel<QPixelLayout::BPP32>(const uchar *src, int index)
89{
90 return reinterpret_cast<const uint *>(src)[index];
91}
92
93template <>
94inline uint QT_FASTCALL fetch1Pixel<QPixelLayout::BPP64>(const uchar *src, int index)
95{
96 // We have to do the conversion in fetch to fit into a 32bit uint
97 QRgba64 c = reinterpret_cast<const QRgba64 *>(src)[index];
98 return c.toArgb32();
99}
100
101template <>
102inline uint QT_FASTCALL fetch1Pixel<QPixelLayout::BPP16FPx4>(const uchar *src, int index)
103{
104 // We have to do the conversion in fetch to fit into a 32bit uint
105 QRgbaFloat16 c = reinterpret_cast<const QRgbaFloat16 *>(src)[index];
106 return c.toArgb32();
107}
108
109template <>
110inline uint QT_FASTCALL fetch1Pixel<QPixelLayout::BPP32FPx4>(const uchar *src, int index)
111{
112 // We have to do the conversion in fetch to fit into a 32bit uint
113 QRgbaFloat32 c = reinterpret_cast<const QRgbaFloat32 *>(src)[index];
114 return c.toArgb32();
115}
116
117typedef uint (QT_FASTCALL *Fetch1PixelFunc)(const uchar *src, int index);
118
119constexpr Fetch1PixelFunc fetch1PixelTable[QPixelLayout::BPPCount] = {
120 nullptr, // BPPNone
121 fetch1Pixel<QPixelLayout::BPP1MSB>,
122 fetch1Pixel<QPixelLayout::BPP1LSB>,
123 fetch1Pixel<QPixelLayout::BPP8>,
124 fetch1Pixel<QPixelLayout::BPP16>,
125 fetch1Pixel<QPixelLayout::BPP24>,
126 fetch1Pixel<QPixelLayout::BPP32>,
127 fetch1Pixel<QPixelLayout::BPP64>,
128 fetch1Pixel<QPixelLayout::BPP16FPx4>,
129 fetch1Pixel<QPixelLayout::BPP32FPx4>,
130};
131
132#if QT_CONFIG(raster_64bit)
133static void QT_FASTCALL convertRGBA64ToRGBA64PM(QRgba64 *buffer, int count)
134{
135 for (int i = 0; i < count; ++i)
136 buffer[i] = buffer[i].premultiplied();
137}
138
139static void QT_FASTCALL convertRGBA64PMToRGBA64PM(QRgba64 *, int)
140{
141}
142
143static void QT_FASTCALL convertRGBA16FToRGBA64PM(QRgba64 *buffer, int count)
144{
145 const QRgbaFloat16 *in = reinterpret_cast<const QRgbaFloat16 *>(buffer);
146 for (int i = 0; i < count; ++i) {
147 QRgbaFloat16 c = in[i];
148 buffer[i] = QRgba64::fromRgba64(red: c.red16(), green: c.green16(), blue: c.blue16(), alpha: c.alpha16()).premultiplied();
149 }
150}
151
152static void QT_FASTCALL convertRGBA16FPMToRGBA64PM(QRgba64 *buffer, int count)
153{
154 const QRgbaFloat16 *in = reinterpret_cast<const QRgbaFloat16 *>(buffer);
155 for (int i = 0; i < count; ++i) {
156 QRgbaFloat16 c = in[i];
157 buffer[i] = QRgba64::fromRgba64(red: c.red16(), green: c.green16(), blue: c.blue16(), alpha: c.alpha16());
158 }
159}
160
161static void QT_FASTCALL convertRGBA32FToRGBA64PM(QRgba64 *buffer, int count)
162{
163 const QRgbaFloat32 *in = reinterpret_cast<const QRgbaFloat32 *>(buffer);
164 for (int i = 0; i < count; ++i) {
165 QRgbaFloat32 c = in[i];
166 buffer[i] = QRgba64::fromRgba64(red: c.red16(), green: c.green16(), blue: c.blue16(), alpha: c.alpha16()).premultiplied();
167 }
168}
169
170static void QT_FASTCALL convertRGBA32FPMToRGBA64PM(QRgba64 *buffer, int count)
171{
172 const QRgbaFloat32 *in = reinterpret_cast<const QRgbaFloat32 *>(buffer);
173 for (int i = 0; i < count; ++i) {
174 QRgbaFloat32 c = in[i];
175 buffer[i] = QRgba64::fromRgba64(red: c.red16(), green: c.green16(), blue: c.blue16(), alpha: c.alpha16());
176 }
177}
178
179static Convert64Func convert64ToRGBA64PM[] = {
180 nullptr,
181 nullptr,
182 nullptr,
183 nullptr,
184 nullptr,
185 nullptr,
186 nullptr,
187 nullptr,
188 nullptr,
189 nullptr,
190 nullptr,
191 nullptr,
192 nullptr,
193 nullptr,
194 nullptr,
195 nullptr,
196 nullptr,
197 nullptr,
198 nullptr,
199 nullptr,
200 nullptr,
201 nullptr,
202 nullptr,
203 nullptr,
204 nullptr,
205 convertRGBA64PMToRGBA64PM,
206 convertRGBA64ToRGBA64PM,
207 convertRGBA64PMToRGBA64PM,
208 nullptr,
209 nullptr,
210 convertRGBA16FPMToRGBA64PM,
211 convertRGBA16FToRGBA64PM,
212 convertRGBA16FPMToRGBA64PM,
213 convertRGBA32FPMToRGBA64PM,
214 convertRGBA32FToRGBA64PM,
215 convertRGBA32FPMToRGBA64PM,
216 nullptr,
217};
218
219static_assert(std::size(convert64ToRGBA64PM) == QImage::NImageFormats);
220#endif
221
222#if QT_CONFIG(raster_fp)
223static void QT_FASTCALL convertRGBA64PMToRGBA32F(QRgbaFloat32 *buffer, const quint64 *src, int count)
224{
225 const auto *in = reinterpret_cast<const QRgba64 *>(src);
226 for (int i = 0; i < count; ++i) {
227 auto c = in[i];
228 buffer[i] = QRgbaFloat32::fromRgba64(red: c.red(), green: c.green(), blue: c.blue(), alpha: c.alpha()).premultiplied();
229 }
230}
231
232static void QT_FASTCALL convertRGBA64ToRGBA32F(QRgbaFloat32 *buffer, const quint64 *src, int count)
233{
234 const auto *in = reinterpret_cast<const QRgba64 *>(src);
235 for (int i = 0; i < count; ++i) {
236 auto c = in[i];
237 buffer[i] = QRgbaFloat32::fromRgba64(red: c.red(), green: c.green(), blue: c.blue(), alpha: c.alpha());
238 }
239}
240
241static void QT_FASTCALL convertRGBA16FPMToRGBA32F(QRgbaFloat32 *buffer, const quint64 *src, int count)
242{
243 qFloatFromFloat16((float *)buffer, (const qfloat16 *)src, length: count * 4);
244 for (int i = 0; i < count; ++i)
245 buffer[i] = buffer[i].premultiplied();
246}
247
248static void QT_FASTCALL convertRGBA16FToRGBA32F(QRgbaFloat32 *buffer, const quint64 *src, int count)
249{
250 qFloatFromFloat16((float *)buffer, (const qfloat16 *)src, length: count * 4);
251}
252
253static Convert64ToFPFunc convert64ToRGBA32F[] = {
254 nullptr,
255 nullptr,
256 nullptr,
257 nullptr,
258 nullptr,
259 nullptr,
260 nullptr,
261 nullptr,
262 nullptr,
263 nullptr,
264 nullptr,
265 nullptr,
266 nullptr,
267 nullptr,
268 nullptr,
269 nullptr,
270 nullptr,
271 nullptr,
272 nullptr,
273 nullptr,
274 nullptr,
275 nullptr,
276 nullptr,
277 nullptr,
278 nullptr,
279 convertRGBA64ToRGBA32F,
280 convertRGBA64PMToRGBA32F,
281 convertRGBA64ToRGBA32F,
282 nullptr,
283 nullptr,
284 convertRGBA16FToRGBA32F,
285 convertRGBA16FPMToRGBA32F,
286 convertRGBA16FToRGBA32F,
287 nullptr,
288 nullptr,
289 nullptr,
290 nullptr,
291};
292
293static_assert(std::size(convert64ToRGBA32F) == QImage::NImageFormats);
294
295static void convertRGBA32FToRGBA32FPM(QRgbaFloat32 *buffer, int count)
296{
297 for (int i = 0; i < count; ++i)
298 buffer[i] = buffer[i].premultiplied();
299}
300
301static void convertRGBA32FToRGBA32F(QRgbaFloat32 *, int)
302{
303}
304
305#endif
306
307/*
308 Destination fetch. This is simple as we don't have to do bounds checks or
309 transformations
310*/
311
312static uint * QT_FASTCALL destFetchMono(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
313{
314 uchar *Q_DECL_RESTRICT data = (uchar *)rasterBuffer->scanLine(y);
315 uint *start = buffer;
316 const uint *end = buffer + length;
317 while (buffer < end) {
318 *buffer = data[x>>3] & (0x80 >> (x & 7)) ? rasterBuffer->destColor1 : rasterBuffer->destColor0;
319 ++buffer;
320 ++x;
321 }
322 return start;
323}
324
325static uint * QT_FASTCALL destFetchMonoLsb(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
326{
327 uchar *Q_DECL_RESTRICT data = (uchar *)rasterBuffer->scanLine(y);
328 uint *start = buffer;
329 const uint *end = buffer + length;
330 while (buffer < end) {
331 *buffer = data[x>>3] & (0x1 << (x & 7)) ? rasterBuffer->destColor1 : rasterBuffer->destColor0;
332 ++buffer;
333 ++x;
334 }
335 return start;
336}
337
338static uint * QT_FASTCALL destFetchARGB32P(uint *, QRasterBuffer *rasterBuffer, int x, int y, int)
339{
340 return (uint *)rasterBuffer->scanLine(y) + x;
341}
342
343static uint * QT_FASTCALL destFetchRGB16(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
344{
345 const ushort *Q_DECL_RESTRICT data = (const ushort *)rasterBuffer->scanLine(y) + x;
346 for (int i = 0; i < length; ++i)
347 buffer[i] = qConvertRgb16To32(c: data[i]);
348 return buffer;
349}
350
351static uint *QT_FASTCALL destFetch(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
352{
353 const QPixelLayout *layout = &qPixelLayouts[rasterBuffer->format];
354 return const_cast<uint *>(layout->fetchToARGB32PM(buffer, rasterBuffer->scanLine(y), x, length, nullptr, nullptr));
355}
356
357static uint *QT_FASTCALL destFetchUndefined(uint *buffer, QRasterBuffer *, int, int, int)
358{
359 return buffer;
360}
361
362static DestFetchProc destFetchProc[] =
363{
364 nullptr, // Format_Invalid
365 destFetchMono, // Format_Mono,
366 destFetchMonoLsb, // Format_MonoLSB
367 nullptr, // Format_Indexed8
368 destFetchARGB32P, // Format_RGB32
369 destFetch, // Format_ARGB32,
370 destFetchARGB32P, // Format_ARGB32_Premultiplied
371 destFetchRGB16, // Format_RGB16
372 destFetch, // Format_ARGB8565_Premultiplied
373 destFetch, // Format_RGB666
374 destFetch, // Format_ARGB6666_Premultiplied
375 destFetch, // Format_RGB555
376 destFetch, // Format_ARGB8555_Premultiplied
377 destFetch, // Format_RGB888
378 destFetch, // Format_RGB444
379 destFetch, // Format_ARGB4444_Premultiplied
380 destFetch, // Format_RGBX8888
381 destFetch, // Format_RGBA8888
382 destFetch, // Format_RGBA8888_Premultiplied
383 destFetch, // Format_BGR30
384 destFetch, // Format_A2BGR30_Premultiplied
385 destFetch, // Format_RGB30
386 destFetch, // Format_A2RGB30_Premultiplied
387 destFetch, // Format_Alpha8
388 destFetch, // Format_Grayscale8
389 destFetch, // Format_RGBX64
390 destFetch, // Format_RGBA64
391 destFetch, // Format_RGBA64_Premultiplied
392 destFetch, // Format_Grayscale16
393 destFetch, // Format_BGR888
394 destFetch, // Format_RGBX16FPx4
395 destFetch, // Format_RGBA16FPx4
396 destFetch, // Format_RGBA16FPx4_Premultiplied
397 destFetch, // Format_RGBX32FPx4
398 destFetch, // Format_RGBA32FPx4
399 destFetch, // Format_RGBA32FPx4_Premultiplied
400 destFetch, // Format_CMYK8888
401};
402
403static_assert(std::size(destFetchProc) == QImage::NImageFormats);
404
405#if QT_CONFIG(raster_64bit)
406static QRgba64 *QT_FASTCALL destFetch64(QRgba64 *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
407{
408 const QPixelLayout *layout = &qPixelLayouts[rasterBuffer->format];
409 return const_cast<QRgba64 *>(layout->fetchToRGBA64PM(buffer, rasterBuffer->scanLine(y), x, length, nullptr, nullptr));
410}
411
412static QRgba64 * QT_FASTCALL destFetchRGB64(QRgba64 *, QRasterBuffer *rasterBuffer, int x, int y, int)
413{
414 return (QRgba64 *)rasterBuffer->scanLine(y) + x;
415}
416
417static QRgba64 * QT_FASTCALL destFetch64Undefined(QRgba64 *buffer, QRasterBuffer *, int, int, int)
418{
419 return buffer;
420}
421
422static DestFetchProc64 destFetchProc64[] =
423{
424 nullptr, // Format_Invalid
425 nullptr, // Format_Mono,
426 nullptr, // Format_MonoLSB
427 nullptr, // Format_Indexed8
428 destFetch64, // Format_RGB32
429 destFetch64, // Format_ARGB32,
430 destFetch64, // Format_ARGB32_Premultiplied
431 destFetch64, // Format_RGB16
432 destFetch64, // Format_ARGB8565_Premultiplied
433 destFetch64, // Format_RGB666
434 destFetch64, // Format_ARGB6666_Premultiplied
435 destFetch64, // Format_RGB555
436 destFetch64, // Format_ARGB8555_Premultiplied
437 destFetch64, // Format_RGB888
438 destFetch64, // Format_RGB444
439 destFetch64, // Format_ARGB4444_Premultiplied
440 destFetch64, // Format_RGBX8888
441 destFetch64, // Format_RGBA8888
442 destFetch64, // Format_RGBA8888_Premultiplied
443 destFetch64, // Format_BGR30
444 destFetch64, // Format_A2BGR30_Premultiplied
445 destFetch64, // Format_RGB30
446 destFetch64, // Format_A2RGB30_Premultiplied
447 destFetch64, // Format_Alpha8
448 destFetch64, // Format_Grayscale8
449 destFetchRGB64, // Format_RGBX64
450 destFetch64, // Format_RGBA64
451 destFetchRGB64, // Format_RGBA64_Premultiplied
452 destFetch64, // Format_Grayscale16
453 destFetch64, // Format_BGR888
454 destFetch64, // Format_RGBX16FPx4
455 destFetch64, // Format_RGBA16FPx4
456 destFetch64, // Format_RGBA16FPx4_Premultiplied
457 destFetch64, // Format_RGBX32FPx4
458 destFetch64, // Format_RGBA32FPx4
459 destFetch64, // Format_RGBA32FPx4_Premultiplied
460 destFetch64, // Format_CMYK8888
461};
462
463static_assert(std::size(destFetchProc64) == QImage::NImageFormats);
464#endif
465
466#if QT_CONFIG(raster_fp)
467static QRgbaFloat32 *QT_FASTCALL destFetchFP(QRgbaFloat32 *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
468{
469 return const_cast<QRgbaFloat32 *>(qFetchToRGBA32F[rasterBuffer->format](buffer, rasterBuffer->scanLine(y), x, length, nullptr, nullptr));
470}
471
472static QRgbaFloat32 *QT_FASTCALL destFetchRGBFP(QRgbaFloat32 *, QRasterBuffer *rasterBuffer, int x, int y, int)
473{
474 return reinterpret_cast<QRgbaFloat32 *>(rasterBuffer->scanLine(y)) + x;
475}
476
477static QRgbaFloat32 *QT_FASTCALL destFetchFPUndefined(QRgbaFloat32 *buffer, QRasterBuffer *, int, int, int)
478{
479 return buffer;
480}
481static DestFetchProcFP destFetchProcFP[] =
482{
483 nullptr, // Format_Invalid
484 nullptr, // Format_Mono,
485 nullptr, // Format_MonoLSB
486 nullptr, // Format_Indexed8
487 destFetchFP, // Format_RGB32
488 destFetchFP, // Format_ARGB32,
489 destFetchFP, // Format_ARGB32_Premultiplied
490 destFetchFP, // Format_RGB16
491 destFetchFP, // Format_ARGB8565_Premultiplied
492 destFetchFP, // Format_RGB666
493 destFetchFP, // Format_ARGB6666_Premultiplied
494 destFetchFP, // Format_RGB555
495 destFetchFP, // Format_ARGB8555_Premultiplied
496 destFetchFP, // Format_RGB888
497 destFetchFP, // Format_RGB444
498 destFetchFP, // Format_ARGB4444_Premultiplied
499 destFetchFP, // Format_RGBX8888
500 destFetchFP, // Format_RGBA8888
501 destFetchFP, // Format_RGBA8888_Premultiplied
502 destFetchFP, // Format_BGR30
503 destFetchFP, // Format_A2BGR30_Premultiplied
504 destFetchFP, // Format_RGB30
505 destFetchFP, // Format_A2RGB30_Premultiplied
506 destFetchFP, // Format_Alpha8
507 destFetchFP, // Format_Grayscale8
508 destFetchFP, // Format_RGBX64
509 destFetchFP, // Format_RGBA64
510 destFetchFP, // Format_RGBA64_Premultiplied
511 destFetchFP, // Format_Grayscale16
512 destFetchFP, // Format_BGR888
513 destFetchFP, // Format_RGBX16FPx4
514 destFetchFP, // Format_RGBA16FPx4
515 destFetchFP, // Format_RGBA16FPx4_Premultiplied
516 destFetchRGBFP, // Format_RGBX32FPx4
517 destFetchFP, // Format_RGBA32FPx4
518 destFetchRGBFP, // Format_RGBA32FPx4_Premultiplied
519 destFetchFP, // Format_CMYK8888
520};
521
522static_assert(std::size(destFetchProcFP) == QImage::NImageFormats);
523#endif
524
525/*
526 Returns the color in the mono destination color table
527 that is the "nearest" to /color/.
528*/
529static inline QRgb findNearestColor(QRgb color, QRasterBuffer *rbuf)
530{
531 const QRgb color_0 = rbuf->destColor0;
532 const QRgb color_1 = rbuf->destColor1;
533
534 int r = qRed(rgb: color);
535 int g = qGreen(rgb: color);
536 int b = qBlue(rgb: color);
537 int rx, gx, bx;
538 int dist_0, dist_1;
539
540 rx = r - qRed(rgb: color_0);
541 gx = g - qGreen(rgb: color_0);
542 bx = b - qBlue(rgb: color_0);
543 dist_0 = rx*rx + gx*gx + bx*bx;
544
545 rx = r - qRed(rgb: color_1);
546 gx = g - qGreen(rgb: color_1);
547 bx = b - qBlue(rgb: color_1);
548 dist_1 = rx*rx + gx*gx + bx*bx;
549
550 if (dist_0 < dist_1)
551 return color_0;
552 return color_1;
553}
554
555/*
556 Destination store.
557*/
558
559static void QT_FASTCALL destStoreMono(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
560{
561 uchar *Q_DECL_RESTRICT data = (uchar *)rasterBuffer->scanLine(y);
562 if (rasterBuffer->monoDestinationWithClut) {
563 for (int i = 0; i < length; ++i) {
564 if (buffer[i] == rasterBuffer->destColor0) {
565 data[x >> 3] &= ~(0x80 >> (x & 7));
566 } else if (buffer[i] == rasterBuffer->destColor1) {
567 data[x >> 3] |= 0x80 >> (x & 7);
568 } else if (findNearestColor(color: buffer[i], rbuf: rasterBuffer) == rasterBuffer->destColor0) {
569 data[x >> 3] &= ~(0x80 >> (x & 7));
570 } else {
571 data[x >> 3] |= 0x80 >> (x & 7);
572 }
573 ++x;
574 }
575 } else {
576 for (int i = 0; i < length; ++i) {
577 if (qGray(rgb: buffer[i]) < int(qt_bayer_matrix[y & 15][x & 15]))
578 data[x >> 3] |= 0x80 >> (x & 7);
579 else
580 data[x >> 3] &= ~(0x80 >> (x & 7));
581 ++x;
582 }
583 }
584}
585
586static void QT_FASTCALL destStoreMonoLsb(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
587{
588 uchar *Q_DECL_RESTRICT data = (uchar *)rasterBuffer->scanLine(y);
589 if (rasterBuffer->monoDestinationWithClut) {
590 for (int i = 0; i < length; ++i) {
591 if (buffer[i] == rasterBuffer->destColor0) {
592 data[x >> 3] &= ~(1 << (x & 7));
593 } else if (buffer[i] == rasterBuffer->destColor1) {
594 data[x >> 3] |= 1 << (x & 7);
595 } else if (findNearestColor(color: buffer[i], rbuf: rasterBuffer) == rasterBuffer->destColor0) {
596 data[x >> 3] &= ~(1 << (x & 7));
597 } else {
598 data[x >> 3] |= 1 << (x & 7);
599 }
600 ++x;
601 }
602 } else {
603 for (int i = 0; i < length; ++i) {
604 if (qGray(rgb: buffer[i]) < int(qt_bayer_matrix[y & 15][x & 15]))
605 data[x >> 3] |= 1 << (x & 7);
606 else
607 data[x >> 3] &= ~(1 << (x & 7));
608 ++x;
609 }
610 }
611}
612
613static void QT_FASTCALL destStoreRGB16(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
614{
615 quint16 *data = (quint16*)rasterBuffer->scanLine(y) + x;
616 for (int i = 0; i < length; ++i)
617 data[i] = qConvertRgb32To16(c: buffer[i]);
618}
619
620static void QT_FASTCALL destStore(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
621{
622 const QPixelLayout *layout = &qPixelLayouts[rasterBuffer->format];
623 ConvertAndStorePixelsFunc store = layout->storeFromARGB32PM;
624 if (!layout->premultiplied && !layout->hasAlphaChannel)
625 store = layout->storeFromRGB32;
626 uchar *dest = rasterBuffer->scanLine(y);
627 store(dest, buffer, x, length, nullptr, nullptr);
628}
629
630static void QT_FASTCALL destStoreGray8(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
631{
632 uchar *data = rasterBuffer->scanLine(y) + x;
633
634 bool failed = false;
635 for (int k = 0; k < length; ++k) {
636 if (!qIsGray(rgb: buffer[k])) {
637 failed = true;
638 break;
639 }
640 data[k] = qRed(rgb: buffer[k]);
641 }
642 if (failed) { // Non-gray colors
643 QColorSpace fromCS = rasterBuffer->colorSpace.isValid() ? rasterBuffer->colorSpace : QColorSpace::SRgb;
644 QColorTransform tf = QColorSpacePrivate::get(colorSpace&: fromCS)->transformationToXYZ();
645 QColorTransformPrivate *tfd = QColorTransformPrivate::get(q: tf);
646
647 tfd->apply(dst: data, src: buffer, count: length, flags: QColorTransformPrivate::InputPremultiplied);
648 }
649}
650
651static void QT_FASTCALL destStoreGray16(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
652{
653 quint16 *data = reinterpret_cast<quint16 *>(rasterBuffer->scanLine(y)) + x;
654
655 bool failed = false;
656 for (int k = 0; k < length; ++k) {
657 if (!qIsGray(rgb: buffer[k])) {
658 failed = true;
659 break;
660 }
661 data[k] = qRed(rgb: buffer[k]) * 257;
662 }
663 if (failed) { // Non-gray colors
664 QColorSpace fromCS = rasterBuffer->colorSpace.isValid() ? rasterBuffer->colorSpace : QColorSpace::SRgb;
665 QColorTransform tf = QColorSpacePrivate::get(colorSpace&: fromCS)->transformationToXYZ();
666 QColorTransformPrivate *tfd = QColorTransformPrivate::get(q: tf);
667
668 Q_DECL_UNINITIALIZED QRgba64 tmp_line[BufferSize];
669 for (int k = 0; k < length; ++k)
670 tmp_line[k] = QRgba64::fromArgb32(rgb: buffer[k]);
671 tfd->apply(dst: data, src: tmp_line, count: length, flags: QColorTransformPrivate::InputPremultiplied);
672 }
673}
674
675static DestStoreProc destStoreProc[] =
676{
677 nullptr, // Format_Invalid
678 destStoreMono, // Format_Mono,
679 destStoreMonoLsb, // Format_MonoLSB
680 nullptr, // Format_Indexed8
681 nullptr, // Format_RGB32
682 destStore, // Format_ARGB32,
683 nullptr, // Format_ARGB32_Premultiplied
684 destStoreRGB16, // Format_RGB16
685 destStore, // Format_ARGB8565_Premultiplied
686 destStore, // Format_RGB666
687 destStore, // Format_ARGB6666_Premultiplied
688 destStore, // Format_RGB555
689 destStore, // Format_ARGB8555_Premultiplied
690 destStore, // Format_RGB888
691 destStore, // Format_RGB444
692 destStore, // Format_ARGB4444_Premultiplied
693 destStore, // Format_RGBX8888
694 destStore, // Format_RGBA8888
695 destStore, // Format_RGBA8888_Premultiplied
696 destStore, // Format_BGR30
697 destStore, // Format_A2BGR30_Premultiplied
698 destStore, // Format_RGB30
699 destStore, // Format_A2RGB30_Premultiplied
700 destStore, // Format_Alpha8
701 destStoreGray8, // Format_Grayscale8
702 destStore, // Format_RGBX64
703 destStore, // Format_RGBA64
704 destStore, // Format_RGBA64_Premultiplied
705 destStoreGray16, // Format_Grayscale16
706 destStore, // Format_BGR888
707 destStore, // Format_RGBX16FPx4
708 destStore, // Format_RGBA16FPx4
709 destStore, // Format_RGBA16FPx4_Premultiplied
710 destStore, // Format_RGBX32FPx4
711 destStore, // Format_RGBA32FPx4
712 destStore, // Format_RGBA32FPx4_Premultiplied
713 destStore, // Format_CMYK8888
714};
715
716static_assert(std::size(destStoreProc) == QImage::NImageFormats);
717
718#if QT_CONFIG(raster_64bit)
719static void QT_FASTCALL destStore64(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length)
720{
721 auto store = qStoreFromRGBA64PM[rasterBuffer->format];
722 uchar *dest = rasterBuffer->scanLine(y);
723 store(dest, buffer, x, length, nullptr, nullptr);
724}
725
726static void QT_FASTCALL destStore64RGBA64(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length)
727{
728 QRgba64 *dest = reinterpret_cast<QRgba64*>(rasterBuffer->scanLine(y)) + x;
729 for (int i = 0; i < length; ++i) {
730 dest[i] = buffer[i].unpremultiplied();
731 }
732}
733
734static void QT_FASTCALL destStore64Gray8(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length)
735{
736 uchar *data = rasterBuffer->scanLine(y) + x;
737
738 bool failed = false;
739 for (int k = 0; k < length; ++k) {
740 if (buffer[k].red() != buffer[k].green() || buffer[k].red() != buffer[k].blue()) {
741 failed = true;
742 break;
743 }
744 data[k] = buffer[k].red8();
745 }
746 if (failed) { // Non-gray colors
747 QColorSpace fromCS = rasterBuffer->colorSpace.isValid() ? rasterBuffer->colorSpace : QColorSpace::SRgb;
748 QColorTransform tf = QColorSpacePrivate::get(colorSpace&: fromCS)->transformationToXYZ();
749 QColorTransformPrivate *tfd = QColorTransformPrivate::get(q: tf);
750
751 Q_DECL_UNINITIALIZED quint16 gray_line[BufferSize];
752 tfd->apply(dst: gray_line, src: buffer, count: length, flags: QColorTransformPrivate::InputPremultiplied);
753 for (int k = 0; k < length; ++k)
754 data[k] = qt_div_257(x: gray_line[k]);
755 }
756}
757
758static void QT_FASTCALL destStore64Gray16(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length)
759{
760 quint16 *data = reinterpret_cast<quint16 *>(rasterBuffer->scanLine(y)) + x;
761
762 bool failed = false;
763 for (int k = 0; k < length; ++k) {
764 if (buffer[k].red() != buffer[k].green() || buffer[k].red() != buffer[k].blue()) {
765 failed = true;
766 break;
767 }
768 data[k] = buffer[k].red();
769 }
770 if (failed) { // Non-gray colors
771 QColorSpace fromCS = rasterBuffer->colorSpace.isValid() ? rasterBuffer->colorSpace : QColorSpace::SRgb;
772 QColorTransform tf = QColorSpacePrivate::get(colorSpace&: fromCS)->transformationToXYZ();
773 QColorTransformPrivate *tfd = QColorTransformPrivate::get(q: tf);
774 tfd->apply(dst: data, src: buffer, count: length, flags: QColorTransformPrivate::InputPremultiplied);
775 }
776}
777
778static DestStoreProc64 destStoreProc64[] =
779{
780 nullptr, // Format_Invalid
781 nullptr, // Format_Mono,
782 nullptr, // Format_MonoLSB
783 nullptr, // Format_Indexed8
784 destStore64, // Format_RGB32
785 destStore64, // Format_ARGB32,
786 destStore64, // Format_ARGB32_Premultiplied
787 destStore64, // Format_RGB16
788 destStore64, // Format_ARGB8565_Premultiplied
789 destStore64, // Format_RGB666
790 destStore64, // Format_ARGB6666_Premultiplied
791 destStore64, // Format_RGB555
792 destStore64, // Format_ARGB8555_Premultiplied
793 destStore64, // Format_RGB888
794 destStore64, // Format_RGB444
795 destStore64, // Format_ARGB4444_Premultiplied
796 destStore64, // Format_RGBX8888
797 destStore64, // Format_RGBA8888
798 destStore64, // Format_RGBA8888_Premultiplied
799 destStore64, // Format_BGR30
800 destStore64, // Format_A2BGR30_Premultiplied
801 destStore64, // Format_RGB30
802 destStore64, // Format_A2RGB30_Premultiplied
803 destStore64, // Format_Alpha8
804 destStore64Gray8, // Format_Grayscale8
805 nullptr, // Format_RGBX64
806 destStore64RGBA64, // Format_RGBA64
807 nullptr, // Format_RGBA64_Premultiplied
808 destStore64Gray16, // Format_Grayscale16
809 destStore64, // Format_BGR888
810 destStore64, // Format_RGBX16FPx4
811 destStore64, // Format_RGBA16FPx4
812 destStore64, // Format_RGBA16FPx4_Premultiplied
813 destStore64, // Format_RGBX32FPx4
814 destStore64, // Format_RGBA32FPx4
815 destStore64, // Format_RGBA32FPx4_Premultiplied
816 destStore64, // Format_CMYK8888
817};
818
819static_assert(std::size(destStoreProc64) == QImage::NImageFormats);
820#endif
821
822#if QT_CONFIG(raster_fp)
823static void QT_FASTCALL destStoreFP(QRasterBuffer *rasterBuffer, int x, int y, const QRgbaFloat32 *buffer, int length)
824{
825 auto store = qStoreFromRGBA32F[rasterBuffer->format];
826 uchar *dest = rasterBuffer->scanLine(y);
827 store(dest, buffer, x, length, nullptr, nullptr);
828}
829#endif
830
831/*
832 Source fetches
833
834 This is a bit more complicated, as we need several fetch routines for every surface type
835
836 We need 5 fetch methods per surface type:
837 untransformed
838 transformed (tiled and not tiled)
839 transformed bilinear (tiled and not tiled)
840
841 We don't need bounds checks for untransformed, but we need them for the other ones.
842
843 The generic implementation does pixel by pixel fetches
844*/
845
846enum TextureBlendType {
847 BlendUntransformed,
848 BlendTiled,
849 BlendTransformed,
850 BlendTransformedTiled,
851 BlendTransformedBilinear,
852 BlendTransformedBilinearTiled,
853 NBlendTypes
854};
855
856static const uint *QT_FASTCALL fetchUntransformed(uint *buffer, const Operator *,
857 const QSpanData *data, int y, int x, int length)
858{
859 const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
860 return layout->fetchToARGB32PM(buffer, data->texture.scanLine(y), x, length, data->texture.colorTable, nullptr);
861}
862
863static const uint *QT_FASTCALL fetchUntransformedARGB32PM(uint *, const Operator *,
864 const QSpanData *data, int y, int x, int)
865{
866 const uchar *scanLine = data->texture.scanLine(y);
867 return reinterpret_cast<const uint *>(scanLine) + x;
868}
869
870static const uint *QT_FASTCALL fetchUntransformedRGB16(uint *buffer, const Operator *,
871 const QSpanData *data, int y, int x,
872 int length)
873{
874 const quint16 *scanLine = (const quint16 *)data->texture.scanLine(y) + x;
875 for (int i = 0; i < length; ++i)
876 buffer[i] = qConvertRgb16To32(c: scanLine[i]);
877 return buffer;
878}
879
880#if QT_CONFIG(raster_64bit)
881static const QRgba64 *QT_FASTCALL fetchUntransformed64(QRgba64 *buffer, const Operator *,
882 const QSpanData *data, int y, int x, int length)
883{
884 const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
885 return layout->fetchToRGBA64PM(buffer, data->texture.scanLine(y), x, length, data->texture.colorTable, nullptr);
886}
887
888static const QRgba64 *QT_FASTCALL fetchUntransformedRGBA64PM(QRgba64 *, const Operator *,
889 const QSpanData *data, int y, int x, int)
890{
891 const uchar *scanLine = data->texture.scanLine(y);
892 return reinterpret_cast<const QRgba64 *>(scanLine) + x;
893}
894#endif
895
896#if QT_CONFIG(raster_fp)
897static const QRgbaFloat32 *QT_FASTCALL fetchUntransformedFP(QRgbaFloat32 *buffer, const Operator *,
898 const QSpanData *data, int y, int x, int length)
899{
900 const auto fetch = qFetchToRGBA32F[data->texture.format];
901 return fetch(buffer, data->texture.scanLine(y), x, length, data->texture.colorTable, nullptr);
902}
903#endif
904
905template<TextureBlendType blendType>
906inline void fetchTransformed_pixelBounds(int max, int l1, int l2, int &v)
907{
908 static_assert(blendType == BlendTransformed || blendType == BlendTransformedTiled);
909 if (blendType == BlendTransformedTiled) {
910 if (v < 0 || v >= max) {
911 v %= max;
912 if (v < 0) v += max;
913 }
914 } else {
915 v = qBound(min: l1, val: v, max: l2);
916 }
917}
918
919static inline bool canUseFastMatrixPath(const qreal cx, const qreal cy, const qsizetype length, const QSpanData *data)
920{
921 if (Q_UNLIKELY(!data->fast_matrix))
922 return false;
923
924 qreal fx = (data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale;
925 qreal fy = (data->m22 * cy + data->m12 * cx + data->dy) * fixed_scale;
926 qreal minc = std::min(a: fx, b: fy);
927 qreal maxc = std::max(a: fx, b: fy);
928 fx += std::trunc(x: data->m11 * fixed_scale) * length;
929 fy += std::trunc(x: data->m12 * fixed_scale) * length;
930 minc = std::min(a: minc, b: std::min(a: fx, b: fy));
931 maxc = std::max(a: maxc, b: std::max(a: fx, b: fy));
932
933 return minc >= std::numeric_limits<int>::min() && maxc <= std::numeric_limits<int>::max();
934}
935
936template<TextureBlendType blendType, QPixelLayout::BPP bpp, typename T>
937static void QT_FASTCALL fetchTransformed_fetcher(T *buffer, const QSpanData *data,
938 int y, int x, int length)
939{
940 static_assert(blendType == BlendTransformed || blendType == BlendTransformedTiled);
941 const QTextureData &image = data->texture;
942
943 const qreal cx = x + qreal(0.5);
944 const qreal cy = y + qreal(0.5);
945
946 constexpr bool useFetch = (bpp < QPixelLayout::BPP32) && sizeof(T) == sizeof(uint);
947 const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
948 if (!useFetch)
949 Q_ASSERT(layout->bpp == bpp || (layout->bpp == QPixelLayout::BPP16FPx4 && bpp == QPixelLayout::BPP64));
950 // When templated 'fetch' should be inlined at compile time:
951 const Fetch1PixelFunc fetch1 = (bpp == QPixelLayout::BPPNone) ? fetch1PixelTable[layout->bpp] : Fetch1PixelFunc(fetch1Pixel<bpp>);
952
953 if (canUseFastMatrixPath(cx, cy, length, data)) {
954 // The increment pr x in the scanline
955 int fdx = (int)(data->m11 * fixed_scale);
956 int fdy = (int)(data->m12 * fixed_scale);
957
958 int fx = int((data->m21 * cy
959 + data->m11 * cx + data->dx) * fixed_scale);
960 int fy = int((data->m22 * cy
961 + data->m12 * cx + data->dy) * fixed_scale);
962
963 if (fdy == 0) { // simple scale, no rotation or shear
964 int py = (fy >> 16);
965 fetchTransformed_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, py);
966 const uchar *src = image.scanLine(y: py);
967
968 int i = 0;
969 if (blendType == BlendTransformed) {
970 int fastLen = length;
971 if (fdx > 0)
972 fastLen = qMin(a: fastLen, b: int((qint64(image.x2 - 1) * fixed_scale - fx) / fdx));
973 else if (fdx < 0)
974 fastLen = qMin(a: fastLen, b: int((qint64(image.x1) * fixed_scale - fx) / fdx));
975
976 for (; i < fastLen; ++i) {
977 int x1 = (fx >> 16);
978 int x2 = x1;
979 fetchTransformed_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1);
980 if (x1 == x2)
981 break;
982 if constexpr (useFetch)
983 buffer[i] = fetch1(src, x1);
984 else
985 buffer[i] = reinterpret_cast<const T*>(src)[x1];
986 fx += fdx;
987 }
988
989 for (; i < fastLen; ++i) {
990 int px = (fx >> 16);
991 if constexpr (useFetch)
992 buffer[i] = fetch1(src, px);
993 else
994 buffer[i] = reinterpret_cast<const T*>(src)[px];
995 fx += fdx;
996 }
997 }
998
999 for (; i < length; ++i) {
1000 int px = (fx >> 16);
1001 fetchTransformed_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, px);
1002 if constexpr (useFetch)
1003 buffer[i] = fetch1(src, px);
1004 else
1005 buffer[i] = reinterpret_cast<const T*>(src)[px];
1006 fx += fdx;
1007 }
1008 } else { // rotation or shear
1009 int i = 0;
1010 if (blendType == BlendTransformed) {
1011 int fastLen = length;
1012 if (fdx > 0)
1013 fastLen = qMin(a: fastLen, b: int((qint64(image.x2 - 1) * fixed_scale - fx) / fdx));
1014 else if (fdx < 0)
1015 fastLen = qMin(a: fastLen, b: int((qint64(image.x1) * fixed_scale - fx) / fdx));
1016 if (fdy > 0)
1017 fastLen = qMin(a: fastLen, b: int((qint64(image.y2 - 1) * fixed_scale - fy) / fdy));
1018 else if (fdy < 0)
1019 fastLen = qMin(a: fastLen, b: int((qint64(image.y1) * fixed_scale - fy) / fdy));
1020
1021 for (; i < fastLen; ++i) {
1022 int x1 = (fx >> 16);
1023 int y1 = (fy >> 16);
1024 int x2 = x1;
1025 int y2 = y1;
1026 fetchTransformed_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1);
1027 fetchTransformed_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1);
1028 if (x1 == x2 && y1 == y2)
1029 break;
1030 if constexpr (useFetch)
1031 buffer[i] = fetch1(image.scanLine(y: y1), x1);
1032 else
1033 buffer[i] = reinterpret_cast<const T*>(image.scanLine(y: y1))[x1];
1034 fx += fdx;
1035 fy += fdy;
1036 }
1037
1038 for (; i < fastLen; ++i) {
1039 int px = (fx >> 16);
1040 int py = (fy >> 16);
1041 if constexpr (useFetch)
1042 buffer[i] = fetch1(image.scanLine(y: py), px);
1043 else
1044 buffer[i] = reinterpret_cast<const T*>(image.scanLine(y: py))[px];
1045 fx += fdx;
1046 fy += fdy;
1047 }
1048 }
1049
1050 for (; i < length; ++i) {
1051 int px = (fx >> 16);
1052 int py = (fy >> 16);
1053 fetchTransformed_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, px);
1054 fetchTransformed_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, py);
1055 if constexpr (useFetch)
1056 buffer[i] = fetch1(image.scanLine(y: py), px);
1057 else
1058 buffer[i] = reinterpret_cast<const T*>(image.scanLine(y: py))[px];
1059 fx += fdx;
1060 fy += fdy;
1061 }
1062 }
1063 } else {
1064 const qreal fdx = data->m11;
1065 const qreal fdy = data->m12;
1066 const qreal fdw = data->m13;
1067
1068 qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
1069 qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
1070 qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
1071
1072 T *const end = buffer + length;
1073 T *b = buffer;
1074 while (b < end) {
1075 const qreal iw = fw == 0 ? 1 : 1 / fw;
1076 const qreal tx = fx * iw;
1077 const qreal ty = fy * iw;
1078 int px = qFloor(v: tx);
1079 int py = qFloor(v: ty);
1080
1081 fetchTransformed_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, py);
1082 fetchTransformed_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, px);
1083 if constexpr (useFetch)
1084 *b = fetch1(image.scanLine(y: py), px);
1085 else
1086 *b = reinterpret_cast<const T*>(image.scanLine(y: py))[px];
1087
1088 fx += fdx;
1089 fy += fdy;
1090 fw += fdw;
1091 //force increment to avoid /0
1092 if (!fw) {
1093 fw += fdw;
1094 }
1095 ++b;
1096 }
1097 }
1098}
1099
1100template<TextureBlendType blendType, QPixelLayout::BPP bpp>
1101static const uint *QT_FASTCALL fetchTransformed(uint *buffer, const Operator *, const QSpanData *data,
1102 int y, int x, int length)
1103{
1104 static_assert(blendType == BlendTransformed || blendType == BlendTransformedTiled);
1105 const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
1106 fetchTransformed_fetcher<blendType, bpp, uint>(buffer, data, y, x, length);
1107 layout->convertToARGB32PM(buffer, length, data->texture.colorTable);
1108 return buffer;
1109}
1110
1111#if QT_CONFIG(raster_64bit)
1112template<TextureBlendType blendType> /* either BlendTransformed or BlendTransformedTiled */
1113static const QRgba64 *QT_FASTCALL fetchTransformed64(QRgba64 *buffer, const Operator *, const QSpanData *data,
1114 int y, int x, int length)
1115{
1116 const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
1117 if (layout->bpp < QPixelLayout::BPP64) {
1118 Q_DECL_UNINITIALIZED uint buffer32[BufferSize];
1119 Q_ASSERT(length <= BufferSize);
1120 if (layout->bpp == QPixelLayout::BPP32)
1121 fetchTransformed_fetcher<blendType, QPixelLayout::BPP32, uint>(buffer32, data, y, x, length);
1122 else
1123 fetchTransformed_fetcher<blendType, QPixelLayout::BPPNone, uint>(buffer32, data, y, x, length);
1124 return layout->convertToRGBA64PM(buffer, buffer32, length, data->texture.colorTable, nullptr);
1125 }
1126
1127 fetchTransformed_fetcher<blendType, QPixelLayout::BPP64, quint64>(reinterpret_cast<quint64*>(buffer), data, y, x, length);
1128 if (auto convert = convert64ToRGBA64PM[data->texture.format])
1129 convert(buffer, length);
1130 return buffer;
1131}
1132#endif
1133
1134#if QT_CONFIG(raster_fp)
1135template<TextureBlendType blendType> /* either BlendTransformed or BlendTransformedTiled */
1136static const QRgbaFloat32 *QT_FASTCALL fetchTransformedFP(QRgbaFloat32 *buffer, const Operator *, const QSpanData *data,
1137 int y, int x, int length)
1138{
1139 const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
1140 if (layout->bpp < QPixelLayout::BPP64) {
1141 Q_DECL_UNINITIALIZED uint buffer32[BufferSize];
1142 Q_ASSERT(length <= BufferSize);
1143 if (layout->bpp == QPixelLayout::BPP32)
1144 fetchTransformed_fetcher<blendType, QPixelLayout::BPP32, uint>(buffer32, data, y, x, length);
1145 else
1146 fetchTransformed_fetcher<blendType, QPixelLayout::BPPNone, uint>(buffer32, data, y, x, length);
1147 qConvertToRGBA32F[data->texture.format](buffer, buffer32, length, data->texture.colorTable, nullptr);
1148 } else if (layout->bpp < QPixelLayout::BPP32FPx4) {
1149 Q_DECL_UNINITIALIZED quint64 buffer64[BufferSize];
1150 fetchTransformed_fetcher<blendType, QPixelLayout::BPP64, quint64>(buffer64, data, y, x, length);
1151 convert64ToRGBA32F[data->texture.format](buffer, buffer64, length);
1152 } else {
1153 fetchTransformed_fetcher<blendType, QPixelLayout::BPP32FPx4, QRgbaFloat32>(buffer, data, y, x, length);
1154 if (data->texture.format == QImage::Format_RGBA32FPx4)
1155 convertRGBA32FToRGBA32FPM(buffer, count: length);
1156 return buffer;
1157 }
1158 return buffer;
1159}
1160#endif
1161
1162/** \internal
1163 interpolate 4 argb pixels with the distx and disty factor.
1164 distx and disty must be between 0 and 16
1165 */
1166static inline uint interpolate_4_pixels_16(uint tl, uint tr, uint bl, uint br, uint distx, uint disty)
1167{
1168 uint distxy = distx * disty;
1169 //idistx * disty = (16-distx) * disty = 16*disty - distxy
1170 //idistx * idisty = (16-distx) * (16-disty) = 16*16 - 16*distx -16*disty + distxy
1171 uint tlrb = (tl & 0x00ff00ff) * (16*16 - 16*distx - 16*disty + distxy);
1172 uint tlag = ((tl & 0xff00ff00) >> 8) * (16*16 - 16*distx - 16*disty + distxy);
1173 uint trrb = ((tr & 0x00ff00ff) * (distx*16 - distxy));
1174 uint trag = (((tr & 0xff00ff00) >> 8) * (distx*16 - distxy));
1175 uint blrb = ((bl & 0x00ff00ff) * (disty*16 - distxy));
1176 uint blag = (((bl & 0xff00ff00) >> 8) * (disty*16 - distxy));
1177 uint brrb = ((br & 0x00ff00ff) * (distxy));
1178 uint brag = (((br & 0xff00ff00) >> 8) * (distxy));
1179 return (((tlrb + trrb + blrb + brrb) >> 8) & 0x00ff00ff) | ((tlag + trag + blag + brag) & 0xff00ff00);
1180}
1181
1182#if defined(__SSE2__)
1183#define interpolate_4_pixels_16_sse2(tl, tr, bl, br, distx, disty, colorMask, v_256, b) \
1184{ \
1185 const __m128i dxdy = _mm_mullo_epi16 (distx, disty); \
1186 const __m128i distx_ = _mm_slli_epi16(distx, 4); \
1187 const __m128i disty_ = _mm_slli_epi16(disty, 4); \
1188 const __m128i idxidy = _mm_add_epi16(dxdy, _mm_sub_epi16(v_256, _mm_add_epi16(distx_, disty_))); \
1189 const __m128i dxidy = _mm_sub_epi16(distx_, dxdy); \
1190 const __m128i idxdy = _mm_sub_epi16(disty_, dxdy); \
1191 \
1192 __m128i tlAG = _mm_srli_epi16(tl, 8); \
1193 __m128i tlRB = _mm_and_si128(tl, colorMask); \
1194 __m128i trAG = _mm_srli_epi16(tr, 8); \
1195 __m128i trRB = _mm_and_si128(tr, colorMask); \
1196 __m128i blAG = _mm_srli_epi16(bl, 8); \
1197 __m128i blRB = _mm_and_si128(bl, colorMask); \
1198 __m128i brAG = _mm_srli_epi16(br, 8); \
1199 __m128i brRB = _mm_and_si128(br, colorMask); \
1200 \
1201 tlAG = _mm_mullo_epi16(tlAG, idxidy); \
1202 tlRB = _mm_mullo_epi16(tlRB, idxidy); \
1203 trAG = _mm_mullo_epi16(trAG, dxidy); \
1204 trRB = _mm_mullo_epi16(trRB, dxidy); \
1205 blAG = _mm_mullo_epi16(blAG, idxdy); \
1206 blRB = _mm_mullo_epi16(blRB, idxdy); \
1207 brAG = _mm_mullo_epi16(brAG, dxdy); \
1208 brRB = _mm_mullo_epi16(brRB, dxdy); \
1209 \
1210 /* Add the values, and shift to only keep 8 significant bits per colors */ \
1211 __m128i rAG =_mm_add_epi16(_mm_add_epi16(tlAG, trAG), _mm_add_epi16(blAG, brAG)); \
1212 __m128i rRB =_mm_add_epi16(_mm_add_epi16(tlRB, trRB), _mm_add_epi16(blRB, brRB)); \
1213 rAG = _mm_andnot_si128(colorMask, rAG); \
1214 rRB = _mm_srli_epi16(rRB, 8); \
1215 _mm_storeu_si128((__m128i*)(b), _mm_or_si128(rAG, rRB)); \
1216}
1217#endif
1218
1219#if defined(__ARM_NEON__)
1220#define interpolate_4_pixels_16_neon(tl, tr, bl, br, distx, disty, disty_, colorMask, invColorMask, v_256, b) \
1221{ \
1222 const int16x8_t dxdy = vmulq_s16(distx, disty); \
1223 const int16x8_t distx_ = vshlq_n_s16(distx, 4); \
1224 const int16x8_t idxidy = vaddq_s16(dxdy, vsubq_s16(v_256, vaddq_s16(distx_, disty_))); \
1225 const int16x8_t dxidy = vsubq_s16(distx_, dxdy); \
1226 const int16x8_t idxdy = vsubq_s16(disty_, dxdy); \
1227 \
1228 int16x8_t tlAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(tl), 8)); \
1229 int16x8_t tlRB = vandq_s16(tl, colorMask); \
1230 int16x8_t trAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(tr), 8)); \
1231 int16x8_t trRB = vandq_s16(tr, colorMask); \
1232 int16x8_t blAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(bl), 8)); \
1233 int16x8_t blRB = vandq_s16(bl, colorMask); \
1234 int16x8_t brAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(br), 8)); \
1235 int16x8_t brRB = vandq_s16(br, colorMask); \
1236 \
1237 int16x8_t rAG = vmulq_s16(tlAG, idxidy); \
1238 int16x8_t rRB = vmulq_s16(tlRB, idxidy); \
1239 rAG = vmlaq_s16(rAG, trAG, dxidy); \
1240 rRB = vmlaq_s16(rRB, trRB, dxidy); \
1241 rAG = vmlaq_s16(rAG, blAG, idxdy); \
1242 rRB = vmlaq_s16(rRB, blRB, idxdy); \
1243 rAG = vmlaq_s16(rAG, brAG, dxdy); \
1244 rRB = vmlaq_s16(rRB, brRB, dxdy); \
1245 \
1246 rAG = vandq_s16(invColorMask, rAG); \
1247 rRB = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(rRB), 8)); \
1248 vst1q_s16((int16_t*)(b), vorrq_s16(rAG, rRB)); \
1249}
1250#endif
1251
1252#if defined(__loongarch_sx)
1253static inline void interpolate_4_pixels_16_lsx(__m128i tl, __m128i tr, __m128i bl, __m128i br,
1254 __m128i distx, __m128i disty, uint *b)
1255{
1256 const __m128i colorMask = __lsx_vreplgr2vr_w(0x00ff00ff);
1257 const __m128i v_256 = __lsx_vreplgr2vr_h(256);
1258 const __m128i dxdy = __lsx_vmul_h(distx, disty);
1259 const __m128i distx_ = __lsx_vslli_h(distx, 4);
1260 const __m128i disty_ = __lsx_vslli_h(disty, 4);
1261 const __m128i idxidy = __lsx_vadd_h(dxdy, __lsx_vsub_h(v_256, __lsx_vadd_h(distx_, disty_)));
1262 const __m128i dxidy = __lsx_vsub_h(distx_, dxdy);
1263 const __m128i idxdy = __lsx_vsub_h(disty_,dxdy);
1264
1265 __m128i tlAG = __lsx_vsrli_h(tl, 8);
1266 __m128i tlRB = __lsx_vand_v(tl, colorMask);
1267 __m128i trAG = __lsx_vsrli_h(tr, 8);
1268 __m128i trRB = __lsx_vand_v(tr, colorMask);
1269 __m128i blAG = __lsx_vsrli_h(bl, 8);
1270 __m128i blRB = __lsx_vand_v(bl, colorMask);
1271 __m128i brAG = __lsx_vsrli_h(br, 8);
1272 __m128i brRB = __lsx_vand_v(br, colorMask);
1273
1274 tlAG = __lsx_vmul_h(tlAG, idxidy);
1275 tlRB = __lsx_vmul_h(tlRB, idxidy);
1276 trAG = __lsx_vmul_h(trAG, dxidy);
1277 trRB = __lsx_vmul_h(trRB, dxidy);
1278 blAG = __lsx_vmul_h(blAG, idxdy);
1279 blRB = __lsx_vmul_h(blRB, idxdy);
1280 brAG = __lsx_vmul_h(brAG, dxdy);
1281 brRB = __lsx_vmul_h(brRB, dxdy);
1282
1283 __m128i rAG =__lsx_vadd_h(__lsx_vadd_h(tlAG, trAG), __lsx_vadd_h(blAG, brAG));
1284 __m128i rRB =__lsx_vadd_h(__lsx_vadd_h(tlRB, trRB), __lsx_vadd_h(blRB, brRB));
1285 rAG = __lsx_vandn_v(colorMask, rAG);
1286 rRB = __lsx_vsrli_h(rRB, 8);
1287 __lsx_vst(__lsx_vor_v(rAG, rRB), b, 0);
1288}
1289#endif
1290
1291template<TextureBlendType blendType>
1292void fetchTransformedBilinear_pixelBounds(int max, int l1, int l2, int &v1, int &v2);
1293
1294template<>
1295inline void fetchTransformedBilinear_pixelBounds<BlendTransformedBilinearTiled>(int max, int, int, int &v1, int &v2)
1296{
1297 v1 %= max;
1298 if (v1 < 0)
1299 v1 += max;
1300 v2 = v1 + 1;
1301 if (v2 == max)
1302 v2 = 0;
1303 Q_ASSERT(v1 >= 0 && v1 < max);
1304 Q_ASSERT(v2 >= 0 && v2 < max);
1305}
1306
1307template<>
1308inline void fetchTransformedBilinear_pixelBounds<BlendTransformedBilinear>(int, int l1, int l2, int &v1, int &v2)
1309{
1310 if (v1 < l1)
1311 v2 = v1 = l1;
1312 else if (v1 >= l2)
1313 v2 = v1 = l2;
1314 else
1315 v2 = v1 + 1;
1316 Q_ASSERT(v1 >= l1 && v1 <= l2);
1317 Q_ASSERT(v2 >= l1 && v2 <= l2);
1318}
1319
1320enum FastTransformTypes {
1321 SimpleScaleTransform,
1322 UpscaleTransform,
1323 DownscaleTransform,
1324 RotateTransform,
1325 FastRotateTransform,
1326 NFastTransformTypes
1327};
1328
1329// Completes the partial interpolation stored in IntermediateBuffer.
1330// by performing the x-axis interpolation and joining the RB and AG buffers.
1331static void QT_FASTCALL intermediate_adder(uint *b, uint *end, const IntermediateBuffer &intermediate, int offset, int &fx, int fdx)
1332{
1333#if defined(QT_COMPILER_SUPPORTS_AVX2)
1334 extern void QT_FASTCALL intermediate_adder_avx2(uint *b, uint *end, const IntermediateBuffer &intermediate, int offset, int &fx, int fdx);
1335 if (qCpuHasFeature(ArchHaswell))
1336 return intermediate_adder_avx2(b, end, intermediate, offset, fx, fdx);
1337#endif
1338
1339 // Switch to intermediate buffer coordinates
1340 fx -= offset * fixed_scale;
1341
1342 while (b < end) {
1343 const int x = (fx >> 16);
1344
1345 const uint distx = (fx & 0x0000ffff) >> 8;
1346 const uint idistx = 256 - distx;
1347 const uint rb = (intermediate.buffer_rb[x] * idistx + intermediate.buffer_rb[x + 1] * distx) & 0xff00ff00;
1348 const uint ag = (intermediate.buffer_ag[x] * idistx + intermediate.buffer_ag[x + 1] * distx) & 0xff00ff00;
1349 *b = (rb >> 8) | ag;
1350 b++;
1351 fx += fdx;
1352 }
1353 fx += offset * fixed_scale;
1354}
1355
1356typedef void (QT_FASTCALL *BilinearFastTransformHelper)(uint *b, uint *end, const QTextureData &image, int &fx, int &fy, int fdx, int fdy);
1357
1358template<TextureBlendType blendType>
1359static void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_scale_helper(uint *b, uint *end, const QTextureData &image,
1360 int &fx, int &fy, int fdx, int /*fdy*/)
1361{
1362 int y1 = (fy >> 16);
1363 int y2;
1364 fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
1365 const uint *s1 = (const uint *)image.scanLine(y: y1);
1366 const uint *s2 = (const uint *)image.scanLine(y: y2);
1367
1368 const int disty = (fy & 0x0000ffff) >> 8;
1369 const int idisty = 256 - disty;
1370 const int length = end - b;
1371
1372 // The intermediate buffer is generated in the positive direction
1373 const int adjust = (fdx < 0) ? fdx * length : 0;
1374 const int offset = (fx + adjust) >> 16;
1375 int x = offset;
1376
1377 Q_DECL_UNINITIALIZED IntermediateBuffer intermediate;
1378 // count is the size used in the intermediate.buffer.
1379 int count = (qint64(length) * qAbs(t: fdx) + fixed_scale - 1) / fixed_scale + 2;
1380 // length is supposed to be <= BufferSize either because data->m11 < 1 or
1381 // data->m11 < 2, and any larger buffers split
1382 Q_ASSERT(count <= BufferSize + 2);
1383 int f = 0;
1384 int lim = count;
1385 if (blendType == BlendTransformedBilinearTiled) {
1386 x %= image.width;
1387 if (x < 0) x += image.width;
1388 } else {
1389 lim = qMin(a: count, b: image.x2 - x);
1390 if (x < image.x1) {
1391 Q_ASSERT(x < image.x2);
1392 uint t = s1[image.x1];
1393 uint b = s2[image.x1];
1394 quint32 rb = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff;
1395 quint32 ag = ((((t>>8) & 0xff00ff) * idisty + ((b>>8) & 0xff00ff) * disty) >> 8) & 0xff00ff;
1396 do {
1397 intermediate.buffer_rb[f] = rb;
1398 intermediate.buffer_ag[f] = ag;
1399 f++;
1400 x++;
1401 } while (x < image.x1 && f < lim);
1402 }
1403 }
1404
1405 if (blendType != BlendTransformedBilinearTiled) {
1406#if defined(__SSE2__)
1407 const __m128i disty_ = _mm_set1_epi16(w: disty);
1408 const __m128i idisty_ = _mm_set1_epi16(w: idisty);
1409 const __m128i colorMask = _mm_set1_epi32(i: 0x00ff00ff);
1410
1411 lim -= 3;
1412 for (; f < lim; x += 4, f += 4) {
1413 // Load 4 pixels from s1, and split the alpha-green and red-blue component
1414 __m128i top = _mm_loadu_si128(p: (const __m128i*)((const uint *)(s1)+x));
1415 __m128i topAG = _mm_srli_epi16(a: top, count: 8);
1416 __m128i topRB = _mm_and_si128(a: top, b: colorMask);
1417 // Multiplies each color component by idisty
1418 topAG = _mm_mullo_epi16 (a: topAG, b: idisty_);
1419 topRB = _mm_mullo_epi16 (a: topRB, b: idisty_);
1420
1421 // Same for the s2 vector
1422 __m128i bottom = _mm_loadu_si128(p: (const __m128i*)((const uint *)(s2)+x));
1423 __m128i bottomAG = _mm_srli_epi16(a: bottom, count: 8);
1424 __m128i bottomRB = _mm_and_si128(a: bottom, b: colorMask);
1425 bottomAG = _mm_mullo_epi16 (a: bottomAG, b: disty_);
1426 bottomRB = _mm_mullo_epi16 (a: bottomRB, b: disty_);
1427
1428 // Add the values, and shift to only keep 8 significant bits per colors
1429 __m128i rAG =_mm_add_epi16(a: topAG, b: bottomAG);
1430 rAG = _mm_srli_epi16(a: rAG, count: 8);
1431 _mm_storeu_si128(p: (__m128i*)(&intermediate.buffer_ag[f]), b: rAG);
1432 __m128i rRB =_mm_add_epi16(a: topRB, b: bottomRB);
1433 rRB = _mm_srli_epi16(a: rRB, count: 8);
1434 _mm_storeu_si128(p: (__m128i*)(&intermediate.buffer_rb[f]), b: rRB);
1435 }
1436#elif defined(__ARM_NEON__)
1437 const int16x8_t disty_ = vdupq_n_s16(disty);
1438 const int16x8_t idisty_ = vdupq_n_s16(idisty);
1439 const int16x8_t colorMask = vdupq_n_s16(0x00ff);
1440
1441 lim -= 3;
1442 for (; f < lim; x += 4, f += 4) {
1443 // Load 4 pixels from s1, and split the alpha-green and red-blue component
1444 int16x8_t top = vld1q_s16((int16_t*)((const uint *)(s1)+x));
1445 int16x8_t topAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(top), 8));
1446 int16x8_t topRB = vandq_s16(top, colorMask);
1447 // Multiplies each color component by idisty
1448 topAG = vmulq_s16(topAG, idisty_);
1449 topRB = vmulq_s16(topRB, idisty_);
1450
1451 // Same for the s2 vector
1452 int16x8_t bottom = vld1q_s16((int16_t*)((const uint *)(s2)+x));
1453 int16x8_t bottomAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(bottom), 8));
1454 int16x8_t bottomRB = vandq_s16(bottom, colorMask);
1455 bottomAG = vmulq_s16(bottomAG, disty_);
1456 bottomRB = vmulq_s16(bottomRB, disty_);
1457
1458 // Add the values, and shift to only keep 8 significant bits per colors
1459 int16x8_t rAG = vaddq_s16(topAG, bottomAG);
1460 rAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(rAG), 8));
1461 vst1q_s16((int16_t*)(&intermediate.buffer_ag[f]), rAG);
1462 int16x8_t rRB = vaddq_s16(topRB, bottomRB);
1463 rRB = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(rRB), 8));
1464 vst1q_s16((int16_t*)(&intermediate.buffer_rb[f]), rRB);
1465 }
1466#elif defined(__loongarch_sx)
1467 const __m128i disty_ = __lsx_vreplgr2vr_h(disty);
1468 const __m128i idisty_ = __lsx_vreplgr2vr_h(idisty);
1469 const __m128i colorMask = __lsx_vreplgr2vr_w(0x00ff00ff);
1470
1471 lim -= 3;
1472 for (; f < lim; x += 4, f += 4) {
1473 // Load 4 pixels from s1, and split the alpha-green and red-blue component
1474 __m128i top = __lsx_vld((const __m128i*)((const uint *)(s1)+x), 0);
1475 __m128i topAG = __lsx_vsrli_h(top, 8);
1476 __m128i topRB = __lsx_vand_v(top, colorMask);
1477 // Multiplies each color component by idisty
1478 topAG = __lsx_vmul_h(topAG, idisty_);
1479 topRB = __lsx_vmul_h(topRB, idisty_);
1480
1481 // Same for the s2 vector
1482 __m128i bottom = __lsx_vld((const __m128i*)((const uint *)(s2)+x), 0);
1483 __m128i bottomAG = __lsx_vsrli_h(bottom, 8);
1484 __m128i bottomRB = __lsx_vand_v(bottom, colorMask);
1485 bottomAG = __lsx_vmul_h(bottomAG, disty_);
1486 bottomRB = __lsx_vmul_h(bottomRB, disty_);
1487
1488 // Add the values, and shift to only keep 8 significant bits per colors
1489 __m128i rAG = __lsx_vadd_h(topAG, bottomAG);
1490 rAG = __lsx_vsrli_h(rAG, 8);
1491 __lsx_vst(rAG, (__m128i*)(&intermediate.buffer_ag[f]), 0);
1492 __m128i rRB = __lsx_vadd_h(topRB, bottomRB);
1493 rRB = __lsx_vsrli_h(rRB, 8);
1494 __lsx_vst(rRB, (__m128i*)(&intermediate.buffer_rb[f]), 0);
1495 }
1496#endif
1497 }
1498 for (; f < count; f++) { // Same as above but without simd
1499 if (blendType == BlendTransformedBilinearTiled) {
1500 if (x >= image.width) x -= image.width;
1501 } else {
1502 x = qMin(a: x, b: image.x2 - 1);
1503 }
1504
1505 uint t = s1[x];
1506 uint b = s2[x];
1507
1508 intermediate.buffer_rb[f] = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff;
1509 intermediate.buffer_ag[f] = ((((t>>8) & 0xff00ff) * idisty + ((b>>8) & 0xff00ff) * disty) >> 8) & 0xff00ff;
1510 x++;
1511 }
1512
1513 // Now interpolate the values from the intermediate.buffer to get the final result.
1514 intermediate_adder(b, end, intermediate, offset, fx, fdx);
1515}
1516
1517template<TextureBlendType blendType>
1518static void QT_FASTCALL fetchTransformedBilinearARGB32PM_upscale_helper(uint *b, uint *end, const QTextureData &image,
1519 int &fx, int &fy, int fdx, int /*fdy*/)
1520{
1521 int y1 = (fy >> 16);
1522 int y2;
1523 fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
1524 const uint *s1 = (const uint *)image.scanLine(y: y1);
1525 const uint *s2 = (const uint *)image.scanLine(y: y2);
1526 const int disty = (fy & 0x0000ffff) >> 8;
1527
1528 if (blendType != BlendTransformedBilinearTiled) {
1529 const qint64 min_fx = qint64(image.x1) * fixed_scale;
1530 const qint64 max_fx = qint64(image.x2 - 1) * fixed_scale;
1531 while (b < end) {
1532 int x1 = (fx >> 16);
1533 int x2;
1534 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
1535 if (x1 != x2)
1536 break;
1537 uint top = s1[x1];
1538 uint bot = s2[x1];
1539 *b = INTERPOLATE_PIXEL_256(x: top, a: 256 - disty, y: bot, b: disty);
1540 fx += fdx;
1541 ++b;
1542 }
1543 uint *boundedEnd = end;
1544 if (fdx > 0)
1545 boundedEnd = qMin(a: boundedEnd, b: b + (max_fx - fx) / fdx);
1546 else if (fdx < 0)
1547 boundedEnd = qMin(a: boundedEnd, b: b + (min_fx - fx) / fdx);
1548
1549 // A fast middle part without boundary checks
1550 while (b < boundedEnd) {
1551 int x = (fx >> 16);
1552 int distx = (fx & 0x0000ffff) >> 8;
1553 *b = interpolate_4_pixels(t: s1 + x, b: s2 + x, distx, disty);
1554 fx += fdx;
1555 ++b;
1556 }
1557 }
1558
1559 while (b < end) {
1560 int x1 = (fx >> 16);
1561 int x2;
1562 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1 , x1, x2);
1563 uint tl = s1[x1];
1564 uint tr = s1[x2];
1565 uint bl = s2[x1];
1566 uint br = s2[x2];
1567 int distx = (fx & 0x0000ffff) >> 8;
1568 *b = interpolate_4_pixels(tl, tr, bl, br, distx, disty);
1569
1570 fx += fdx;
1571 ++b;
1572 }
1573}
1574
1575template<TextureBlendType blendType>
1576static void QT_FASTCALL fetchTransformedBilinearARGB32PM_downscale_helper(uint *b, uint *end, const QTextureData &image,
1577 int &fx, int &fy, int fdx, int /*fdy*/)
1578{
1579 int y1 = (fy >> 16);
1580 int y2;
1581 fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
1582 const uint *s1 = (const uint *)image.scanLine(y: y1);
1583 const uint *s2 = (const uint *)image.scanLine(y: y2);
1584 const int disty8 = (fy & 0x0000ffff) >> 8;
1585 const int disty4 = (disty8 + 0x08) >> 4;
1586
1587 if (blendType != BlendTransformedBilinearTiled) {
1588 const qint64 min_fx = qint64(image.x1) * fixed_scale;
1589 const qint64 max_fx = qint64(image.x2 - 1) * fixed_scale;
1590 while (b < end) {
1591 int x1 = (fx >> 16);
1592 int x2;
1593 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
1594 if (x1 != x2)
1595 break;
1596 uint top = s1[x1];
1597 uint bot = s2[x1];
1598 *b = INTERPOLATE_PIXEL_256(x: top, a: 256 - disty8, y: bot, b: disty8);
1599 fx += fdx;
1600 ++b;
1601 }
1602 uint *boundedEnd = end;
1603 if (fdx > 0)
1604 boundedEnd = qMin(a: boundedEnd, b: b + (max_fx - fx) / fdx);
1605 else if (fdx < 0)
1606 boundedEnd = qMin(a: boundedEnd, b: b + (min_fx - fx) / fdx);
1607 // A fast middle part without boundary checks
1608#if defined(__SSE2__)
1609 const __m128i colorMask = _mm_set1_epi32(i: 0x00ff00ff);
1610 const __m128i v_256 = _mm_set1_epi16(w: 256);
1611 const __m128i v_disty = _mm_set1_epi16(w: disty4);
1612 const __m128i v_fdx = _mm_set1_epi32(i: fdx*4);
1613 const __m128i v_fx_r = _mm_set1_epi32(i: 0x8);
1614 __m128i v_fx = _mm_setr_epi32(i0: fx, i1: fx + fdx, i2: fx + fdx + fdx, i3: fx + fdx + fdx + fdx);
1615
1616 while (b < boundedEnd - 3) {
1617 __m128i offset = _mm_srli_epi32(a: v_fx, count: 16);
1618 const int offset0 = _mm_cvtsi128_si32(a: offset); offset = _mm_srli_si128(offset, 4);
1619 const int offset1 = _mm_cvtsi128_si32(a: offset); offset = _mm_srli_si128(offset, 4);
1620 const int offset2 = _mm_cvtsi128_si32(a: offset); offset = _mm_srli_si128(offset, 4);
1621 const int offset3 = _mm_cvtsi128_si32(a: offset);
1622 const __m128i tl = _mm_setr_epi32(i0: s1[offset0], i1: s1[offset1], i2: s1[offset2], i3: s1[offset3]);
1623 const __m128i tr = _mm_setr_epi32(i0: s1[offset0 + 1], i1: s1[offset1 + 1], i2: s1[offset2 + 1], i3: s1[offset3 + 1]);
1624 const __m128i bl = _mm_setr_epi32(i0: s2[offset0], i1: s2[offset1], i2: s2[offset2], i3: s2[offset3]);
1625 const __m128i br = _mm_setr_epi32(i0: s2[offset0 + 1], i1: s2[offset1 + 1], i2: s2[offset2 + 1], i3: s2[offset3 + 1]);
1626
1627 __m128i v_distx = _mm_srli_epi16(a: v_fx, count: 8);
1628 v_distx = _mm_srli_epi16(a: _mm_add_epi32(a: v_distx, b: v_fx_r), count: 4);
1629 v_distx = _mm_shufflehi_epi16(v_distx, _MM_SHUFFLE(2,2,0,0));
1630 v_distx = _mm_shufflelo_epi16(v_distx, _MM_SHUFFLE(2,2,0,0));
1631
1632 interpolate_4_pixels_16_sse2(tl, tr, bl, br, v_distx, v_disty, colorMask, v_256, b);
1633 b += 4;
1634 v_fx = _mm_add_epi32(a: v_fx, b: v_fdx);
1635 }
1636 fx = _mm_cvtsi128_si32(a: v_fx);
1637#elif defined(__ARM_NEON__)
1638 const int16x8_t colorMask = vdupq_n_s16(0x00ff);
1639 const int16x8_t invColorMask = vmvnq_s16(colorMask);
1640 const int16x8_t v_256 = vdupq_n_s16(256);
1641 const int16x8_t v_disty = vdupq_n_s16(disty4);
1642 const int16x8_t v_disty_ = vshlq_n_s16(v_disty, 4);
1643 int32x4_t v_fdx = vdupq_n_s32(fdx*4);
1644
1645 int32x4_t v_fx = vmovq_n_s32(fx);
1646 v_fx = vsetq_lane_s32(fx + fdx, v_fx, 1);
1647 v_fx = vsetq_lane_s32(fx + fdx * 2, v_fx, 2);
1648 v_fx = vsetq_lane_s32(fx + fdx * 3, v_fx, 3);
1649
1650 const int32x4_t v_ffff_mask = vdupq_n_s32(0x0000ffff);
1651 const int32x4_t v_fx_r = vdupq_n_s32(0x0800);
1652
1653 // Pre-initialize to work-around code-analysis warnings/crashes in MSVC:
1654 uint32x4x2_t v_top = {};
1655 uint32x4x2_t v_bot = {};
1656 while (b < boundedEnd - 3) {
1657 int x1 = (fx >> 16);
1658 fx += fdx;
1659 v_top = vld2q_lane_u32(s1 + x1, v_top, 0);
1660 v_bot = vld2q_lane_u32(s2 + x1, v_bot, 0);
1661 x1 = (fx >> 16);
1662 fx += fdx;
1663 v_top = vld2q_lane_u32(s1 + x1, v_top, 1);
1664 v_bot = vld2q_lane_u32(s2 + x1, v_bot, 1);
1665 x1 = (fx >> 16);
1666 fx += fdx;
1667 v_top = vld2q_lane_u32(s1 + x1, v_top, 2);
1668 v_bot = vld2q_lane_u32(s2 + x1, v_bot, 2);
1669 x1 = (fx >> 16);
1670 fx += fdx;
1671 v_top = vld2q_lane_u32(s1 + x1, v_top, 3);
1672 v_bot = vld2q_lane_u32(s2 + x1, v_bot, 3);
1673
1674 int32x4_t v_distx = vshrq_n_s32(vaddq_s32(vandq_s32(v_fx, v_ffff_mask), v_fx_r), 12);
1675 v_distx = vorrq_s32(v_distx, vshlq_n_s32(v_distx, 16));
1676
1677 interpolate_4_pixels_16_neon(
1678 vreinterpretq_s16_u32(v_top.val[0]), vreinterpretq_s16_u32(v_top.val[1]),
1679 vreinterpretq_s16_u32(v_bot.val[0]), vreinterpretq_s16_u32(v_bot.val[1]),
1680 vreinterpretq_s16_s32(v_distx), v_disty, v_disty_,
1681 colorMask, invColorMask, v_256, b);
1682 b+=4;
1683 v_fx = vaddq_s32(v_fx, v_fdx);
1684 }
1685#elif defined (__loongarch_sx)
1686 const __m128i shuffleMask = (__m128i)(v8i16){0, 0, 2, 2, 4, 4, 6, 6};
1687 const __m128i v_disty = __lsx_vreplgr2vr_h(disty4);
1688 const __m128i v_fdx = __lsx_vreplgr2vr_w(fdx*4);
1689 const __m128i v_fx_r = __lsx_vreplgr2vr_w(0x8);
1690 __m128i v_fx = (__m128i)(v4i32){fx, fx + fdx, fx + fdx + fdx, fx + fdx + fdx + fdx};
1691
1692 while (b < boundedEnd - 3) {
1693 __m128i offset = __lsx_vsrli_w(v_fx, 16);
1694 const int offset0 = __lsx_vpickve2gr_w(offset, 0);
1695 const int offset1 = __lsx_vpickve2gr_w(offset, 1);
1696 const int offset2 = __lsx_vpickve2gr_w(offset, 2);
1697 const int offset3 = __lsx_vpickve2gr_w(offset, 3);
1698 const __m128i tl = (__m128i)(v4u32){s1[offset0], s1[offset1], s1[offset2], s1[offset3]};
1699 const __m128i tr = (__m128i)(v4u32){s1[offset0 + 1], s1[offset1 + 1], s1[offset2 + 1], s1[offset3 + 1]};
1700 const __m128i bl = (__m128i)(v4u32){s2[offset0], s2[offset1], s2[offset2], s2[offset3]};
1701 const __m128i br = (__m128i)(v4u32){s2[offset0 + 1], s2[offset1 + 1], s2[offset2 + 1], s2[offset3 + 1]};
1702
1703 __m128i v_distx = __lsx_vsrli_h(v_fx, 8);
1704 v_distx = __lsx_vsrli_h(__lsx_vadd_w(v_distx, v_fx_r), 4);
1705 v_distx = __lsx_vshuf_h(shuffleMask, v_distx, v_distx);
1706
1707 interpolate_4_pixels_16_lsx(tl, tr, bl, br, v_distx, v_disty, b);
1708 b += 4;
1709 v_fx = __lsx_vadd_w(v_fx, v_fdx);
1710 }
1711 fx = __lsx_vpickve2gr_w(v_fx, 0);
1712#endif
1713 while (b < boundedEnd) {
1714 int x = (fx >> 16);
1715 if (hasFastInterpolate4()) {
1716 int distx8 = (fx & 0x0000ffff) >> 8;
1717 *b = interpolate_4_pixels(t: s1 + x, b: s2 + x, distx: distx8, disty: disty8);
1718 } else {
1719 int distx4 = ((fx & 0x0000ffff) + 0x0800) >> 12;
1720 *b = interpolate_4_pixels_16(tl: s1[x], tr: s1[x + 1], bl: s2[x], br: s2[x + 1], distx: distx4, disty: disty4);
1721 }
1722 fx += fdx;
1723 ++b;
1724 }
1725 }
1726
1727 while (b < end) {
1728 int x1 = (fx >> 16);
1729 int x2;
1730 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
1731 uint tl = s1[x1];
1732 uint tr = s1[x2];
1733 uint bl = s2[x1];
1734 uint br = s2[x2];
1735 if (hasFastInterpolate4()) {
1736 int distx8 = (fx & 0x0000ffff) >> 8;
1737 *b = interpolate_4_pixels(tl, tr, bl, br, distx: distx8, disty: disty8);
1738 } else {
1739 int distx4 = ((fx & 0x0000ffff) + 0x0800) >> 12;
1740 *b = interpolate_4_pixels_16(tl, tr, bl, br, distx: distx4, disty: disty4);
1741 }
1742 fx += fdx;
1743 ++b;
1744 }
1745}
1746
1747template<TextureBlendType blendType>
1748static void QT_FASTCALL fetchTransformedBilinearARGB32PM_rotate_helper(uint *b, uint *end, const QTextureData &image,
1749 int &fx, int &fy, int fdx, int fdy)
1750{
1751 // if we are zooming more than 8 times, we use 8bit precision for the position.
1752 while (b < end) {
1753 int x1 = (fx >> 16);
1754 int x2;
1755 int y1 = (fy >> 16);
1756 int y2;
1757
1758 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
1759 fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
1760
1761 const uint *s1 = (const uint *)image.scanLine(y: y1);
1762 const uint *s2 = (const uint *)image.scanLine(y: y2);
1763
1764 uint tl = s1[x1];
1765 uint tr = s1[x2];
1766 uint bl = s2[x1];
1767 uint br = s2[x2];
1768
1769 int distx = (fx & 0x0000ffff) >> 8;
1770 int disty = (fy & 0x0000ffff) >> 8;
1771
1772 *b = interpolate_4_pixels(tl, tr, bl, br, distx, disty);
1773
1774 fx += fdx;
1775 fy += fdy;
1776 ++b;
1777 }
1778}
1779
1780template<TextureBlendType blendType>
1781static void QT_FASTCALL fetchTransformedBilinearARGB32PM_fast_rotate_helper(uint *b, uint *end, const QTextureData &image,
1782 int &fx, int &fy, int fdx, int fdy)
1783{
1784 //we are zooming less than 8x, use 4bit precision
1785 if (blendType != BlendTransformedBilinearTiled) {
1786 const qint64 min_fx = qint64(image.x1) * fixed_scale;
1787 const qint64 max_fx = qint64(image.x2 - 1) * fixed_scale;
1788 const qint64 min_fy = qint64(image.y1) * fixed_scale;
1789 const qint64 max_fy = qint64(image.y2 - 1) * fixed_scale;
1790 // first handle the possibly bounded part in the beginning
1791 while (b < end) {
1792 int x1 = (fx >> 16);
1793 int x2;
1794 int y1 = (fy >> 16);
1795 int y2;
1796 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
1797 fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
1798 if (x1 != x2 && y1 != y2)
1799 break;
1800 const uint *s1 = (const uint *)image.scanLine(y: y1);
1801 const uint *s2 = (const uint *)image.scanLine(y: y2);
1802 uint tl = s1[x1];
1803 uint tr = s1[x2];
1804 uint bl = s2[x1];
1805 uint br = s2[x2];
1806 if (hasFastInterpolate4()) {
1807 int distx = (fx & 0x0000ffff) >> 8;
1808 int disty = (fy & 0x0000ffff) >> 8;
1809 *b = interpolate_4_pixels(tl, tr, bl, br, distx, disty);
1810 } else {
1811 int distx = ((fx & 0x0000ffff) + 0x0800) >> 12;
1812 int disty = ((fy & 0x0000ffff) + 0x0800) >> 12;
1813 *b = interpolate_4_pixels_16(tl, tr, bl, br, distx, disty);
1814 }
1815 fx += fdx;
1816 fy += fdy;
1817 ++b;
1818 }
1819 uint *boundedEnd = end;
1820 if (fdx > 0)
1821 boundedEnd = qMin(a: boundedEnd, b: b + (max_fx - fx) / fdx);
1822 else if (fdx < 0)
1823 boundedEnd = qMin(a: boundedEnd, b: b + (min_fx - fx) / fdx);
1824 if (fdy > 0)
1825 boundedEnd = qMin(a: boundedEnd, b: b + (max_fy - fy) / fdy);
1826 else if (fdy < 0)
1827 boundedEnd = qMin(a: boundedEnd, b: b + (min_fy - fy) / fdy);
1828
1829 // until boundedEnd we can now have a fast middle part without boundary checks
1830#if defined(__SSE2__)
1831 const __m128i colorMask = _mm_set1_epi32(i: 0x00ff00ff);
1832 const __m128i v_256 = _mm_set1_epi16(w: 256);
1833 const __m128i v_fdx = _mm_set1_epi32(i: fdx*4);
1834 const __m128i v_fdy = _mm_set1_epi32(i: fdy*4);
1835 const __m128i v_fxy_r = _mm_set1_epi32(i: 0x8);
1836 __m128i v_fx = _mm_setr_epi32(i0: fx, i1: fx + fdx, i2: fx + fdx + fdx, i3: fx + fdx + fdx + fdx);
1837 __m128i v_fy = _mm_setr_epi32(i0: fy, i1: fy + fdy, i2: fy + fdy + fdy, i3: fy + fdy + fdy + fdy);
1838
1839 const uchar *textureData = image.imageData;
1840 const qsizetype bytesPerLine = image.bytesPerLine;
1841 const __m128i vbpl = _mm_shufflelo_epi16(_mm_cvtsi32_si128(bytesPerLine/4), _MM_SHUFFLE(0, 0, 0, 0));
1842
1843 while (b < boundedEnd - 3) {
1844 const __m128i vy = _mm_packs_epi32(a: _mm_srli_epi32(a: v_fy, count: 16), b: _mm_setzero_si128());
1845 // 4x16bit * 4x16bit -> 4x32bit
1846 __m128i offset = _mm_unpacklo_epi16(a: _mm_mullo_epi16(a: vy, b: vbpl), b: _mm_mulhi_epi16(a: vy, b: vbpl));
1847 offset = _mm_add_epi32(a: offset, b: _mm_srli_epi32(a: v_fx, count: 16));
1848 const int offset0 = _mm_cvtsi128_si32(a: offset); offset = _mm_srli_si128(offset, 4);
1849 const int offset1 = _mm_cvtsi128_si32(a: offset); offset = _mm_srli_si128(offset, 4);
1850 const int offset2 = _mm_cvtsi128_si32(a: offset); offset = _mm_srli_si128(offset, 4);
1851 const int offset3 = _mm_cvtsi128_si32(a: offset);
1852 const uint *topData = (const uint *)(textureData);
1853 const __m128i tl = _mm_setr_epi32(i0: topData[offset0], i1: topData[offset1], i2: topData[offset2], i3: topData[offset3]);
1854 const __m128i tr = _mm_setr_epi32(i0: topData[offset0 + 1], i1: topData[offset1 + 1], i2: topData[offset2 + 1], i3: topData[offset3 + 1]);
1855 const uint *bottomData = (const uint *)(textureData + bytesPerLine);
1856 const __m128i bl = _mm_setr_epi32(i0: bottomData[offset0], i1: bottomData[offset1], i2: bottomData[offset2], i3: bottomData[offset3]);
1857 const __m128i br = _mm_setr_epi32(i0: bottomData[offset0 + 1], i1: bottomData[offset1 + 1], i2: bottomData[offset2 + 1], i3: bottomData[offset3 + 1]);
1858
1859 __m128i v_distx = _mm_srli_epi16(a: v_fx, count: 8);
1860 __m128i v_disty = _mm_srli_epi16(a: v_fy, count: 8);
1861 v_distx = _mm_srli_epi16(a: _mm_add_epi32(a: v_distx, b: v_fxy_r), count: 4);
1862 v_disty = _mm_srli_epi16(a: _mm_add_epi32(a: v_disty, b: v_fxy_r), count: 4);
1863 v_distx = _mm_shufflehi_epi16(v_distx, _MM_SHUFFLE(2,2,0,0));
1864 v_distx = _mm_shufflelo_epi16(v_distx, _MM_SHUFFLE(2,2,0,0));
1865 v_disty = _mm_shufflehi_epi16(v_disty, _MM_SHUFFLE(2,2,0,0));
1866 v_disty = _mm_shufflelo_epi16(v_disty, _MM_SHUFFLE(2,2,0,0));
1867
1868 interpolate_4_pixels_16_sse2(tl, tr, bl, br, v_distx, v_disty, colorMask, v_256, b);
1869 b += 4;
1870 v_fx = _mm_add_epi32(a: v_fx, b: v_fdx);
1871 v_fy = _mm_add_epi32(a: v_fy, b: v_fdy);
1872 }
1873 fx = _mm_cvtsi128_si32(a: v_fx);
1874 fy = _mm_cvtsi128_si32(a: v_fy);
1875#elif defined(__ARM_NEON__)
1876 const int16x8_t colorMask = vdupq_n_s16(0x00ff);
1877 const int16x8_t invColorMask = vmvnq_s16(colorMask);
1878 const int16x8_t v_256 = vdupq_n_s16(256);
1879 int32x4_t v_fdx = vdupq_n_s32(fdx * 4);
1880 int32x4_t v_fdy = vdupq_n_s32(fdy * 4);
1881
1882 const uchar *textureData = image.imageData;
1883 const qsizetype bytesPerLine = image.bytesPerLine;
1884
1885 int32x4_t v_fx = vmovq_n_s32(fx);
1886 int32x4_t v_fy = vmovq_n_s32(fy);
1887 v_fx = vsetq_lane_s32(fx + fdx, v_fx, 1);
1888 v_fy = vsetq_lane_s32(fy + fdy, v_fy, 1);
1889 v_fx = vsetq_lane_s32(fx + fdx * 2, v_fx, 2);
1890 v_fy = vsetq_lane_s32(fy + fdy * 2, v_fy, 2);
1891 v_fx = vsetq_lane_s32(fx + fdx * 3, v_fx, 3);
1892 v_fy = vsetq_lane_s32(fy + fdy * 3, v_fy, 3);
1893
1894 const int32x4_t v_ffff_mask = vdupq_n_s32(0x0000ffff);
1895 const int32x4_t v_round = vdupq_n_s32(0x0800);
1896
1897 // Pre-initialize to work-around code-analysis warnings/crashes in MSVC:
1898 uint32x4x2_t v_top = {};
1899 uint32x4x2_t v_bot = {};
1900 while (b < boundedEnd - 3) {
1901 int x1 = (fx >> 16);
1902 int y1 = (fy >> 16);
1903 fx += fdx; fy += fdy;
1904 const uchar *sl = textureData + bytesPerLine * y1;
1905 const uint *s1 = reinterpret_cast<const uint *>(sl);
1906 const uint *s2 = reinterpret_cast<const uint *>(sl + bytesPerLine);
1907 v_top = vld2q_lane_u32(s1 + x1, v_top, 0);
1908 v_bot = vld2q_lane_u32(s2 + x1, v_bot, 0);
1909 x1 = (fx >> 16);
1910 y1 = (fy >> 16);
1911 fx += fdx; fy += fdy;
1912 sl = textureData + bytesPerLine * y1;
1913 s1 = reinterpret_cast<const uint *>(sl);
1914 s2 = reinterpret_cast<const uint *>(sl + bytesPerLine);
1915 v_top = vld2q_lane_u32(s1 + x1, v_top, 1);
1916 v_bot = vld2q_lane_u32(s2 + x1, v_bot, 1);
1917 x1 = (fx >> 16);
1918 y1 = (fy >> 16);
1919 fx += fdx; fy += fdy;
1920 sl = textureData + bytesPerLine * y1;
1921 s1 = reinterpret_cast<const uint *>(sl);
1922 s2 = reinterpret_cast<const uint *>(sl + bytesPerLine);
1923 v_top = vld2q_lane_u32(s1 + x1, v_top, 2);
1924 v_bot = vld2q_lane_u32(s2 + x1, v_bot, 2);
1925 x1 = (fx >> 16);
1926 y1 = (fy >> 16);
1927 fx += fdx; fy += fdy;
1928 sl = textureData + bytesPerLine * y1;
1929 s1 = reinterpret_cast<const uint *>(sl);
1930 s2 = reinterpret_cast<const uint *>(sl + bytesPerLine);
1931 v_top = vld2q_lane_u32(s1 + x1, v_top, 3);
1932 v_bot = vld2q_lane_u32(s2 + x1, v_bot, 3);
1933
1934 int32x4_t v_distx = vshrq_n_s32(vaddq_s32(vandq_s32(v_fx, v_ffff_mask), v_round), 12);
1935 int32x4_t v_disty = vshrq_n_s32(vaddq_s32(vandq_s32(v_fy, v_ffff_mask), v_round), 12);
1936 v_distx = vorrq_s32(v_distx, vshlq_n_s32(v_distx, 16));
1937 v_disty = vorrq_s32(v_disty, vshlq_n_s32(v_disty, 16));
1938 int16x8_t v_disty_ = vshlq_n_s16(vreinterpretq_s16_s32(v_disty), 4);
1939
1940 interpolate_4_pixels_16_neon(
1941 vreinterpretq_s16_u32(v_top.val[0]), vreinterpretq_s16_u32(v_top.val[1]),
1942 vreinterpretq_s16_u32(v_bot.val[0]), vreinterpretq_s16_u32(v_bot.val[1]),
1943 vreinterpretq_s16_s32(v_distx), vreinterpretq_s16_s32(v_disty),
1944 v_disty_, colorMask, invColorMask, v_256, b);
1945 b += 4;
1946 v_fx = vaddq_s32(v_fx, v_fdx);
1947 v_fy = vaddq_s32(v_fy, v_fdy);
1948 }
1949#elif defined(__loongarch_sx)
1950 const __m128i v_fdx = __lsx_vreplgr2vr_w(fdx*4);
1951 const __m128i v_fdy = __lsx_vreplgr2vr_w(fdy*4);
1952 const __m128i v_fxy_r = __lsx_vreplgr2vr_w(0x8);
1953 __m128i v_fx = (__m128i)(v4i32){fx, fx + fdx, fx + fdx + fdx, fx + fdx + fdx + fdx};
1954 __m128i v_fy = (__m128i)(v4i32){fy, fy + fdy, fy + fdy + fdy, fy + fdy + fdy + fdy};
1955
1956 const uchar *textureData = image.imageData;
1957 const qsizetype bytesPerLine = image.bytesPerLine;
1958 const __m128i zero = __lsx_vldi(0);
1959 const __m128i shuffleMask = (__m128i)(v8i16){0, 0, 0, 0, 4, 5, 6, 7};
1960 const __m128i shuffleMask1 = (__m128i)(v8i16){0, 0, 2, 2, 4, 4, 6, 6};
1961 const __m128i vbpl = __lsx_vshuf_h(shuffleMask, zero, __lsx_vinsgr2vr_w(zero, bytesPerLine/4, 0));
1962
1963 while (b < boundedEnd - 3) {
1964 const __m128i vy = __lsx_vpickev_h(zero, __lsx_vsat_w(__lsx_vsrli_w(v_fy, 16), 15));
1965 // 4x16bit * 4x16bit -> 4x32bit
1966 __m128i offset = __lsx_vilvl_h(__lsx_vmuh_h(vy, vbpl), __lsx_vmul_h(vy, vbpl));
1967 offset = __lsx_vadd_w(offset, __lsx_vsrli_w(v_fx, 16));
1968 const int offset0 = __lsx_vpickve2gr_w(offset, 0);
1969 const int offset1 = __lsx_vpickve2gr_w(offset, 1);
1970 const int offset2 = __lsx_vpickve2gr_w(offset, 2);
1971 const int offset3 = __lsx_vpickve2gr_w(offset, 3);
1972 const uint *topData = (const uint *)(textureData);
1973 const __m128i tl = (__m128i)(v4u32){topData[offset0], topData[offset1], topData[offset2], topData[offset3]};
1974 const __m128i tr = (__m128i)(v4u32){topData[offset0 + 1], topData[offset1 + 1], topData[offset2 + 1], topData[offset3 + 1]};
1975 const uint *bottomData = (const uint *)(textureData + bytesPerLine);
1976 const __m128i bl = (__m128i)(v4u32){bottomData[offset0], bottomData[offset1], bottomData[offset2], bottomData[offset3]};
1977 const __m128i br = (__m128i)(v4u32){bottomData[offset0 + 1], bottomData[offset1 + 1], bottomData[offset2 + 1], bottomData[offset3 + 1]};
1978
1979 __m128i v_distx = __lsx_vsrli_h(v_fx, 8);
1980 __m128i v_disty = __lsx_vsrli_h(v_fy, 8);
1981 v_distx = __lsx_vsrli_h(__lsx_vadd_w(v_distx, v_fxy_r), 4);
1982 v_disty = __lsx_vsrli_h(__lsx_vadd_w(v_disty, v_fxy_r), 4);
1983 v_distx = __lsx_vshuf_h(shuffleMask1, zero, v_distx);
1984 v_disty = __lsx_vshuf_h(shuffleMask1, zero, v_disty);
1985
1986 interpolate_4_pixels_16_lsx(tl, tr, bl, br, v_distx, v_disty, b);
1987 b += 4;
1988 v_fx = __lsx_vadd_w(v_fx, v_fdx);
1989 v_fy = __lsx_vadd_w(v_fy, v_fdy);
1990 }
1991 fx = __lsx_vpickve2gr_w(v_fx, 0);
1992 fy = __lsx_vpickve2gr_w(v_fy, 0);
1993#endif
1994 while (b < boundedEnd) {
1995 int x = (fx >> 16);
1996 int y = (fy >> 16);
1997
1998 const uint *s1 = (const uint *)image.scanLine(y);
1999 const uint *s2 = (const uint *)image.scanLine(y: y + 1);
2000
2001 if (hasFastInterpolate4()) {
2002 int distx = (fx & 0x0000ffff) >> 8;
2003 int disty = (fy & 0x0000ffff) >> 8;
2004 *b = interpolate_4_pixels(t: s1 + x, b: s2 + x, distx, disty);
2005 } else {
2006 int distx = ((fx & 0x0000ffff) + 0x0800) >> 12;
2007 int disty = ((fy & 0x0000ffff) + 0x0800) >> 12;
2008 *b = interpolate_4_pixels_16(tl: s1[x], tr: s1[x + 1], bl: s2[x], br: s2[x + 1], distx, disty);
2009 }
2010
2011 fx += fdx;
2012 fy += fdy;
2013 ++b;
2014 }
2015 }
2016
2017 while (b < end) {
2018 int x1 = (fx >> 16);
2019 int x2;
2020 int y1 = (fy >> 16);
2021 int y2;
2022
2023 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
2024 fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
2025
2026 const uint *s1 = (const uint *)image.scanLine(y: y1);
2027 const uint *s2 = (const uint *)image.scanLine(y: y2);
2028
2029 uint tl = s1[x1];
2030 uint tr = s1[x2];
2031 uint bl = s2[x1];
2032 uint br = s2[x2];
2033
2034 if (hasFastInterpolate4()) {
2035 int distx = (fx & 0x0000ffff) >> 8;
2036 int disty = (fy & 0x0000ffff) >> 8;
2037 *b = interpolate_4_pixels(tl, tr, bl, br, distx, disty);
2038 } else {
2039 int distx = ((fx & 0x0000ffff) + 0x0800) >> 12;
2040 int disty = ((fy & 0x0000ffff) + 0x0800) >> 12;
2041 *b = interpolate_4_pixels_16(tl, tr, bl, br, distx, disty);
2042 }
2043
2044 fx += fdx;
2045 fy += fdy;
2046 ++b;
2047 }
2048}
2049
2050
2051static BilinearFastTransformHelper bilinearFastTransformHelperARGB32PM[2][NFastTransformTypes] = {
2052 {
2053 fetchTransformedBilinearARGB32PM_simple_scale_helper<BlendTransformedBilinear>,
2054 fetchTransformedBilinearARGB32PM_upscale_helper<BlendTransformedBilinear>,
2055 fetchTransformedBilinearARGB32PM_downscale_helper<BlendTransformedBilinear>,
2056 fetchTransformedBilinearARGB32PM_rotate_helper<BlendTransformedBilinear>,
2057 fetchTransformedBilinearARGB32PM_fast_rotate_helper<BlendTransformedBilinear>
2058 },
2059 {
2060 fetchTransformedBilinearARGB32PM_simple_scale_helper<BlendTransformedBilinearTiled>,
2061 fetchTransformedBilinearARGB32PM_upscale_helper<BlendTransformedBilinearTiled>,
2062 fetchTransformedBilinearARGB32PM_downscale_helper<BlendTransformedBilinearTiled>,
2063 fetchTransformedBilinearARGB32PM_rotate_helper<BlendTransformedBilinearTiled>,
2064 fetchTransformedBilinearARGB32PM_fast_rotate_helper<BlendTransformedBilinearTiled>
2065 }
2066};
2067
2068template<TextureBlendType blendType> /* blendType = BlendTransformedBilinear or BlendTransformedBilinearTiled */
2069static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, const Operator *,
2070 const QSpanData *data, int y, int x,
2071 int length)
2072{
2073 const qreal cx = x + qreal(0.5);
2074 const qreal cy = y + qreal(0.5);
2075 constexpr int tiled = (blendType == BlendTransformedBilinearTiled) ? 1 : 0;
2076
2077 uint *end = buffer + length;
2078 uint *b = buffer;
2079 if (canUseFastMatrixPath(cx, cy, length, data)) {
2080 // The increment pr x in the scanline
2081 int fdx = (int)(data->m11 * fixed_scale);
2082 int fdy = (int)(data->m12 * fixed_scale);
2083
2084 int fx = int((data->m21 * cy
2085 + data->m11 * cx + data->dx) * fixed_scale);
2086 int fy = int((data->m22 * cy
2087 + data->m12 * cx + data->dy) * fixed_scale);
2088
2089 fx -= half_point;
2090 fy -= half_point;
2091
2092 if (fdy == 0) { // simple scale, no rotation or shear
2093 if (qAbs(t: fdx) <= fixed_scale) {
2094 // simple scale up on X
2095 bilinearFastTransformHelperARGB32PM[tiled][SimpleScaleTransform](b, end, data->texture, fx, fy, fdx, fdy);
2096 } else if (qAbs(t: fdx) <= 2 * fixed_scale) {
2097 // simple scale down on X, less than 2x
2098 const int mid = (length * 2 < BufferSize) ? length : ((length + 1) / 2);
2099 bilinearFastTransformHelperARGB32PM[tiled][SimpleScaleTransform](buffer, buffer + mid, data->texture, fx, fy, fdx, fdy);
2100 if (mid != length)
2101 bilinearFastTransformHelperARGB32PM[tiled][SimpleScaleTransform](buffer + mid, buffer + length, data->texture, fx, fy, fdx, fdy);
2102 } else if (qAbs(t: data->m22) < qreal(1./8.)) {
2103 // scale up more than 8x (on Y)
2104 bilinearFastTransformHelperARGB32PM[tiled][UpscaleTransform](b, end, data->texture, fx, fy, fdx, fdy);
2105 } else {
2106 // scale down on X
2107 bilinearFastTransformHelperARGB32PM[tiled][DownscaleTransform](b, end, data->texture, fx, fy, fdx, fdy);
2108 }
2109 } else { // rotation or shear
2110 if (qAbs(t: data->m11) < qreal(1./8.) || qAbs(t: data->m22) < qreal(1./8.) ) {
2111 // if we are zooming more than 8 times, we use 8bit precision for the position.
2112 bilinearFastTransformHelperARGB32PM[tiled][RotateTransform](b, end, data->texture, fx, fy, fdx, fdy);
2113 } else {
2114 // we are zooming less than 8x, use 4bit precision
2115 bilinearFastTransformHelperARGB32PM[tiled][FastRotateTransform](b, end, data->texture, fx, fy, fdx, fdy);
2116 }
2117 }
2118 } else {
2119 const QTextureData &image = data->texture;
2120
2121 const qreal fdx = data->m11;
2122 const qreal fdy = data->m12;
2123 const qreal fdw = data->m13;
2124
2125 qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
2126 qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
2127 qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
2128
2129 while (b < end) {
2130 const qreal iw = fw == 0 ? 1 : 1 / fw;
2131 const qreal px = fx * iw - qreal(0.5);
2132 const qreal py = fy * iw - qreal(0.5);
2133
2134 int x1 = int(px) - (px < 0);
2135 int x2;
2136 int y1 = int(py) - (py < 0);
2137 int y2;
2138
2139 int distx = int((px - x1) * 256);
2140 int disty = int((py - y1) * 256);
2141
2142 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
2143 fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
2144
2145 const uint *s1 = (const uint *)data->texture.scanLine(y: y1);
2146 const uint *s2 = (const uint *)data->texture.scanLine(y: y2);
2147
2148 uint tl = s1[x1];
2149 uint tr = s1[x2];
2150 uint bl = s2[x1];
2151 uint br = s2[x2];
2152
2153 *b = interpolate_4_pixels(tl, tr, bl, br, distx, disty);
2154
2155 fx += fdx;
2156 fy += fdy;
2157 fw += fdw;
2158 //force increment to avoid /0
2159 if (!fw) {
2160 fw += fdw;
2161 }
2162 ++b;
2163 }
2164 }
2165
2166 return buffer;
2167}
2168
2169template<TextureBlendType blendType>
2170static void QT_FASTCALL fetchTransformedBilinear_simple_scale_helper(uint *b, uint *end, const QTextureData &image,
2171 int &fx, int &fy, int fdx, int /*fdy*/)
2172{
2173 const QPixelLayout *layout = &qPixelLayouts[image.format];
2174 const QList<QRgb> *clut = image.colorTable;
2175 const FetchAndConvertPixelsFunc fetch = layout->fetchToARGB32PM;
2176
2177 int y1 = (fy >> 16);
2178 int y2;
2179 fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
2180 const uchar *s1 = image.scanLine(y: y1);
2181 const uchar *s2 = image.scanLine(y: y2);
2182
2183 const int disty = (fy & 0x0000ffff) >> 8;
2184 const int idisty = 256 - disty;
2185 const int length = end - b;
2186
2187 // The intermediate buffer is generated in the positive direction
2188 const int adjust = (fdx < 0) ? fdx * length : 0;
2189 const int offset = (fx + adjust) >> 16;
2190 int x = offset;
2191
2192 Q_DECL_UNINITIALIZED IntermediateBuffer intermediate;
2193 uint *buf1 = intermediate.buffer_rb;
2194 uint *buf2 = intermediate.buffer_ag;
2195 const uint *ptr1;
2196 const uint *ptr2;
2197
2198 int count = (qint64(length) * qAbs(t: fdx) + fixed_scale - 1) / fixed_scale + 2;
2199 Q_ASSERT(count <= BufferSize + 2);
2200
2201 if (blendType == BlendTransformedBilinearTiled) {
2202 x %= image.width;
2203 if (x < 0)
2204 x += image.width;
2205 int len1 = qMin(a: count, b: image.width - x);
2206 int len2 = qMin(a: x, b: count - len1);
2207
2208 ptr1 = fetch(buf1, s1, x, len1, clut, nullptr);
2209 ptr2 = fetch(buf2, s2, x, len1, clut, nullptr);
2210 for (int i = 0; i < len1; ++i) {
2211 uint t = ptr1[i];
2212 uint b = ptr2[i];
2213 buf1[i] = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff;
2214 buf2[i] = ((((t >> 8) & 0xff00ff) * idisty + ((b >> 8) & 0xff00ff) * disty) >> 8) & 0xff00ff;
2215 }
2216
2217 if (len2) {
2218 ptr1 = fetch(buf1 + len1, s1, 0, len2, clut, nullptr);
2219 ptr2 = fetch(buf2 + len1, s2, 0, len2, clut, nullptr);
2220 for (int i = 0; i < len2; ++i) {
2221 uint t = ptr1[i];
2222 uint b = ptr2[i];
2223 buf1[i + len1] = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff;
2224 buf2[i + len1] = ((((t >> 8) & 0xff00ff) * idisty + ((b >> 8) & 0xff00ff) * disty) >> 8) & 0xff00ff;
2225 }
2226 }
2227 // Generate the rest by repeatedly repeating the previous set of pixels
2228 for (int i = image.width; i < count; ++i) {
2229 buf1[i] = buf1[i - image.width];
2230 buf2[i] = buf2[i - image.width];
2231 }
2232 } else {
2233 int start = qMax(a: x, b: image.x1);
2234 int end = qMin(a: x + count, b: image.x2);
2235 int len = qMax(a: 1, b: end - start);
2236 int leading = start - x;
2237
2238 ptr1 = fetch(buf1 + leading, s1, start, len, clut, nullptr);
2239 ptr2 = fetch(buf2 + leading, s2, start, len, clut, nullptr);
2240
2241 for (int i = 0; i < len; ++i) {
2242 uint t = ptr1[i];
2243 uint b = ptr2[i];
2244 buf1[i + leading] = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff;
2245 buf2[i + leading] = ((((t >> 8) & 0xff00ff) * idisty + ((b >> 8) & 0xff00ff) * disty) >> 8) & 0xff00ff;
2246 }
2247
2248 for (int i = 0; i < leading; ++i) {
2249 buf1[i] = buf1[leading];
2250 buf2[i] = buf2[leading];
2251 }
2252 for (int i = leading + len; i < count; ++i) {
2253 buf1[i] = buf1[i - 1];
2254 buf2[i] = buf2[i - 1];
2255 }
2256 }
2257
2258 // Now interpolate the values from the intermediate.buffer to get the final result.
2259 intermediate_adder(b, end, intermediate, offset, fx, fdx);
2260}
2261
2262
2263template<TextureBlendType blendType, QPixelLayout::BPP bpp, typename T>
2264static void QT_FASTCALL fetchTransformedBilinear_fetcher(T *buf1, T *buf2, const int len, const QTextureData &image,
2265 int fx, int fy, const int fdx, const int fdy)
2266{
2267 const QPixelLayout &layout = qPixelLayouts[image.format];
2268 constexpr bool useFetch = (bpp < QPixelLayout::BPP32);
2269 if (useFetch)
2270 Q_ASSERT(sizeof(T) == sizeof(uint));
2271 else
2272 Q_ASSERT(layout.bpp == bpp || (layout.bpp == QPixelLayout::BPP16FPx4 && bpp == QPixelLayout::BPP64));
2273 const Fetch1PixelFunc fetch1 = (bpp == QPixelLayout::BPPNone) ? fetch1PixelTable[layout.bpp] : fetch1Pixel<bpp>;
2274 if (fdy == 0) {
2275 int y1 = (fy >> 16);
2276 int y2;
2277 fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
2278 const uchar *s1 = image.scanLine(y: y1);
2279 const uchar *s2 = image.scanLine(y: y2);
2280
2281 int i = 0;
2282 if (blendType == BlendTransformedBilinear) {
2283 for (; i < len; ++i) {
2284 int x1 = (fx >> 16);
2285 int x2;
2286 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
2287 if (x1 != x2)
2288 break;
2289 if constexpr (useFetch) {
2290 buf1[i * 2 + 0] = buf1[i * 2 + 1] = fetch1(s1, x1);
2291 buf2[i * 2 + 0] = buf2[i * 2 + 1] = fetch1(s2, x1);
2292 } else {
2293 buf1[i * 2 + 0] = buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x1];
2294 buf2[i * 2 + 0] = buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x1];
2295 }
2296 fx += fdx;
2297 }
2298 int fastLen = len;
2299 if (fdx > 0)
2300 fastLen = qMin(a: fastLen, b: int((qint64(image.x2 - 1) * fixed_scale - fx) / fdx));
2301 else if (fdx < 0)
2302 fastLen = qMin(a: fastLen, b: int((qint64(image.x1) * fixed_scale - fx) / fdx));
2303
2304 for (; i < fastLen; ++i) {
2305 int x = (fx >> 16);
2306 if constexpr (useFetch) {
2307 buf1[i * 2 + 0] = fetch1(s1, x);
2308 buf1[i * 2 + 1] = fetch1(s1, x + 1);
2309 buf2[i * 2 + 0] = fetch1(s2, x);
2310 buf2[i * 2 + 1] = fetch1(s2, x + 1);
2311 } else {
2312 buf1[i * 2 + 0] = reinterpret_cast<const T *>(s1)[x];
2313 buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x + 1];
2314 buf2[i * 2 + 0] = reinterpret_cast<const T *>(s2)[x];
2315 buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x + 1];
2316 }
2317 fx += fdx;
2318 }
2319 }
2320
2321 for (; i < len; ++i) {
2322 int x1 = (fx >> 16);
2323 int x2;
2324 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
2325 if constexpr (useFetch) {
2326 buf1[i * 2 + 0] = fetch1(s1, x1);
2327 buf1[i * 2 + 1] = fetch1(s1, x2);
2328 buf2[i * 2 + 0] = fetch1(s2, x1);
2329 buf2[i * 2 + 1] = fetch1(s2, x2);
2330 } else {
2331 buf1[i * 2 + 0] = reinterpret_cast<const T *>(s1)[x1];
2332 buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x2];
2333 buf2[i * 2 + 0] = reinterpret_cast<const T *>(s2)[x1];
2334 buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x2];
2335 }
2336 fx += fdx;
2337 }
2338 } else {
2339 int i = 0;
2340 if (blendType == BlendTransformedBilinear) {
2341 for (; i < len; ++i) {
2342 int x1 = (fx >> 16);
2343 int x2;
2344 int y1 = (fy >> 16);
2345 int y2;
2346 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
2347 fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
2348 if (x1 != x2 && y1 != y2)
2349 break;
2350 const uchar *s1 = image.scanLine(y: y1);
2351 const uchar *s2 = image.scanLine(y: y2);
2352 if constexpr (useFetch) {
2353 buf1[i * 2 + 0] = fetch1(s1, x1);
2354 buf1[i * 2 + 1] = fetch1(s1, x2);
2355 buf2[i * 2 + 0] = fetch1(s2, x1);
2356 buf2[i * 2 + 1] = fetch1(s2, x2);
2357 } else {
2358 buf1[i * 2 + 0] = reinterpret_cast<const T *>(s1)[x1];
2359 buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x2];
2360 buf2[i * 2 + 0] = reinterpret_cast<const T *>(s2)[x1];
2361 buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x2];
2362 }
2363 fx += fdx;
2364 fy += fdy;
2365 }
2366 int fastLen = len;
2367 if (fdx > 0)
2368 fastLen = qMin(a: fastLen, b: int((qint64(image.x2 - 1) * fixed_scale - fx) / fdx));
2369 else if (fdx < 0)
2370 fastLen = qMin(a: fastLen, b: int((qint64(image.x1) * fixed_scale - fx) / fdx));
2371 if (fdy > 0)
2372 fastLen = qMin(a: fastLen, b: int((qint64(image.y2 - 1) * fixed_scale - fy) / fdy));
2373 else if (fdy < 0)
2374 fastLen = qMin(a: fastLen, b: int((qint64(image.y1) * fixed_scale - fy) / fdy));
2375
2376 for (; i < fastLen; ++i) {
2377 int x = (fx >> 16);
2378 int y = (fy >> 16);
2379 const uchar *s1 = image.scanLine(y);
2380 const uchar *s2 = s1 + image.bytesPerLine;
2381 if constexpr (useFetch) {
2382 buf1[i * 2 + 0] = fetch1(s1, x);
2383 buf1[i * 2 + 1] = fetch1(s1, x + 1);
2384 buf2[i * 2 + 0] = fetch1(s2, x);
2385 buf2[i * 2 + 1] = fetch1(s2, x + 1);
2386 } else {
2387 buf1[i * 2 + 0] = reinterpret_cast<const T *>(s1)[x];
2388 buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x + 1];
2389 buf2[i * 2 + 0] = reinterpret_cast<const T *>(s2)[x];
2390 buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x + 1];
2391 }
2392 fx += fdx;
2393 fy += fdy;
2394 }
2395 }
2396
2397 for (; i < len; ++i) {
2398 int x1 = (fx >> 16);
2399 int x2;
2400 int y1 = (fy >> 16);
2401 int y2;
2402 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
2403 fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
2404
2405 const uchar *s1 = image.scanLine(y: y1);
2406 const uchar *s2 = image.scanLine(y: y2);
2407 if constexpr (useFetch) {
2408 buf1[i * 2 + 0] = fetch1(s1, x1);
2409 buf1[i * 2 + 1] = fetch1(s1, x2);
2410 buf2[i * 2 + 0] = fetch1(s2, x1);
2411 buf2[i * 2 + 1] = fetch1(s2, x2);
2412 } else {
2413 buf1[i * 2 + 0] = reinterpret_cast<const T *>(s1)[x1];
2414 buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x2];
2415 buf2[i * 2 + 0] = reinterpret_cast<const T *>(s2)[x1];
2416 buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x2];
2417 }
2418 fx += fdx;
2419 fy += fdy;
2420 }
2421 }
2422}
2423
2424template<TextureBlendType blendType, QPixelLayout::BPP bpp, typename T>
2425static void QT_FASTCALL fetchTransformedBilinear_slow_fetcher(T *buf1, T *buf2, ushort *distxs, ushort *distys,
2426 const int len, const QTextureData &image,
2427 qreal &fx, qreal &fy, qreal &fw,
2428 const qreal fdx, const qreal fdy, const qreal fdw)
2429{
2430 const QPixelLayout &layout = qPixelLayouts[image.format];
2431 constexpr bool useFetch = (bpp < QPixelLayout::BPP32);
2432 if (useFetch)
2433 Q_ASSERT(sizeof(T) == sizeof(uint));
2434 else
2435 Q_ASSERT(layout.bpp == bpp);
2436
2437 const Fetch1PixelFunc fetch1 = (bpp == QPixelLayout::BPPNone) ? fetch1PixelTable[layout.bpp] : fetch1Pixel<bpp>;
2438
2439 for (int i = 0; i < len; ++i) {
2440 const qreal iw = fw == 0 ? 16384 : 1 / fw;
2441 const qreal px = fx * iw - qreal(0.5);
2442 const qreal py = fy * iw - qreal(0.5);
2443
2444 int x1 = qFloor(v: px);
2445 int x2;
2446 int y1 = qFloor(v: py);
2447 int y2;
2448
2449 distxs[i] = ushort((px - x1) * (1<<16));
2450 distys[i] = ushort((py - y1) * (1<<16));
2451
2452 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
2453 fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
2454
2455 const uchar *s1 = image.scanLine(y: y1);
2456 const uchar *s2 = image.scanLine(y: y2);
2457 if constexpr (useFetch) {
2458 buf1[i * 2 + 0] = fetch1(s1, x1);
2459 buf1[i * 2 + 1] = fetch1(s1, x2);
2460 buf2[i * 2 + 0] = fetch1(s2, x1);
2461 buf2[i * 2 + 1] = fetch1(s2, x2);
2462 } else {
2463 buf1[i * 2 + 0] = reinterpret_cast<const T *>(s1)[x1];
2464 buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x2];
2465 buf2[i * 2 + 0] = reinterpret_cast<const T *>(s2)[x1];
2466 buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x2];
2467 }
2468
2469 fx += fdx;
2470 fy += fdy;
2471 fw += fdw;
2472 }
2473}
2474
2475// blendType = BlendTransformedBilinear or BlendTransformedBilinearTiled
2476template<TextureBlendType blendType, QPixelLayout::BPP bpp>
2477static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Operator *,
2478 const QSpanData *data, int y, int x, int length)
2479{
2480 const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
2481 const QList<QRgb> *clut = data->texture.colorTable;
2482 Q_ASSERT(bpp == QPixelLayout::BPPNone || layout->bpp == bpp);
2483
2484 const qreal cx = x + qreal(0.5);
2485 const qreal cy = y + qreal(0.5);
2486
2487 if (canUseFastMatrixPath(cx, cy, length, data)) {
2488 // The increment pr x in the scanline
2489 int fdx = (int)(data->m11 * fixed_scale);
2490 int fdy = (int)(data->m12 * fixed_scale);
2491
2492 int fx = int((data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale);
2493 int fy = int((data->m22 * cy + data->m12 * cx + data->dy) * fixed_scale);
2494
2495 fx -= half_point;
2496 fy -= half_point;
2497
2498 if (fdy == 0) { // simple scale, no rotation or shear
2499 if (qAbs(t: fdx) <= fixed_scale) { // scale up on X
2500 fetchTransformedBilinear_simple_scale_helper<blendType>(buffer, buffer + length, data->texture, fx, fy, fdx, fdy);
2501 } else if (qAbs(t: fdx) <= 2 * fixed_scale) { // scale down on X less than 2x
2502 const int mid = (length * 2 < BufferSize) ? length : ((length + 1) / 2);
2503 fetchTransformedBilinear_simple_scale_helper<blendType>(buffer, buffer + mid, data->texture, fx, fy, fdx, fdy);
2504 if (mid != length)
2505 fetchTransformedBilinear_simple_scale_helper<blendType>(buffer + mid, buffer + length, data->texture, fx, fy, fdx, fdy);
2506 } else {
2507 const auto fetcher = fetchTransformedBilinear_fetcher<blendType,bpp,uint>;
2508
2509 Q_DECL_UNINITIALIZED uint buf1[BufferSize];
2510 Q_DECL_UNINITIALIZED uint buf2[BufferSize];
2511 uint *b = buffer;
2512 while (length) {
2513 int len = qMin(a: length, b: BufferSize / 2);
2514 fetcher(buf1, buf2, len, data->texture, fx, fy, fdx, 0);
2515 layout->convertToARGB32PM(buf1, len * 2, clut);
2516 layout->convertToARGB32PM(buf2, len * 2, clut);
2517
2518 if (hasFastInterpolate4() || qAbs(t: data->m22) < qreal(1./8.)) { // scale up more than 8x (on Y)
2519 int disty = (fy & 0x0000ffff) >> 8;
2520 for (int i = 0; i < len; ++i) {
2521 int distx = (fx & 0x0000ffff) >> 8;
2522 b[i] = interpolate_4_pixels(t: buf1 + i * 2, b: buf2 + i * 2, distx, disty);
2523 fx += fdx;
2524 }
2525 } else {
2526 int disty = ((fy & 0x0000ffff) + 0x0800) >> 12;
2527 for (int i = 0; i < len; ++i) {
2528 uint tl = buf1[i * 2 + 0];
2529 uint tr = buf1[i * 2 + 1];
2530 uint bl = buf2[i * 2 + 0];
2531 uint br = buf2[i * 2 + 1];
2532 int distx = ((fx & 0x0000ffff) + 0x0800) >> 12;
2533 b[i] = interpolate_4_pixels_16(tl, tr, bl, br, distx, disty);
2534 fx += fdx;
2535 }
2536 }
2537 length -= len;
2538 b += len;
2539 }
2540 }
2541 } else { // rotation or shear
2542 const auto fetcher = fetchTransformedBilinear_fetcher<blendType,bpp,uint>;
2543
2544 Q_DECL_UNINITIALIZED uint buf1[BufferSize];
2545 Q_DECL_UNINITIALIZED uint buf2[BufferSize];
2546 uint *b = buffer;
2547 while (length) {
2548 int len = qMin(a: length, b: BufferSize / 2);
2549 fetcher(buf1, buf2, len, data->texture, fx, fy, fdx, fdy);
2550 layout->convertToARGB32PM(buf1, len * 2, clut);
2551 layout->convertToARGB32PM(buf2, len * 2, clut);
2552
2553 if (hasFastInterpolate4() || qAbs(t: data->m11) < qreal(1./8.) || qAbs(t: data->m22) < qreal(1./8.)) {
2554 // If we are zooming more than 8 times, we use 8bit precision for the position.
2555 for (int i = 0; i < len; ++i) {
2556 int distx = (fx & 0x0000ffff) >> 8;
2557 int disty = (fy & 0x0000ffff) >> 8;
2558
2559 b[i] = interpolate_4_pixels(t: buf1 + i * 2, b: buf2 + i * 2, distx, disty);
2560 fx += fdx;
2561 fy += fdy;
2562 }
2563 } else {
2564 // We are zooming less than 8x, use 4bit precision
2565 for (int i = 0; i < len; ++i) {
2566 uint tl = buf1[i * 2 + 0];
2567 uint tr = buf1[i * 2 + 1];
2568 uint bl = buf2[i * 2 + 0];
2569 uint br = buf2[i * 2 + 1];
2570
2571 int distx = ((fx & 0x0000ffff) + 0x0800) >> 12;
2572 int disty = ((fy & 0x0000ffff) + 0x0800) >> 12;
2573
2574 b[i] = interpolate_4_pixels_16(tl, tr, bl, br, distx, disty);
2575 fx += fdx;
2576 fy += fdy;
2577 }
2578 }
2579
2580 length -= len;
2581 b += len;
2582 }
2583 }
2584 } else {
2585 const auto fetcher = fetchTransformedBilinear_slow_fetcher<blendType,bpp,uint>;
2586
2587 const qreal fdx = data->m11;
2588 const qreal fdy = data->m12;
2589 const qreal fdw = data->m13;
2590
2591 qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
2592 qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
2593 qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
2594
2595 Q_DECL_UNINITIALIZED uint buf1[BufferSize];
2596 Q_DECL_UNINITIALIZED uint buf2[BufferSize];
2597 uint *b = buffer;
2598
2599 Q_DECL_UNINITIALIZED ushort distxs[BufferSize / 2];
2600 Q_DECL_UNINITIALIZED ushort distys[BufferSize / 2];
2601
2602 while (length) {
2603 const int len = qMin(a: length, b: BufferSize / 2);
2604 fetcher(buf1, buf2, distxs, distys, len, data->texture, fx, fy, fw, fdx, fdy, fdw);
2605
2606 layout->convertToARGB32PM(buf1, len * 2, clut);
2607 layout->convertToARGB32PM(buf2, len * 2, clut);
2608
2609 for (int i = 0; i < len; ++i) {
2610 const int distx = distxs[i] >> 8;
2611 const int disty = distys[i] >> 8;
2612
2613 b[i] = interpolate_4_pixels(t: buf1 + i * 2, b: buf2 + i * 2, distx, disty);
2614 }
2615 length -= len;
2616 b += len;
2617 }
2618 }
2619
2620 return buffer;
2621}
2622
2623#if QT_CONFIG(raster_64bit)
2624template<TextureBlendType blendType>
2625static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint32(QRgba64 *buffer, const QSpanData *data,
2626 int y, int x, int length)
2627{
2628 const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
2629 const auto *clut = data->texture.colorTable;
2630 const auto convert = layout->convertToRGBA64PM;
2631
2632 const qreal cx = x + qreal(0.5);
2633 const qreal cy = y + qreal(0.5);
2634
2635 Q_DECL_UNINITIALIZED uint sbuf1[BufferSize];
2636 Q_DECL_UNINITIALIZED uint sbuf2[BufferSize];
2637 alignas(8) Q_DECL_UNINITIALIZED QRgba64 buf1[BufferSize];
2638 alignas(8) Q_DECL_UNINITIALIZED QRgba64 buf2[BufferSize];
2639 QRgba64 *b = buffer;
2640
2641 if (canUseFastMatrixPath(cx, cy, length, data)) {
2642 // The increment pr x in the scanline
2643 const int fdx = (int)(data->m11 * fixed_scale);
2644 const int fdy = (int)(data->m12 * fixed_scale);
2645
2646 int fx = int((data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale);
2647 int fy = int((data->m22 * cy + data->m12 * cx + data->dy) * fixed_scale);
2648
2649 fx -= half_point;
2650 fy -= half_point;
2651
2652 const auto fetcher =
2653 (layout->bpp == QPixelLayout::BPP32)
2654 ? fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPP32, uint>
2655 : fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPPNone, uint>;
2656
2657 if (fdy == 0) { //simple scale, no rotation
2658 while (length) {
2659 const int len = qMin(a: length, b: BufferSize / 2);
2660 const int disty = (fy & 0x0000ffff);
2661#if defined(__SSE2__)
2662 const __m128i vdy = _mm_set1_epi16(w: disty);
2663 const __m128i vidy = _mm_set1_epi16(w: 0x10000 - disty);
2664#endif
2665 fetcher(sbuf1, sbuf2, len, data->texture, fx, fy, fdx, fdy);
2666
2667 convert(buf1, sbuf1, len * 2, clut, nullptr);
2668 if (disty)
2669 convert(buf2, sbuf2, len * 2, clut, nullptr);
2670
2671 for (int i = 0; i < len; ++i) {
2672 const int distx = (fx & 0x0000ffff);
2673#if defined(__SSE2__)
2674 __m128i vt = _mm_loadu_si128(p: (const __m128i*)(buf1 + i*2));
2675 if (disty) {
2676 __m128i vb = _mm_loadu_si128(p: (const __m128i*)(buf2 + i*2));
2677 vt = _mm_mulhi_epu16(a: vt, b: vidy);
2678 vb = _mm_mulhi_epu16(a: vb, b: vdy);
2679 vt = _mm_add_epi16(a: vt, b: vb);
2680 }
2681 if (distx) {
2682 const __m128i vdistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(distx), _MM_SHUFFLE(0, 0, 0, 0));
2683 const __m128i vidistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(0x10000 - distx), _MM_SHUFFLE(0, 0, 0, 0));
2684 vt = _mm_mulhi_epu16(a: vt, b: _mm_unpacklo_epi64(a: vidistx, b: vdistx));
2685 vt = _mm_add_epi16(a: vt, _mm_srli_si128(vt, 8));
2686 }
2687 _mm_storel_epi64(p: (__m128i*)(b+i), a: vt);
2688#else
2689 b[i] = interpolate_4_pixels_rgb64(buf1 + i*2, buf2 + i*2, distx, disty);
2690#endif
2691 fx += fdx;
2692 }
2693 length -= len;
2694 b += len;
2695 }
2696 } else { // rotation or shear
2697 while (length) {
2698 const int len = qMin(a: length, b: BufferSize / 2);
2699
2700 fetcher(sbuf1, sbuf2, len, data->texture, fx, fy, fdx, fdy);
2701
2702 convert(buf1, sbuf1, len * 2, clut, nullptr);
2703 convert(buf2, sbuf2, len * 2, clut, nullptr);
2704
2705 for (int i = 0; i < len; ++i) {
2706 const int distx = (fx & 0x0000ffff);
2707 const int disty = (fy & 0x0000ffff);
2708 b[i] = interpolate_4_pixels_rgb64(t: buf1 + i*2, b: buf2 + i*2, distx, disty);
2709 fx += fdx;
2710 fy += fdy;
2711 }
2712
2713 length -= len;
2714 b += len;
2715 }
2716 }
2717 } else { // !(data->fast_matrix)
2718 const auto fetcher =
2719 (layout->bpp == QPixelLayout::BPP32)
2720 ? fetchTransformedBilinear_slow_fetcher<blendType, QPixelLayout::BPP32, uint>
2721 : fetchTransformedBilinear_slow_fetcher<blendType, QPixelLayout::BPPNone, uint>;
2722
2723 const qreal fdx = data->m11;
2724 const qreal fdy = data->m12;
2725 const qreal fdw = data->m13;
2726
2727 qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
2728 qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
2729 qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
2730
2731 Q_DECL_UNINITIALIZED ushort distxs[BufferSize / 2];
2732 Q_DECL_UNINITIALIZED ushort distys[BufferSize / 2];
2733
2734 while (length) {
2735 const int len = qMin(a: length, b: BufferSize / 2);
2736 fetcher(sbuf1, sbuf2, distxs, distys, len, data->texture, fx, fy, fw, fdx, fdy, fdw);
2737
2738 convert(buf1, sbuf1, len * 2, clut, nullptr);
2739 convert(buf2, sbuf2, len * 2, clut, nullptr);
2740
2741 for (int i = 0; i < len; ++i) {
2742 const int distx = distxs[i];
2743 const int disty = distys[i];
2744 b[i] = interpolate_4_pixels_rgb64(t: buf1 + i*2, b: buf2 + i*2, distx, disty);
2745 }
2746
2747 length -= len;
2748 b += len;
2749 }
2750 }
2751 return buffer;
2752}
2753
2754template<TextureBlendType blendType>
2755static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint64(QRgba64 *buffer, const QSpanData *data,
2756 int y, int x, int length)
2757{
2758 const auto convert = convert64ToRGBA64PM[data->texture.format];
2759
2760 const qreal cx = x + qreal(0.5);
2761 const qreal cy = y + qreal(0.5);
2762
2763 alignas(8) Q_DECL_UNINITIALIZED QRgba64 buf1[BufferSize];
2764 alignas(8) Q_DECL_UNINITIALIZED QRgba64 buf2[BufferSize];
2765 QRgba64 *end = buffer + length;
2766 QRgba64 *b = buffer;
2767
2768 if (canUseFastMatrixPath(cx, cy, length, data)) {
2769 // The increment pr x in the scanline
2770 const int fdx = (int)(data->m11 * fixed_scale);
2771 const int fdy = (int)(data->m12 * fixed_scale);
2772
2773 int fx = int((data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale);
2774 int fy = int((data->m22 * cy + data->m12 * cx + data->dy) * fixed_scale);
2775
2776 fx -= half_point;
2777 fy -= half_point;
2778 const auto fetcher = fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPP64, QRgba64>;
2779
2780 if (fdy == 0) { //simple scale, no rotation
2781 while (length) {
2782 int len = qMin(a: length, b: BufferSize / 2);
2783 int disty = (fy & 0x0000ffff);
2784#if defined(__SSE2__)
2785 const __m128i vdy = _mm_set1_epi16(w: disty);
2786 const __m128i vidy = _mm_set1_epi16(w: 0x10000 - disty);
2787#endif
2788 fetcher(buf1, buf2, len, data->texture, fx, fy, fdx, fdy);
2789
2790 convert(buf1, len * 2);
2791 if (disty)
2792 convert(buf2, len * 2);
2793
2794 for (int i = 0; i < len; ++i) {
2795 int distx = (fx & 0x0000ffff);
2796#if defined(__SSE2__)
2797 __m128i vt = _mm_loadu_si128(p: (const __m128i*)(buf1 + i*2));
2798 if (disty) {
2799 __m128i vb = _mm_loadu_si128(p: (const __m128i*)(buf2 + i*2));
2800 vt = _mm_mulhi_epu16(a: vt, b: vidy);
2801 vb = _mm_mulhi_epu16(a: vb, b: vdy);
2802 vt = _mm_add_epi16(a: vt, b: vb);
2803 }
2804 if (distx) {
2805 const __m128i vdistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(distx), _MM_SHUFFLE(0, 0, 0, 0));
2806 const __m128i vidistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(0x10000 - distx), _MM_SHUFFLE(0, 0, 0, 0));
2807 vt = _mm_mulhi_epu16(a: vt, b: _mm_unpacklo_epi64(a: vidistx, b: vdistx));
2808 vt = _mm_add_epi16(a: vt, _mm_srli_si128(vt, 8));
2809 }
2810 _mm_storel_epi64(p: (__m128i*)(b+i), a: vt);
2811#else
2812 b[i] = interpolate_4_pixels_rgb64(buf1 + i*2, buf2 + i*2, distx, disty);
2813#endif
2814 fx += fdx;
2815 }
2816 length -= len;
2817 b += len;
2818 }
2819 } else { // rotation or shear
2820 while (b < end) {
2821 int len = qMin(a: length, b: BufferSize / 2);
2822
2823 fetcher(buf1, buf2, len, data->texture, fx, fy, fdx, fdy);
2824
2825 convert(buf1, len * 2);
2826 convert(buf2, len * 2);
2827
2828 for (int i = 0; i < len; ++i) {
2829 int distx = (fx & 0x0000ffff);
2830 int disty = (fy & 0x0000ffff);
2831 b[i] = interpolate_4_pixels_rgb64(t: buf1 + i*2, b: buf2 + i*2, distx, disty);
2832 fx += fdx;
2833 fy += fdy;
2834 }
2835
2836 length -= len;
2837 b += len;
2838 }
2839 }
2840 } else { // !(data->fast_matrix)
2841 const auto fetcher = fetchTransformedBilinear_slow_fetcher<blendType, QPixelLayout::BPP64, QRgba64>;
2842
2843 const qreal fdx = data->m11;
2844 const qreal fdy = data->m12;
2845 const qreal fdw = data->m13;
2846
2847 qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
2848 qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
2849 qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
2850
2851 Q_DECL_UNINITIALIZED ushort distxs[BufferSize / 2];
2852 Q_DECL_UNINITIALIZED ushort distys[BufferSize / 2];
2853
2854 while (length) {
2855 const int len = qMin(a: length, b: BufferSize / 2);
2856 fetcher(buf1, buf2, distxs, distys, len, data->texture, fx, fy, fw, fdx, fdy, fdw);
2857
2858 convert(buf1, len * 2);
2859 convert(buf2, len * 2);
2860
2861 for (int i = 0; i < len; ++i) {
2862 const int distx = distxs[i];
2863 const int disty = distys[i];
2864 b[i] = interpolate_4_pixels_rgb64(t: buf1 + i*2, b: buf2 + i*2, distx, disty);
2865 }
2866
2867 length -= len;
2868 b += len;
2869 }
2870 }
2871 return buffer;
2872}
2873
2874template<TextureBlendType blendType>
2875static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_f32x4(QRgba64 *buffer, const QSpanData *data,
2876 int y, int x, int length)
2877{
2878 const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
2879 const auto *clut = data->texture.colorTable;
2880 const auto convert = layout->fetchToRGBA64PM;
2881
2882 const qreal cx = x + qreal(0.5);
2883 const qreal cy = y + qreal(0.5);
2884
2885 Q_DECL_UNINITIALIZED QRgbaFloat32 sbuf1[BufferSize];
2886 Q_DECL_UNINITIALIZED QRgbaFloat32 sbuf2[BufferSize];
2887 alignas(8) Q_DECL_UNINITIALIZED QRgba64 buf1[BufferSize];
2888 alignas(8) Q_DECL_UNINITIALIZED QRgba64 buf2[BufferSize];
2889 QRgba64 *b = buffer;
2890
2891 if (canUseFastMatrixPath(cx, cy, length, data)) {
2892 // The increment pr x in the scanline
2893 const int fdx = (int)(data->m11 * fixed_scale);
2894 const int fdy = (int)(data->m12 * fixed_scale);
2895
2896 int fx = int((data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale);
2897 int fy = int((data->m22 * cy + data->m12 * cx + data->dy) * fixed_scale);
2898
2899 fx -= half_point;
2900 fy -= half_point;
2901
2902 const auto fetcher = fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPP32FPx4, QRgbaFloat32>;
2903
2904 const bool skipsecond = (fdy == 0) && ((fy & 0x0000ffff) == 0);
2905 while (length) {
2906 const int len = qMin(a: length, b: BufferSize / 2);
2907
2908 fetcher(sbuf1, sbuf2, len, data->texture, fx, fy, fdx, fdy);
2909
2910 convert(buf1, (const uchar *)sbuf1, 0, len * 2, clut, nullptr);
2911 if (!skipsecond)
2912 convert(buf2, (const uchar *)sbuf2, 0, len * 2, clut, nullptr);
2913
2914 for (int i = 0; i < len; ++i) {
2915 const int distx = (fx & 0x0000ffff);
2916 const int disty = (fy & 0x0000ffff);
2917 b[i] = interpolate_4_pixels_rgb64(t: buf1 + i*2, b: buf2 + i*2, distx, disty);
2918 fx += fdx;
2919 fy += fdy;
2920 }
2921
2922 length -= len;
2923 b += len;
2924 }
2925 } else { // !(data->fast_matrix)
2926 const auto fetcher = fetchTransformedBilinear_slow_fetcher<blendType, QPixelLayout::BPP32FPx4, QRgbaFloat32>;
2927
2928 const qreal fdx = data->m11;
2929 const qreal fdy = data->m12;
2930 const qreal fdw = data->m13;
2931
2932 qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
2933 qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
2934 qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
2935
2936 Q_DECL_UNINITIALIZED ushort distxs[BufferSize / 2];
2937 Q_DECL_UNINITIALIZED ushort distys[BufferSize / 2];
2938
2939 while (length) {
2940 const int len = qMin(a: length, b: BufferSize / 2);
2941 fetcher(sbuf1, sbuf2, distxs, distys, len, data->texture, fx, fy, fw, fdx, fdy, fdw);
2942
2943 convert(buf1, (const uchar *)sbuf1, 0, len * 2, clut, nullptr);
2944 convert(buf2, (const uchar *)sbuf2, 0, len * 2, clut, nullptr);
2945
2946 for (int i = 0; i < len; ++i) {
2947 const int distx = distxs[i];
2948 const int disty = distys[i];
2949 b[i] = interpolate_4_pixels_rgb64(t: buf1 + i*2, b: buf2 + i*2, distx, disty);
2950 }
2951
2952 length -= len;
2953 b += len;
2954 }
2955 }
2956 return buffer;
2957}
2958
2959template<TextureBlendType blendType>
2960static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, const Operator *,
2961 const QSpanData *data, int y, int x, int length)
2962{
2963 switch (qPixelLayouts[data->texture.format].bpp) {
2964 case QPixelLayout::BPP64:
2965 case QPixelLayout::BPP16FPx4:
2966 return fetchTransformedBilinear64_uint64<blendType>(buffer, data, y, x, length);
2967 case QPixelLayout::BPP32FPx4:
2968 return fetchTransformedBilinear64_f32x4<blendType>(buffer, data, y, x, length);
2969 default:
2970 return fetchTransformedBilinear64_uint32<blendType>(buffer, data, y, x, length);
2971 }
2972}
2973#endif
2974
2975#if QT_CONFIG(raster_fp)
2976static void interpolate_simple_rgba32f(QRgbaFloat32 *b, const QRgbaFloat32 *buf1, const QRgbaFloat32 *buf2, int len,
2977 int &fx, int fdx,
2978 int &fy, int fdy)
2979{
2980 for (int i = 0; i < len; ++i) {
2981 const int distx = (fx & 0x0000ffff);
2982 const int disty = (fy & 0x0000ffff);
2983 b[i] = interpolate_4_pixels_rgba32f(t: buf1 + i*2, b: buf2 + i*2, distx, disty);
2984 fx += fdx;
2985 fy += fdy;
2986 }
2987}
2988
2989static void interpolate_perspective_rgba32f(QRgbaFloat32 *b, const QRgbaFloat32 *buf1, const QRgbaFloat32 *buf2, int len,
2990 unsigned short *distxs,
2991 unsigned short *distys)
2992{
2993 for (int i = 0; i < len; ++i) {
2994 const int dx = distxs[i];
2995 const int dy = distys[i];
2996 b[i] = interpolate_4_pixels_rgba32f(t: buf1 + i*2, b: buf2 + i*2, distx: dx, disty: dy);
2997 }
2998}
2999
3000template<TextureBlendType blendType>
3001static const QRgbaFloat32 *QT_FASTCALL fetchTransformedBilinearFP_uint32(QRgbaFloat32 *buffer, const QSpanData *data,
3002 int y, int x, int length)
3003{
3004 const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
3005 const auto *clut = data->texture.colorTable;
3006 const auto convert = qConvertToRGBA32F[data->texture.format];
3007
3008 const qreal cx = x + qreal(0.5);
3009 const qreal cy = y + qreal(0.5);
3010
3011 Q_DECL_UNINITIALIZED uint sbuf1[BufferSize];
3012 Q_DECL_UNINITIALIZED uint sbuf2[BufferSize];
3013 Q_DECL_UNINITIALIZED QRgbaFloat32 buf1[BufferSize];
3014 Q_DECL_UNINITIALIZED QRgbaFloat32 buf2[BufferSize];
3015 QRgbaFloat32 *b = buffer;
3016
3017 if (canUseFastMatrixPath(cx, cy, length, data)) {
3018 // The increment pr x in the scanline
3019 const int fdx = (int)(data->m11 * fixed_scale);
3020 const int fdy = (int)(data->m12 * fixed_scale);
3021
3022 int fx = int((data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale);
3023 int fy = int((data->m22 * cy + data->m12 * cx + data->dy) * fixed_scale);
3024
3025 fx -= half_point;
3026 fy -= half_point;
3027
3028 const auto fetcher =
3029 (layout->bpp == QPixelLayout::BPP32)
3030 ? fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPP32, uint>
3031 : fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPPNone, uint>;
3032
3033 const bool skipsecond = (fdy == 0) && ((fy & 0x0000ffff) == 0);
3034 while (length) {
3035 const int len = qMin(a: length, b: BufferSize / 2);
3036 fetcher(sbuf1, sbuf2, len, data->texture, fx, fy, fdx, fdy);
3037
3038 convert(buf1, sbuf1, len * 2, clut, nullptr);
3039 if (!skipsecond)
3040 convert(buf2, sbuf2, len * 2, clut, nullptr);
3041
3042 interpolate_simple_rgba32f(b, buf1, buf2, len, fx, fdx, fy, fdy);
3043
3044 length -= len;
3045 b += len;
3046 }
3047 } else { // !(data->fast_matrix)
3048 const auto fetcher =
3049 (layout->bpp == QPixelLayout::BPP32)
3050 ? fetchTransformedBilinear_slow_fetcher<blendType, QPixelLayout::BPP32, uint>
3051 : fetchTransformedBilinear_slow_fetcher<blendType, QPixelLayout::BPPNone, uint>;
3052
3053 const qreal fdx = data->m11;
3054 const qreal fdy = data->m12;
3055 const qreal fdw = data->m13;
3056 qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
3057 qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
3058 qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
3059 Q_DECL_UNINITIALIZED ushort distxs[BufferSize / 2];
3060 Q_DECL_UNINITIALIZED ushort distys[BufferSize / 2];
3061
3062 while (length) {
3063 const int len = qMin(a: length, b: BufferSize / 2);
3064 fetcher(sbuf1, sbuf2, distxs, distys, len, data->texture, fx, fy, fw, fdx, fdy, fdw);
3065
3066 convert(buf1, sbuf1, len * 2, clut, nullptr);
3067 convert(buf2, sbuf2, len * 2, clut, nullptr);
3068
3069 interpolate_perspective_rgba32f(b, buf1, buf2, len, distxs, distys);
3070
3071 length -= len;
3072 b += len;
3073 }
3074 }
3075 return buffer;
3076}
3077
3078template<TextureBlendType blendType>
3079static const QRgbaFloat32 *QT_FASTCALL fetchTransformedBilinearFP_uint64(QRgbaFloat32 *buffer, const QSpanData *data,
3080 int y, int x, int length)
3081{
3082 const auto convert = convert64ToRGBA32F[data->texture.format];
3083
3084 const qreal cx = x + qreal(0.5);
3085 const qreal cy = y + qreal(0.5);
3086
3087 Q_DECL_UNINITIALIZED quint64 sbuf1[BufferSize] ;
3088 Q_DECL_UNINITIALIZED quint64 sbuf2[BufferSize];
3089 Q_DECL_UNINITIALIZED QRgbaFloat32 buf1[BufferSize];
3090 Q_DECL_UNINITIALIZED QRgbaFloat32 buf2[BufferSize];
3091 QRgbaFloat32 *b = buffer;
3092
3093 if (canUseFastMatrixPath(cx, cy, length, data)) {
3094 // The increment pr x in the scanline
3095 const int fdx = (int)(data->m11 * fixed_scale);
3096 const int fdy = (int)(data->m12 * fixed_scale);
3097
3098 int fx = int((data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale);
3099 int fy = int((data->m22 * cy + data->m12 * cx + data->dy) * fixed_scale);
3100
3101 fx -= half_point;
3102 fy -= half_point;
3103 const auto fetcher = fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPP64, quint64>;
3104
3105 const bool skipsecond = (fdy == 0) && ((fy & 0x0000ffff) == 0);
3106 while (length) {
3107 const int len = qMin(a: length, b: BufferSize / 2);
3108 fetcher(sbuf1, sbuf2, len, data->texture, fx, fy, fdx, fdy);
3109
3110 convert(buf1, sbuf1, len * 2);
3111 if (!skipsecond)
3112 convert(buf2, sbuf2, len * 2);
3113
3114 interpolate_simple_rgba32f(b, buf1, buf2, len, fx, fdx, fy, fdy);
3115
3116 length -= len;
3117 b += len;
3118 }
3119 } else { // !(data->fast_matrix)
3120 const auto fetcher = fetchTransformedBilinear_slow_fetcher<blendType, QPixelLayout::BPP64, quint64>;
3121
3122 const qreal fdx = data->m11;
3123 const qreal fdy = data->m12;
3124 const qreal fdw = data->m13;
3125
3126 qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
3127 qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
3128 qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
3129
3130 Q_DECL_UNINITIALIZED ushort distxs[BufferSize / 2];
3131 Q_DECL_UNINITIALIZED ushort distys[BufferSize / 2];
3132
3133 while (length) {
3134 const int len = qMin(a: length, b: BufferSize / 2);
3135 fetcher(sbuf1, sbuf2, distxs, distys, len, data->texture, fx, fy, fw, fdx, fdy, fdw);
3136
3137 convert(buf1, sbuf1, len * 2);
3138 convert(buf2, sbuf2, len * 2);
3139
3140 interpolate_perspective_rgba32f(b, buf1, buf2, len, distxs, distys);
3141
3142 length -= len;
3143 b += len;
3144 }
3145 }
3146 return buffer;
3147}
3148
3149template<TextureBlendType blendType>
3150static const QRgbaFloat32 *QT_FASTCALL fetchTransformedBilinearFP(QRgbaFloat32 *buffer, const QSpanData *data,
3151 int y, int x, int length)
3152{
3153 const auto convert = data->rasterBuffer->format == QImage::Format_RGBA32FPx4 ? convertRGBA32FToRGBA32FPM
3154 : convertRGBA32FToRGBA32F;
3155
3156 const qreal cx = x + qreal(0.5);
3157 const qreal cy = y + qreal(0.5);
3158
3159 Q_DECL_UNINITIALIZED QRgbaFloat32 buf1[BufferSize];
3160 Q_DECL_UNINITIALIZED QRgbaFloat32 buf2[BufferSize];
3161 QRgbaFloat32 *b = buffer;
3162
3163 if (canUseFastMatrixPath(cx, cy, length, data)) {
3164 // The increment pr x in the scanline
3165 const int fdx = (int)(data->m11 * fixed_scale);
3166 const int fdy = (int)(data->m12 * fixed_scale);
3167
3168 int fx = int((data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale);
3169 int fy = int((data->m22 * cy + data->m12 * cx + data->dy) * fixed_scale);
3170
3171 fx -= half_point;
3172 fy -= half_point;
3173 const auto fetcher = fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPP32FPx4, QRgbaFloat32>;
3174
3175 const bool skipsecond = (fdy == 0) && ((fy & 0x0000ffff) == 0);
3176 while (length) {
3177 const int len = qMin(a: length, b: BufferSize / 2);
3178 fetcher(buf1, buf2, len, data->texture, fx, fy, fdx, fdy);
3179
3180 convert(buf1, len * 2);
3181 if (!skipsecond)
3182 convert(buf2, len * 2);
3183
3184 interpolate_simple_rgba32f(b, buf1, buf2, len, fx, fdx, fy, fdy);
3185
3186 length -= len;
3187 b += len;
3188 }
3189 } else { // !(data->fast_matrix)
3190 const auto fetcher = fetchTransformedBilinear_slow_fetcher<blendType, QPixelLayout::BPP32FPx4, QRgbaFloat32>;
3191
3192 const qreal fdx = data->m11;
3193 const qreal fdy = data->m12;
3194 const qreal fdw = data->m13;
3195
3196 qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
3197 qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
3198 qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
3199
3200 Q_DECL_UNINITIALIZED ushort distxs[BufferSize / 2];
3201 Q_DECL_UNINITIALIZED ushort distys[BufferSize / 2];
3202
3203 while (length) {
3204 const int len = qMin(a: length, b: BufferSize / 2);
3205 fetcher(buf1, buf2, distxs, distys, len, data->texture, fx, fy, fw, fdx, fdy, fdw);
3206
3207 convert(buf1, len * 2);
3208 convert(buf2, len * 2);
3209
3210 interpolate_perspective_rgba32f(b, buf1, buf2, len, distxs, distys);
3211
3212 length -= len;
3213 b += len;
3214 }
3215 }
3216 return buffer;
3217}
3218
3219template<TextureBlendType blendType>
3220static const QRgbaFloat32 *QT_FASTCALL fetchTransformedBilinearFP(QRgbaFloat32 *buffer, const Operator *,
3221 const QSpanData *data, int y, int x, int length)
3222{
3223 switch (qPixelLayouts[data->texture.format].bpp) {
3224 case QPixelLayout::BPP64:
3225 case QPixelLayout::BPP16FPx4:
3226 return fetchTransformedBilinearFP_uint64<blendType>(buffer, data, y, x, length);
3227 case QPixelLayout::BPP32FPx4:
3228 return fetchTransformedBilinearFP<blendType>(buffer, data, y, x, length);
3229 default:
3230 return fetchTransformedBilinearFP_uint32<blendType>(buffer, data, y, x, length);
3231 }
3232}
3233#endif // QT_CONFIG(raster_fp)
3234
3235// FetchUntransformed can have more specialized methods added depending on SIMD features.
3236static SourceFetchProc sourceFetchUntransformed[] = {
3237 nullptr, // Invalid
3238 fetchUntransformed, // Mono
3239 fetchUntransformed, // MonoLsb
3240 fetchUntransformed, // Indexed8
3241 fetchUntransformedARGB32PM, // RGB32
3242 fetchUntransformed, // ARGB32
3243 fetchUntransformedARGB32PM, // ARGB32_Premultiplied
3244 fetchUntransformedRGB16, // RGB16
3245 fetchUntransformed, // ARGB8565_Premultiplied
3246 fetchUntransformed, // RGB666
3247 fetchUntransformed, // ARGB6666_Premultiplied
3248 fetchUntransformed, // RGB555
3249 fetchUntransformed, // ARGB8555_Premultiplied
3250 fetchUntransformed, // RGB888
3251 fetchUntransformed, // RGB444
3252 fetchUntransformed, // ARGB4444_Premultiplied
3253 fetchUntransformed, // RGBX8888
3254 fetchUntransformed, // RGBA8888
3255 fetchUntransformed, // RGBA8888_Premultiplied
3256 fetchUntransformed, // Format_BGR30
3257 fetchUntransformed, // Format_A2BGR30_Premultiplied
3258 fetchUntransformed, // Format_RGB30
3259 fetchUntransformed, // Format_A2RGB30_Premultiplied
3260 fetchUntransformed, // Alpha8
3261 fetchUntransformed, // Grayscale8
3262 fetchUntransformed, // RGBX64
3263 fetchUntransformed, // RGBA64
3264 fetchUntransformed, // RGBA64_Premultiplied
3265 fetchUntransformed, // Grayscale16
3266 fetchUntransformed, // BGR888
3267 fetchUntransformed, // RGBX16FPx4
3268 fetchUntransformed, // RGBA16FPx4
3269 fetchUntransformed, // RGBA16FPx4_Premultiplied
3270 fetchUntransformed, // RGBX32Px4
3271 fetchUntransformed, // RGBA32FPx4
3272 fetchUntransformed, // RGBA32FPx4_Premultiplied
3273 fetchUntransformed, // CMYK8888
3274};
3275
3276static_assert(std::size(sourceFetchUntransformed) == QImage::NImageFormats);
3277
3278static const SourceFetchProc sourceFetchGeneric[] = {
3279 fetchUntransformed, // Untransformed
3280 fetchUntransformed, // Tiled
3281 fetchTransformed<BlendTransformed, QPixelLayout::BPPNone>, // Transformed
3282 fetchTransformed<BlendTransformedTiled, QPixelLayout::BPPNone>, // TransformedTiled
3283 fetchTransformedBilinear<BlendTransformedBilinear, QPixelLayout::BPPNone>, // TransformedBilinear
3284 fetchTransformedBilinear<BlendTransformedBilinearTiled, QPixelLayout::BPPNone> // TransformedBilinearTiled
3285};
3286
3287static_assert(std::size(sourceFetchGeneric) == NBlendTypes);
3288
3289static SourceFetchProc sourceFetchARGB32PM[] = {
3290 fetchUntransformedARGB32PM, // Untransformed
3291 fetchUntransformedARGB32PM, // Tiled
3292 fetchTransformed<BlendTransformed, QPixelLayout::BPP32>, // Transformed
3293 fetchTransformed<BlendTransformedTiled, QPixelLayout::BPP32>, // TransformedTiled
3294 fetchTransformedBilinearARGB32PM<BlendTransformedBilinear>, // Bilinear
3295 fetchTransformedBilinearARGB32PM<BlendTransformedBilinearTiled> // BilinearTiled
3296};
3297
3298static_assert(std::size(sourceFetchARGB32PM) == NBlendTypes);
3299
3300static SourceFetchProc sourceFetchAny16[] = {
3301 fetchUntransformed, // Untransformed
3302 fetchUntransformed, // Tiled
3303 fetchTransformed<BlendTransformed, QPixelLayout::BPP16>, // Transformed
3304 fetchTransformed<BlendTransformedTiled, QPixelLayout::BPP16>, // TransformedTiled
3305 fetchTransformedBilinear<BlendTransformedBilinear, QPixelLayout::BPP16>, // TransformedBilinear
3306 fetchTransformedBilinear<BlendTransformedBilinearTiled, QPixelLayout::BPP16> // TransformedBilinearTiled
3307};
3308
3309static_assert(std::size(sourceFetchAny16) == NBlendTypes);
3310
3311static SourceFetchProc sourceFetchAny32[] = {
3312 fetchUntransformed, // Untransformed
3313 fetchUntransformed, // Tiled
3314 fetchTransformed<BlendTransformed, QPixelLayout::BPP32>, // Transformed
3315 fetchTransformed<BlendTransformedTiled, QPixelLayout::BPP32>, // TransformedTiled
3316 fetchTransformedBilinear<BlendTransformedBilinear, QPixelLayout::BPP32>, // TransformedBilinear
3317 fetchTransformedBilinear<BlendTransformedBilinearTiled, QPixelLayout::BPP32> // TransformedBilinearTiled
3318};
3319
3320static_assert(std::size(sourceFetchAny32) == NBlendTypes);
3321
3322static inline SourceFetchProc getSourceFetch(TextureBlendType blendType, QImage::Format format)
3323{
3324 if (format == QImage::Format_RGB32 || format == QImage::Format_ARGB32_Premultiplied)
3325 return sourceFetchARGB32PM[blendType];
3326 if (blendType == BlendUntransformed || blendType == BlendTiled)
3327 return sourceFetchUntransformed[format];
3328 if (qPixelLayouts[format].bpp == QPixelLayout::BPP16)
3329 return sourceFetchAny16[blendType];
3330 if (qPixelLayouts[format].bpp == QPixelLayout::BPP32)
3331 return sourceFetchAny32[blendType];
3332 return sourceFetchGeneric[blendType];
3333}
3334
3335#if QT_CONFIG(raster_64bit)
3336static const SourceFetchProc64 sourceFetchGeneric64[] = {
3337 fetchUntransformed64, // Untransformed
3338 fetchUntransformed64, // Tiled
3339 fetchTransformed64<BlendTransformed>, // Transformed
3340 fetchTransformed64<BlendTransformedTiled>, // TransformedTiled
3341 fetchTransformedBilinear64<BlendTransformedBilinear>, // Bilinear
3342 fetchTransformedBilinear64<BlendTransformedBilinearTiled> // BilinearTiled
3343};
3344
3345static_assert(std::size(sourceFetchGeneric64) == NBlendTypes);
3346
3347static const SourceFetchProc64 sourceFetchRGBA64PM[] = {
3348 fetchUntransformedRGBA64PM, // Untransformed
3349 fetchUntransformedRGBA64PM, // Tiled
3350 fetchTransformed64<BlendTransformed>, // Transformed
3351 fetchTransformed64<BlendTransformedTiled>, // TransformedTiled
3352 fetchTransformedBilinear64<BlendTransformedBilinear>, // Bilinear
3353 fetchTransformedBilinear64<BlendTransformedBilinearTiled> // BilinearTiled
3354};
3355
3356static_assert(std::size(sourceFetchRGBA64PM) == NBlendTypes);
3357
3358static inline SourceFetchProc64 getSourceFetch64(TextureBlendType blendType, QImage::Format format)
3359{
3360 if (format == QImage::Format_RGBX64 || format == QImage::Format_RGBA64_Premultiplied)
3361 return sourceFetchRGBA64PM[blendType];
3362 return sourceFetchGeneric64[blendType];
3363}
3364#endif
3365
3366#if QT_CONFIG(raster_fp)
3367static const SourceFetchProcFP sourceFetchGenericFP[] = {
3368 fetchUntransformedFP, // Untransformed
3369 fetchUntransformedFP, // Tiled
3370 fetchTransformedFP<BlendTransformed>, // Transformed
3371 fetchTransformedFP<BlendTransformedTiled>, // TransformedTiled
3372 fetchTransformedBilinearFP<BlendTransformedBilinear>, // Bilinear
3373 fetchTransformedBilinearFP<BlendTransformedBilinearTiled> // BilinearTiled
3374};
3375
3376static_assert(std::size(sourceFetchGenericFP) == NBlendTypes);
3377
3378static inline SourceFetchProcFP getSourceFetchFP(TextureBlendType blendType, QImage::Format /*format*/)
3379{
3380 return sourceFetchGenericFP[blendType];
3381}
3382#endif
3383
3384#define FIXPT_BITS 8
3385#define FIXPT_SIZE (1<<FIXPT_BITS)
3386#define FIXPT_MAX (INT_MAX >> (FIXPT_BITS + 1))
3387
3388static uint qt_gradient_pixel_fixed(const QGradientData *data, int fixed_pos)
3389{
3390 int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
3391 return data->colorTable32[qt_gradient_clamp(data, ipos)];
3392}
3393
3394#if QT_CONFIG(raster_64bit)
3395static const QRgba64& qt_gradient_pixel64_fixed(const QGradientData *data, int fixed_pos)
3396{
3397 int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
3398 return data->colorTable64[qt_gradient_clamp(data, ipos)];
3399}
3400#endif
3401
3402#if QT_CONFIG(raster_fp)
3403static inline QRgbaFloat32 qt_gradient_pixelFP(const QGradientData *data, qreal pos)
3404{
3405 int ipos = int(pos * (GRADIENT_STOPTABLE_SIZE - 1) + qreal(0.5));
3406 QRgba64 rgb64 = data->colorTable64[qt_gradient_clamp(data, ipos)];
3407 return QRgbaFloat32::fromRgba64(red: rgb64.red(),green: rgb64.green(), blue: rgb64.blue(), alpha: rgb64.alpha());
3408}
3409
3410static inline QRgbaFloat32 qt_gradient_pixelFP_fixed(const QGradientData *data, int fixed_pos)
3411{
3412 int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
3413 QRgba64 rgb64 = data->colorTable64[qt_gradient_clamp(data, ipos)];
3414 return QRgbaFloat32::fromRgba64(red: rgb64.red(), green: rgb64.green(), blue: rgb64.blue(), alpha: rgb64.alpha());
3415}
3416#endif
3417
3418static void QT_FASTCALL getLinearGradientValues(LinearGradientValues *v, const QSpanData *data)
3419{
3420 v->dx = data->gradient.linear.end.x - data->gradient.linear.origin.x;
3421 v->dy = data->gradient.linear.end.y - data->gradient.linear.origin.y;
3422 v->l = v->dx * v->dx + v->dy * v->dy;
3423 v->off = 0;
3424 if (v->l != 0) {
3425 v->dx /= v->l;
3426 v->dy /= v->l;
3427 v->off = -v->dx * data->gradient.linear.origin.x - v->dy * data->gradient.linear.origin.y;
3428 }
3429}
3430
3431class GradientBase32
3432{
3433public:
3434 typedef uint Type;
3435 static Type null() { return 0; }
3436 static Type fetchSingle(const QGradientData& gradient, qreal v)
3437 {
3438 Q_ASSERT(std::isfinite(v));
3439 return qt_gradient_pixel(data: &gradient, pos: v);
3440 }
3441 static Type fetchSingle(const QGradientData& gradient, int v)
3442 {
3443 return qt_gradient_pixel_fixed(data: &gradient, fixed_pos: v);
3444 }
3445 static void memfill(Type *buffer, Type fill, int length)
3446 {
3447 qt_memfill32(buffer, fill, length);
3448 }
3449};
3450
3451#if QT_CONFIG(raster_64bit)
3452class GradientBase64
3453{
3454public:
3455 typedef QRgba64 Type;
3456 static Type null() { return QRgba64::fromRgba64(c: 0); }
3457 static Type fetchSingle(const QGradientData& gradient, qreal v)
3458 {
3459 Q_ASSERT(std::isfinite(v));
3460 return qt_gradient_pixel64(data: &gradient, pos: v);
3461 }
3462 static Type fetchSingle(const QGradientData& gradient, int v)
3463 {
3464 return qt_gradient_pixel64_fixed(data: &gradient, fixed_pos: v);
3465 }
3466 static void memfill(Type *buffer, Type fill, int length)
3467 {
3468 qt_memfill64((quint64*)buffer, fill, length);
3469 }
3470};
3471#endif
3472
3473#if QT_CONFIG(raster_fp)
3474class GradientBaseFP
3475{
3476public:
3477 typedef QRgbaFloat32 Type;
3478 static Type null() { return QRgbaFloat32::fromRgba64(red: 0,green: 0,blue: 0,alpha: 0); }
3479 static Type fetchSingle(const QGradientData& gradient, qreal v)
3480 {
3481 Q_ASSERT(std::isfinite(v));
3482 return qt_gradient_pixelFP(data: &gradient, pos: v);
3483 }
3484 static Type fetchSingle(const QGradientData& gradient, int v)
3485 {
3486 return qt_gradient_pixelFP_fixed(data: &gradient, fixed_pos: v);
3487 }
3488 static void memfill(Type *buffer, Type fill, int length)
3489 {
3490 quint64 fillCopy;
3491 memcpy(dest: &fillCopy, src: &fill, n: sizeof(quint64));
3492 qt_memfill64((quint64*)buffer, fillCopy, length);
3493 }
3494};
3495#endif
3496
3497template<class GradientBase, typename BlendType>
3498static inline const BlendType * QT_FASTCALL qt_fetch_linear_gradient_template(
3499 BlendType *buffer, const Operator *op, const QSpanData *data,
3500 int y, int x, int length)
3501{
3502 const BlendType *b = buffer;
3503 qreal t, inc;
3504
3505 bool affine = true;
3506 qreal rx=0, ry=0;
3507 if (op->linear.l == 0) {
3508 t = inc = 0;
3509 } else {
3510 rx = data->m21 * (y + qreal(0.5)) + data->m11 * (x + qreal(0.5)) + data->dx;
3511 ry = data->m22 * (y + qreal(0.5)) + data->m12 * (x + qreal(0.5)) + data->dy;
3512 t = op->linear.dx*rx + op->linear.dy*ry + op->linear.off;
3513 inc = op->linear.dx * data->m11 + op->linear.dy * data->m12;
3514 affine = !data->m13 && !data->m23;
3515
3516 if (affine) {
3517 t *= (GRADIENT_STOPTABLE_SIZE - 1);
3518 inc *= (GRADIENT_STOPTABLE_SIZE - 1);
3519 }
3520 }
3521
3522 const BlendType *end = buffer + length;
3523 if (affine) {
3524 if (inc > qreal(-1e-5) && inc < qreal(1e-5)) {
3525 if (std::abs(x: t) < FIXPT_MAX)
3526 GradientBase::memfill(buffer, GradientBase::fetchSingle(data->gradient, int(t * FIXPT_SIZE)), length);
3527 else
3528 GradientBase::memfill(buffer, GradientBase::fetchSingle(data->gradient, t / GRADIENT_STOPTABLE_SIZE), length);
3529 } else {
3530 if (std::abs(x: t) < FIXPT_MAX && std::abs(x: inc) < FIXPT_MAX && std::abs(x: t + inc * length) < FIXPT_MAX) {
3531 // we can use fixed point math
3532 int t_fixed = int(t * FIXPT_SIZE);
3533 int inc_fixed = int(inc * FIXPT_SIZE);
3534 while (buffer < end) {
3535 *buffer = GradientBase::fetchSingle(data->gradient, t_fixed);
3536 t_fixed += inc_fixed;
3537 ++buffer;
3538 }
3539 } else {
3540 // we have to fall back to float math
3541 while (buffer < end) {
3542 *buffer = GradientBase::fetchSingle(data->gradient, t/GRADIENT_STOPTABLE_SIZE);
3543 t += inc;
3544 ++buffer;
3545 }
3546 }
3547 }
3548 } else { // fall back to float math here as well
3549 qreal rw = data->m23 * (y + qreal(0.5)) + data->m13 * (x + qreal(0.5)) + data->m33;
3550 while (buffer < end) {
3551 qreal x = rx/rw;
3552 qreal y = ry/rw;
3553 t = (op->linear.dx*x + op->linear.dy *y) + op->linear.off;
3554
3555 *buffer = GradientBase::fetchSingle(data->gradient, t);
3556 rx += data->m11;
3557 ry += data->m12;
3558 rw += data->m13;
3559 if (!rw) {
3560 rw += data->m13;
3561 }
3562 ++buffer;
3563 }
3564 }
3565
3566 return b;
3567}
3568
3569static const uint * QT_FASTCALL qt_fetch_linear_gradient(uint *buffer, const Operator *op, const QSpanData *data,
3570 int y, int x, int length)
3571{
3572 return qt_fetch_linear_gradient_template<GradientBase32, uint>(buffer, op, data, y, x, length);
3573}
3574
3575#if QT_CONFIG(raster_64bit)
3576static const QRgba64 * QT_FASTCALL qt_fetch_linear_gradient_rgb64(QRgba64 *buffer, const Operator *op, const QSpanData *data,
3577 int y, int x, int length)
3578{
3579 return qt_fetch_linear_gradient_template<GradientBase64, QRgba64>(buffer, op, data, y, x, length);
3580}
3581#endif
3582#if QT_CONFIG(raster_fp)
3583static const QRgbaFloat32 * QT_FASTCALL qt_fetch_linear_gradient_rgbfp(QRgbaFloat32 *buffer, const Operator *op, const QSpanData *data,
3584 int y, int x, int length)
3585{
3586 return qt_fetch_linear_gradient_template<GradientBaseFP, QRgbaFloat32>(buffer, op, data, y, x, length);
3587}
3588#endif
3589
3590static void QT_FASTCALL getRadialGradientValues(RadialGradientValues *v, const QSpanData *data)
3591{
3592 v->dx = data->gradient.radial.center.x - data->gradient.radial.focal.x;
3593 v->dy = data->gradient.radial.center.y - data->gradient.radial.focal.y;
3594
3595 v->dr = data->gradient.radial.center.radius - data->gradient.radial.focal.radius;
3596 v->sqrfr = data->gradient.radial.focal.radius * data->gradient.radial.focal.radius;
3597
3598 v->a = v->dr * v->dr - v->dx*v->dx - v->dy*v->dy;
3599
3600 v->extended = !qFuzzyIsNull(d: data->gradient.radial.focal.radius) || v->a <= 0;
3601}
3602
3603template <class GradientBase>
3604class RadialFetchPlain : public GradientBase
3605{
3606public:
3607 typedef typename GradientBase::Type BlendType;
3608 static void fetch(BlendType *buffer, BlendType *end,
3609 const Operator *op, const QSpanData *data, qreal det,
3610 qreal delta_det, qreal delta_delta_det, qreal b, qreal delta_b)
3611 {
3612 if (op->radial.extended) {
3613 while (buffer < end) {
3614 BlendType result = GradientBase::null();
3615 if (det >= 0) {
3616 qreal w = qSqrt(v: det) - b;
3617 if (data->gradient.radial.focal.radius + op->radial.dr * w >= 0)
3618 result = GradientBase::fetchSingle(data->gradient, w);
3619 }
3620
3621 *buffer = result;
3622
3623 det += delta_det;
3624 delta_det += delta_delta_det;
3625 b += delta_b;
3626
3627 ++buffer;
3628 }
3629 } else {
3630 while (buffer < end) {
3631 BlendType result = GradientBase::null();
3632 if (det >= 0) {
3633 qreal w = qSqrt(v: det) - b;
3634 result = GradientBase::fetchSingle(data->gradient, w);
3635 }
3636
3637 *buffer++ = result;
3638
3639 det += delta_det;
3640 delta_det += delta_delta_det;
3641 b += delta_b;
3642 }
3643 }
3644 }
3645};
3646
3647const uint * QT_FASTCALL qt_fetch_radial_gradient_plain(uint *buffer, const Operator *op, const QSpanData *data,
3648 int y, int x, int length)
3649{
3650 return qt_fetch_radial_gradient_template<RadialFetchPlain<GradientBase32>, uint>(buffer, op, data, y, x, length);
3651}
3652
3653static SourceFetchProc qt_fetch_radial_gradient = qt_fetch_radial_gradient_plain;
3654
3655#if QT_CONFIG(raster_64bit)
3656const QRgba64 * QT_FASTCALL qt_fetch_radial_gradient_rgb64(QRgba64 *buffer, const Operator *op, const QSpanData *data,
3657 int y, int x, int length)
3658{
3659 return qt_fetch_radial_gradient_template<RadialFetchPlain<GradientBase64>, QRgba64>(buffer, op, data, y, x, length);
3660}
3661#endif
3662
3663#if QT_CONFIG(raster_fp)
3664static const QRgbaFloat32 * QT_FASTCALL qt_fetch_radial_gradient_rgbfp(QRgbaFloat32 *buffer, const Operator *op, const QSpanData *data,
3665 int y, int x, int length)
3666{
3667 return qt_fetch_radial_gradient_template<RadialFetchPlain<GradientBaseFP>, QRgbaFloat32>(buffer, op, data, y, x, length);
3668}
3669#endif
3670
3671template <class GradientBase, typename BlendType>
3672static inline const BlendType * QT_FASTCALL qt_fetch_conical_gradient_template(
3673 BlendType *buffer, const QSpanData *data,
3674 int y, int x, int length)
3675{
3676 const BlendType *b = buffer;
3677 qreal rx = data->m21 * (y + qreal(0.5))
3678 + data->dx + data->m11 * (x + qreal(0.5));
3679 qreal ry = data->m22 * (y + qreal(0.5))
3680 + data->dy + data->m12 * (x + qreal(0.5));
3681 bool affine = !data->m13 && !data->m23;
3682
3683 const qreal inv2pi = M_1_PI / 2.0;
3684
3685 const BlendType *end = buffer + length;
3686 if (affine) {
3687 rx -= data->gradient.conical.center.x;
3688 ry -= data->gradient.conical.center.y;
3689 while (buffer < end) {
3690 qreal angle = qAtan2(y: ry, x: rx) + data->gradient.conical.angle;
3691
3692 *buffer = GradientBase::fetchSingle(data->gradient, 1 - angle * inv2pi);
3693
3694 rx += data->m11;
3695 ry += data->m12;
3696 ++buffer;
3697 }
3698 } else {
3699 qreal rw = data->m23 * (y + qreal(0.5))
3700 + data->m33 + data->m13 * (x + qreal(0.5));
3701 if (!rw)
3702 rw = 1;
3703 while (buffer < end) {
3704 qreal angle = qAtan2(y: ry/rw - data->gradient.conical.center.x,
3705 x: rx/rw - data->gradient.conical.center.y)
3706 + data->gradient.conical.angle;
3707
3708 *buffer = GradientBase::fetchSingle(data->gradient, 1 - angle * inv2pi);
3709
3710 rx += data->m11;
3711 ry += data->m12;
3712 rw += data->m13;
3713 if (!rw) {
3714 rw += data->m13;
3715 }
3716 ++buffer;
3717 }
3718 }
3719 return b;
3720}
3721
3722static const uint * QT_FASTCALL qt_fetch_conical_gradient(uint *buffer, const Operator *, const QSpanData *data,
3723 int y, int x, int length)
3724{
3725 return qt_fetch_conical_gradient_template<GradientBase32, uint>(buffer, data, y, x, length);
3726}
3727
3728#if QT_CONFIG(raster_64bit)
3729static const QRgba64 * QT_FASTCALL qt_fetch_conical_gradient_rgb64(QRgba64 *buffer, const Operator *, const QSpanData *data,
3730 int y, int x, int length)
3731{
3732 return qt_fetch_conical_gradient_template<GradientBase64, QRgba64>(buffer, data, y, x, length);
3733}
3734#endif
3735
3736#if QT_CONFIG(raster_fp)
3737static const QRgbaFloat32 * QT_FASTCALL qt_fetch_conical_gradient_rgbfp(QRgbaFloat32 *buffer, const Operator *, const QSpanData *data,
3738 int y, int x, int length)
3739{
3740 return qt_fetch_conical_gradient_template<GradientBaseFP, QRgbaFloat32>(buffer, data, y, x, length);
3741}
3742#endif
3743
3744extern CompositionFunctionSolid qt_functionForModeSolid_C[];
3745extern CompositionFunctionSolid64 qt_functionForModeSolid64_C[];
3746extern CompositionFunctionSolidFP qt_functionForModeSolidFP_C[];
3747
3748static const CompositionFunctionSolid *functionForModeSolid = qt_functionForModeSolid_C;
3749#if QT_CONFIG(raster_64bit)
3750static const CompositionFunctionSolid64 *functionForModeSolid64 = qt_functionForModeSolid64_C;
3751#endif
3752#if QT_CONFIG(raster_fp)
3753static const CompositionFunctionSolidFP *functionForModeSolidFP = qt_functionForModeSolidFP_C;
3754#endif
3755
3756extern CompositionFunction qt_functionForMode_C[];
3757extern CompositionFunction64 qt_functionForMode64_C[];
3758extern CompositionFunctionFP qt_functionForModeFP_C[];
3759
3760static const CompositionFunction *functionForMode = qt_functionForMode_C;
3761#if QT_CONFIG(raster_64bit)
3762static const CompositionFunction64 *functionForMode64 = qt_functionForMode64_C;
3763#endif
3764#if QT_CONFIG(raster_fp)
3765static const CompositionFunctionFP *functionForModeFP = qt_functionForModeFP_C;
3766#endif
3767
3768static TextureBlendType getBlendType(const QSpanData *data)
3769{
3770 TextureBlendType ft;
3771 if (data->texture.type == QTextureData::Pattern)
3772 ft = BlendTiled;
3773 else if (data->txop <= QTransform::TxTranslate)
3774 if (data->texture.type == QTextureData::Tiled)
3775 ft = BlendTiled;
3776 else
3777 ft = BlendUntransformed;
3778 else if (data->bilinear)
3779 if (data->texture.type == QTextureData::Tiled)
3780 ft = BlendTransformedBilinearTiled;
3781 else
3782 ft = BlendTransformedBilinear;
3783 else
3784 if (data->texture.type == QTextureData::Tiled)
3785 ft = BlendTransformedTiled;
3786 else
3787 ft = BlendTransformed;
3788 return ft;
3789}
3790
3791static inline Operator getOperator(const QSpanData *data, const QT_FT_Span *spans, int spanCount)
3792{
3793 Operator op;
3794 bool solidSource = false;
3795 switch(data->type) {
3796 case QSpanData::Solid:
3797 solidSource = data->solidColor.alphaF() >= 1.0f;
3798 op.noGradient = {};
3799 op.srcFetch = nullptr;
3800 op.srcFetch64 = nullptr;
3801 op.srcFetchFP = nullptr;
3802 break;
3803 case QSpanData::LinearGradient:
3804 solidSource = !data->gradient.alphaColor;
3805 getLinearGradientValues(v: &op.linear, data);
3806 op.srcFetch = qt_fetch_linear_gradient;
3807#if QT_CONFIG(raster_64bit)
3808 op.srcFetch64 = qt_fetch_linear_gradient_rgb64;
3809#endif
3810#if QT_CONFIG(raster_fp)
3811 op.srcFetchFP = qt_fetch_linear_gradient_rgbfp;
3812#endif
3813 break;
3814 case QSpanData::RadialGradient:
3815 solidSource = !data->gradient.alphaColor;
3816 getRadialGradientValues(v: &op.radial, data);
3817 op.srcFetch = qt_fetch_radial_gradient;
3818#if QT_CONFIG(raster_64bit)
3819 op.srcFetch64 = qt_fetch_radial_gradient_rgb64;
3820#endif
3821#if QT_CONFIG(raster_fp)
3822 op.srcFetchFP = qt_fetch_radial_gradient_rgbfp;
3823#endif
3824 break;
3825 case QSpanData::ConicalGradient:
3826 solidSource = !data->gradient.alphaColor;
3827 op.noGradient = {}; // sic!
3828 op.srcFetch = qt_fetch_conical_gradient;
3829#if QT_CONFIG(raster_64bit)
3830 op.srcFetch64 = qt_fetch_conical_gradient_rgb64;
3831#endif
3832#if QT_CONFIG(raster_fp)
3833 op.srcFetchFP = qt_fetch_conical_gradient_rgbfp;
3834#endif
3835 break;
3836 case QSpanData::Texture:
3837 solidSource = !data->texture.hasAlpha;
3838 op.noGradient = {};
3839 op.srcFetch = getSourceFetch(blendType: getBlendType(data), format: data->texture.format);
3840#if QT_CONFIG(raster_64bit)
3841 op.srcFetch64 = getSourceFetch64(blendType: getBlendType(data), format: data->texture.format);
3842#endif
3843#if QT_CONFIG(raster_fp)
3844 op.srcFetchFP = getSourceFetchFP(blendType: getBlendType(data), data->texture.format);
3845#endif
3846 break;
3847 default:
3848 Q_UNREACHABLE();
3849 break;
3850 }
3851#if !QT_CONFIG(raster_64bit)
3852 op.srcFetch64 = nullptr;
3853#endif
3854#if !QT_CONFIG(raster_fp)
3855 op.srcFetchFP = nullptr;
3856#endif
3857
3858 op.mode = data->rasterBuffer->compositionMode;
3859 if (op.mode == QPainter::CompositionMode_SourceOver && solidSource)
3860 op.mode = QPainter::CompositionMode_Source;
3861
3862 op.destFetch = destFetchProc[data->rasterBuffer->format];
3863#if QT_CONFIG(raster_64bit)
3864 op.destFetch64 = destFetchProc64[data->rasterBuffer->format];
3865#else
3866 op.destFetch64 = nullptr;
3867#endif
3868#if QT_CONFIG(raster_fp)
3869 op.destFetchFP = destFetchProcFP[data->rasterBuffer->format];
3870#else
3871 op.destFetchFP = nullptr;
3872#endif
3873 if (op.mode == QPainter::CompositionMode_Source &&
3874 (data->type != QSpanData::Texture || data->texture.const_alpha == 256)) {
3875 const QT_FT_Span *lastSpan = spans + spanCount;
3876 bool alphaSpans = false;
3877 while (spans < lastSpan) {
3878 if (spans->coverage != 255) {
3879 alphaSpans = true;
3880 break;
3881 }
3882 ++spans;
3883 }
3884 if (!alphaSpans && spanCount > 0) {
3885 // If all spans are opaque we do not need to fetch dest.
3886 // But don't clear passthrough destFetch as they are just as fast and save destStore.
3887 if (op.destFetch != destFetchARGB32P)
3888 op.destFetch = destFetchUndefined;
3889#if QT_CONFIG(raster_64bit)
3890 if (op.destFetch64 != destFetchRGB64)
3891 op.destFetch64 = destFetch64Undefined;
3892#endif
3893#if QT_CONFIG(raster_fp)
3894 if (op.destFetchFP != destFetchRGBFP)
3895 op.destFetchFP = destFetchFPUndefined;
3896#endif
3897 }
3898 }
3899
3900 op.destStore = destStoreProc[data->rasterBuffer->format];
3901 op.funcSolid = functionForModeSolid[op.mode];
3902 op.func = functionForMode[op.mode];
3903#if QT_CONFIG(raster_64bit)
3904 op.destStore64 = destStoreProc64[data->rasterBuffer->format];
3905 op.funcSolid64 = functionForModeSolid64[op.mode];
3906 op.func64 = functionForMode64[op.mode];
3907#else
3908 op.destStore64 = nullptr;
3909 op.funcSolid64 = nullptr;
3910 op.func64 = nullptr;
3911#endif
3912#if QT_CONFIG(raster_fp)
3913 op.destStoreFP = destStoreFP;
3914 op.funcSolidFP = functionForModeSolidFP[op.mode];
3915 op.funcFP = functionForModeFP[op.mode];
3916#else
3917 op.destStoreFP = nullptr;
3918 op.funcSolidFP = nullptr;
3919 op.funcFP = nullptr;
3920#endif
3921
3922 return op;
3923}
3924
3925static void spanfill_from_first(QRasterBuffer *rasterBuffer, QPixelLayout::BPP bpp, int x, int y, int length)
3926{
3927 switch (bpp) {
3928 case QPixelLayout::BPP32FPx4: {
3929 QRgbaFloat32 *dest = reinterpret_cast<QRgbaFloat32 *>(rasterBuffer->scanLine(y)) + x;
3930 qt_memfill_template(dest: dest + 1, color: dest[0], count: length - 1);
3931 break;
3932 }
3933 case QPixelLayout::BPP16FPx4:
3934 case QPixelLayout::BPP64: {
3935 quint64 *dest = reinterpret_cast<quint64 *>(rasterBuffer->scanLine(y)) + x;
3936 qt_memfill_template(dest: dest + 1, color: dest[0], count: length - 1);
3937 break;
3938 }
3939 case QPixelLayout::BPP32: {
3940 quint32 *dest = reinterpret_cast<quint32 *>(rasterBuffer->scanLine(y)) + x;
3941 qt_memfill_template(dest: dest + 1, color: dest[0], count: length - 1);
3942 break;
3943 }
3944 case QPixelLayout::BPP24: {
3945 quint24 *dest = reinterpret_cast<quint24 *>(rasterBuffer->scanLine(y)) + x;
3946 qt_memfill_template(dest: dest + 1, color: dest[0], count: length - 1);
3947 break;
3948 }
3949 case QPixelLayout::BPP16: {
3950 quint16 *dest = reinterpret_cast<quint16 *>(rasterBuffer->scanLine(y)) + x;
3951 qt_memfill_template(dest: dest + 1, color: dest[0], count: length - 1);
3952 break;
3953 }
3954 case QPixelLayout::BPP8: {
3955 uchar *dest = rasterBuffer->scanLine(y) + x;
3956 memset(s: dest + 1, c: dest[0], n: length - 1);
3957 break;
3958 }
3959 default:
3960 Q_UNREACHABLE();
3961 }
3962}
3963
3964
3965// -------------------- blend methods ---------------------
3966
3967#if QT_CONFIG(qtgui_threadpool)
3968#define QT_THREAD_PARALLEL_FILLS(function) \
3969 const int segments = (count + 32) / 64; \
3970 QThreadPool *threadPool = QGuiApplicationPrivate::qtGuiThreadPool(); \
3971 if (segments > 1 && qPixelLayouts[data->rasterBuffer->format].bpp >= QPixelLayout::BPP8 \
3972 && threadPool && !threadPool->contains(QThread::currentThread())) { \
3973 QSemaphore semaphore; \
3974 int c = 0; \
3975 for (int i = 0; i < segments; ++i) { \
3976 int cn = (count - c) / (segments - i); \
3977 threadPool->start([&, c, cn]() { \
3978 function(c, c + cn); \
3979 semaphore.release(1); \
3980 }, 1); \
3981 c += cn; \
3982 } \
3983 semaphore.acquire(segments); \
3984 } else \
3985 function(0, count)
3986#else
3987#define QT_THREAD_PARALLEL_FILLS(function) function(0, count)
3988#endif
3989
3990static void blend_color_generic(int count, const QT_FT_Span *spans, void *userData)
3991{
3992 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
3993 const Operator op = getOperator(data, spans: nullptr, spanCount: 0);
3994 const uint color = data->solidColor.rgba();
3995 const bool solidFill = op.mode == QPainter::CompositionMode_Source;
3996 const QPixelLayout::BPP bpp = qPixelLayouts[data->rasterBuffer->format].bpp;
3997
3998 auto function = [=] (int cStart, int cEnd) {
3999 alignas(16) Q_DECL_UNINITIALIZED uint buffer[BufferSize];
4000 for (int c = cStart; c < cEnd; ++c) {
4001 int x = spans[c].x;
4002 int length = spans[c].len;
4003 if (solidFill && bpp >= QPixelLayout::BPP8 && spans[c].coverage == 255 && length && op.destStore) {
4004 // If dest doesn't matter we don't need to bother with blending or converting all the identical pixels
4005 op.destStore(data->rasterBuffer, x, spans[c].y, &color, 1);
4006 spanfill_from_first(rasterBuffer: data->rasterBuffer, bpp, x, y: spans[c].y, length);
4007 length = 0;
4008 }
4009
4010 while (length) {
4011 int l = qMin(a: BufferSize, b: length);
4012 uint *dest = op.destFetch(buffer, data->rasterBuffer, x, spans[c].y, l);
4013 op.funcSolid(dest, l, color, spans[c].coverage);
4014 if (op.destStore)
4015 op.destStore(data->rasterBuffer, x, spans[c].y, dest, l);
4016 length -= l;
4017 x += l;
4018 }
4019 }
4020 };
4021 QT_THREAD_PARALLEL_FILLS(function);
4022}
4023
4024static void blend_color_argb(int count, const QT_FT_Span *spans, void *userData)
4025{
4026 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4027
4028 const Operator op = getOperator(data, spans: nullptr, spanCount: 0);
4029 const uint color = data->solidColor.rgba();
4030
4031 if (op.mode == QPainter::CompositionMode_Source) {
4032 // inline for performance
4033 while (count--) {
4034 uint *target = ((uint *)data->rasterBuffer->scanLine(y: spans->y)) + spans->x;
4035 if (spans->coverage == 255) {
4036 qt_memfill(dest: target, color, count: spans->len);
4037#ifdef __SSE2__
4038 } else if (spans->len > 16) {
4039 op.funcSolid(target, spans->len, color, spans->coverage);
4040#endif
4041 } else {
4042 uint c = BYTE_MUL(x: color, a: spans->coverage);
4043 int ialpha = 255 - spans->coverage;
4044 for (int i = 0; i < spans->len; ++i)
4045 target[i] = c + BYTE_MUL(x: target[i], a: ialpha);
4046 }
4047 ++spans;
4048 }
4049 return;
4050 }
4051 const auto funcSolid = op.funcSolid;
4052 auto function = [=] (int cStart, int cEnd) {
4053 for (int c = cStart; c < cEnd; ++c) {
4054 uint *target = ((uint *)data->rasterBuffer->scanLine(y: spans[c].y)) + spans[c].x;
4055 funcSolid(target, spans[c].len, color, spans[c].coverage);
4056 }
4057 };
4058 QT_THREAD_PARALLEL_FILLS(function);
4059}
4060
4061static void blend_color_generic_rgb64(int count, const QT_FT_Span *spans, void *userData)
4062{
4063#if QT_CONFIG(raster_64bit)
4064 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4065 const Operator op = getOperator(data, spans: nullptr, spanCount: 0);
4066 if (!op.funcSolid64) {
4067 qCDebug(lcQtGuiDrawHelper, "blend_color_generic_rgb64: unsupported 64bit blend attempted, falling back to 32-bit");
4068 return blend_color_generic(count, spans, userData);
4069 }
4070
4071 const QRgba64 color = data->solidColor.rgba64();
4072 const bool solidFill = op.mode == QPainter::CompositionMode_Source;
4073 const QPixelLayout::BPP bpp = qPixelLayouts[data->rasterBuffer->format].bpp;
4074
4075 auto function = [=, &op] (int cStart, int cEnd)
4076 {
4077 alignas(16) Q_DECL_UNINITIALIZED QRgba64 buffer[BufferSize];
4078 for (int c = cStart; c < cEnd; ++c) {
4079 int x = spans[c].x;
4080 int length = spans[c].len;
4081 if (solidFill && bpp >= QPixelLayout::BPP8 && spans[c].coverage == 255 && length && op.destStore64) {
4082 // If dest doesn't matter we don't need to bother with blending or converting all the identical pixels
4083 op.destStore64(data->rasterBuffer, x, spans[c].y, &color, 1);
4084 spanfill_from_first(rasterBuffer: data->rasterBuffer, bpp, x, y: spans[c].y, length);
4085 length = 0;
4086 }
4087
4088 while (length) {
4089 int l = qMin(a: BufferSize, b: length);
4090 QRgba64 *dest = op.destFetch64(buffer, data->rasterBuffer, x, spans[c].y, l);
4091 op.funcSolid64(dest, l, color, spans[c].coverage);
4092 if (op.destStore64)
4093 op.destStore64(data->rasterBuffer, x, spans[c].y, dest, l);
4094 length -= l;
4095 x += l;
4096 }
4097 }
4098 };
4099 QT_THREAD_PARALLEL_FILLS(function);
4100#else
4101 blend_color_generic(count, spans, userData);
4102#endif
4103}
4104
4105static void blend_color_generic_fp(int count, const QT_FT_Span *spans, void *userData)
4106{
4107#if QT_CONFIG(raster_fp)
4108 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4109 const Operator op = getOperator(data, spans: nullptr, spanCount: 0);
4110 if (!op.funcSolidFP || !op.destFetchFP) {
4111 qCDebug(lcQtGuiDrawHelper, "blend_color_generic_fp: unsupported 4xF16 blend attempted, falling back to 32-bit");
4112 return blend_color_generic(count, spans, userData);
4113 }
4114
4115 float r, g, b, a;
4116 data->solidColor.getRgbF(r: &r, g: &g, b: &b, a: &a);
4117 const QRgbaFloat32 color{.r: r, .g: g, .b: b, .a: a};
4118 const bool solidFill = op.mode == QPainter::CompositionMode_Source;
4119 QPixelLayout::BPP bpp = qPixelLayouts[data->rasterBuffer->format].bpp;
4120
4121 auto function = [=, &op] (int cStart, int cEnd)
4122 {
4123 alignas(16) Q_DECL_UNINITIALIZED QRgbaFloat32 buffer[BufferSize];
4124 for (int c = cStart; c < cEnd; ++c) {
4125 int x = spans[c].x;
4126 int length = spans[c].len;
4127 if (solidFill && bpp >= QPixelLayout::BPP8 && spans[c].coverage == 255 && length && op.destStoreFP) {
4128 // If dest doesn't matter we don't need to bother with blending or converting all the identical pixels
4129 op.destStoreFP(data->rasterBuffer, x, spans[c].y, &color, 1);
4130 spanfill_from_first(rasterBuffer: data->rasterBuffer, bpp, x, y: spans[c].y, length);
4131 length = 0;
4132 }
4133
4134 while (length) {
4135 int l = qMin(a: BufferSize, b: length);
4136 QRgbaFloat32 *dest = op.destFetchFP(buffer, data->rasterBuffer, x, spans[c].y, l);
4137 op.funcSolidFP(dest, l, color, spans[c].coverage);
4138 if (op.destStoreFP)
4139 op.destStoreFP(data->rasterBuffer, x, spans[c].y, dest, l);
4140 length -= l;
4141 x += l;
4142 }
4143 }
4144 };
4145 QT_THREAD_PARALLEL_FILLS(function);
4146#else
4147 blend_color_generic(count, spans, userData);
4148#endif
4149}
4150
4151template <typename T>
4152void handleSpans(int count, const QT_FT_Span *spans, const QSpanData *data, const Operator &op)
4153{
4154 const int const_alpha = (data->type == QSpanData::Texture) ? data->texture.const_alpha : 256;
4155 const bool solidSource = op.mode == QPainter::CompositionMode_Source && const_alpha == 256;
4156
4157 auto function = [=, &op] (int cStart, int cEnd)
4158 {
4159 T Q_DECL_UNINITIALIZED handler(data, op);
4160 int coverage = 0;
4161 for (int c = cStart; c < cEnd;) {
4162 if (!spans[c].len) {
4163 ++c;
4164 continue;
4165 }
4166 int x = spans[c].x;
4167 const int y = spans[c].y;
4168 int right = x + spans[c].len;
4169 const bool fetchDest = !solidSource || spans[c].coverage < 255;
4170
4171 // compute length of adjacent spans
4172 for (int i = c + 1; i < cEnd && spans[i].y == y && spans[i].x == right && fetchDest == (!solidSource || spans[i].coverage < 255); ++i)
4173 right += spans[i].len;
4174 int length = right - x;
4175
4176 while (length) {
4177 int l = qMin(a: BufferSize, b: length);
4178 length -= l;
4179
4180 int process_length = l;
4181 int process_x = x;
4182
4183 const auto *src = handler.fetch(process_x, y, process_length, fetchDest);
4184 int offset = 0;
4185 while (l > 0) {
4186 if (x == spans[c].x) // new span?
4187 coverage = (spans[c].coverage * const_alpha) >> 8;
4188
4189 int right = spans[c].x + spans[c].len;
4190 int len = qMin(a: l, b: right - x);
4191
4192 handler.process(x, y, len, coverage, src, offset);
4193
4194 l -= len;
4195 x += len;
4196 offset += len;
4197
4198 if (x == right) // done with current span?
4199 ++c;
4200 }
4201 handler.store(process_x, y, process_length);
4202 }
4203 }
4204 };
4205 QT_THREAD_PARALLEL_FILLS(function);
4206}
4207
4208struct QBlendBase
4209{
4210 const QSpanData *data;
4211 const Operator &op;
4212};
4213
4214class BlendSrcGeneric : public QBlendBase
4215{
4216public:
4217 uint *dest = nullptr;
4218 alignas(16) uint buffer[BufferSize];
4219 alignas(16) uint src_buffer[BufferSize];
4220 BlendSrcGeneric(const QSpanData *d, const Operator &o)
4221 : QBlendBase{.data: d, .op: o}
4222 {
4223 }
4224
4225 const uint *fetch(int x, int y, int len, bool fetchDest)
4226 {
4227 if (fetchDest || op.destFetch == destFetchARGB32P)
4228 dest = op.destFetch(buffer, data->rasterBuffer, x, y, len);
4229 else
4230 dest = buffer;
4231 return op.srcFetch(src_buffer, &op, data, y, x, len);
4232 }
4233
4234 void process(int, int, int len, int coverage, const uint *src, int offset)
4235 {
4236 op.func(dest + offset, src + offset, len, coverage);
4237 }
4238
4239 void store(int x, int y, int len)
4240 {
4241 if (op.destStore)
4242 op.destStore(data->rasterBuffer, x, y, dest, len);
4243 }
4244};
4245
4246#if QT_CONFIG(raster_64bit)
4247class BlendSrcGenericRGB64 : public QBlendBase
4248{
4249public:
4250 QRgba64 *dest = nullptr;
4251 alignas(16) QRgba64 buffer[BufferSize];
4252 alignas(16) QRgba64 src_buffer[BufferSize];
4253 BlendSrcGenericRGB64(const QSpanData *d, const Operator &o)
4254 : QBlendBase{.data: d, .op: o}
4255 {
4256 }
4257
4258 bool isSupported() const
4259 {
4260 return op.func64 && op.destFetch64;
4261 }
4262
4263 const QRgba64 *fetch(int x, int y, int len, bool fetchDest)
4264 {
4265 if (fetchDest || op.destFetch64 == destFetchRGB64)
4266 dest = op.destFetch64(buffer, data->rasterBuffer, x, y, len);
4267 else
4268 dest = buffer;
4269 return op.srcFetch64(src_buffer, &op, data, y, x, len);
4270 }
4271
4272 void process(int, int, int len, int coverage, const QRgba64 *src, int offset)
4273 {
4274 op.func64(dest + offset, src + offset, len, coverage);
4275 }
4276
4277 void store(int x, int y, int len)
4278 {
4279 if (op.destStore64)
4280 op.destStore64(data->rasterBuffer, x, y, dest, len);
4281 }
4282};
4283#endif
4284
4285#if QT_CONFIG(raster_fp)
4286class BlendSrcGenericRGBFP : public QBlendBase
4287{
4288public:
4289 QRgbaFloat32 *dest = nullptr;
4290 alignas(16) QRgbaFloat32 buffer[BufferSize];
4291 alignas(16) QRgbaFloat32 src_buffer[BufferSize];
4292 BlendSrcGenericRGBFP(const QSpanData *d, const Operator &o)
4293 : QBlendBase{.data: d, .op: o}
4294 {
4295 }
4296
4297 bool isSupported() const
4298 {
4299 return op.funcFP && op.destFetchFP && op.srcFetchFP;
4300 }
4301
4302 const QRgbaFloat32 *fetch(int x, int y, int len, bool fetchDest)
4303 {
4304 if (fetchDest || op.destFetchFP == destFetchRGBFP)
4305 dest = op.destFetchFP(buffer, data->rasterBuffer, x, y, len);
4306 else
4307 dest = buffer;
4308 return op.srcFetchFP(src_buffer, &op, data, y, x, len);
4309 }
4310
4311 void process(int, int, int len, int coverage, const QRgbaFloat32 *src, int offset)
4312 {
4313 op.funcFP(dest + offset, src + offset, len, coverage);
4314 }
4315
4316 void store(int x, int y, int len)
4317 {
4318 if (op.destStoreFP)
4319 op.destStoreFP(data->rasterBuffer, x, y, dest, len);
4320 }
4321};
4322#endif
4323
4324static void blend_src_generic(int count, const QT_FT_Span *spans, void *userData)
4325{
4326 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4327 const Operator op = getOperator(data, spans: nullptr, spanCount: 0);
4328 handleSpans<BlendSrcGeneric>(count, spans, data, op);
4329}
4330
4331#if QT_CONFIG(raster_64bit)
4332static void blend_src_generic_rgb64(int count, const QT_FT_Span *spans, void *userData)
4333{
4334 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4335 const Operator op = getOperator(data, spans: nullptr, spanCount: 0);
4336 if (op.func64 && op.destFetch64) {
4337 handleSpans<BlendSrcGenericRGB64>(count, spans, data, op);
4338 } else {
4339 qCDebug(lcQtGuiDrawHelper, "blend_src_generic_rgb64: unsupported 64-bit blend attempted, falling back to 32-bit");
4340 handleSpans<BlendSrcGeneric>(count, spans, data, op);
4341 }
4342}
4343#endif
4344
4345#if QT_CONFIG(raster_fp)
4346static void blend_src_generic_fp(int count, const QT_FT_Span *spans, void *userData)
4347{
4348 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4349 const Operator op = getOperator(data, spans, spanCount: count);
4350 if (op.funcFP && op.destFetchFP && op.srcFetchFP) {
4351 handleSpans<BlendSrcGenericRGBFP>(count, spans, data, op);
4352 } else {
4353 qCDebug(lcQtGuiDrawHelper, "blend_src_generic_fp: unsupported 4xFP blend attempted, falling back to 32-bit");
4354 handleSpans<BlendSrcGeneric>(count, spans, data, op);
4355 }
4356}
4357#endif
4358
4359static void blend_untransformed_generic(int count, const QT_FT_Span *spans, void *userData)
4360{
4361 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4362
4363 const Operator op = getOperator(data, spans, spanCount: count);
4364
4365 const int image_width = data->texture.width;
4366 const int image_height = data->texture.height;
4367 const int const_alpha = data->texture.const_alpha;
4368 const int xoff = -qRound(d: -data->dx);
4369 const int yoff = -qRound(d: -data->dy);
4370 const bool solidSource = op.mode == QPainter::CompositionMode_Source && const_alpha == 256 && op.destFetch != destFetchARGB32P;
4371
4372 auto function = [=, &op] (int cStart, int cEnd)
4373 {
4374 alignas(16) Q_DECL_UNINITIALIZED uint buffer[BufferSize];
4375 alignas(16) Q_DECL_UNINITIALIZED uint src_buffer[BufferSize];
4376 for (int c = cStart; c < cEnd; ++c) {
4377 if (!spans[c].len)
4378 continue;
4379 int x = spans[c].x;
4380 int length = spans[c].len;
4381 int sx = xoff + x;
4382 int sy = yoff + spans[c].y;
4383 const bool fetchDest = !solidSource || spans[c].coverage < 255;
4384 if (sy >= 0 && sy < image_height && sx < image_width) {
4385 if (sx < 0) {
4386 x -= sx;
4387 length += sx;
4388 sx = 0;
4389 }
4390 if (sx + length > image_width)
4391 length = image_width - sx;
4392 if (length > 0) {
4393 const int coverage = (spans[c].coverage * const_alpha) >> 8;
4394 while (length) {
4395 int l = qMin(a: BufferSize, b: length);
4396 const uint *src = op.srcFetch(src_buffer, &op, data, sy, sx, l);
4397 uint *dest = fetchDest ? op.destFetch(buffer, data->rasterBuffer, x, spans[c].y, l) : buffer;
4398 op.func(dest, src, l, coverage);
4399 if (op.destStore)
4400 op.destStore(data->rasterBuffer, x, spans[c].y, dest, l);
4401 x += l;
4402 sx += l;
4403 length -= l;
4404 }
4405 }
4406 }
4407 }
4408 };
4409 QT_THREAD_PARALLEL_FILLS(function);
4410}
4411
4412#if QT_CONFIG(raster_64bit)
4413static void blend_untransformed_generic_rgb64(int count, const QT_FT_Span *spans, void *userData)
4414{
4415 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4416
4417 const Operator op = getOperator(data, spans, spanCount: count);
4418 if (!op.func64) {
4419 qCDebug(lcQtGuiDrawHelper, "blend_untransformed_generic_rgb64: unsupported 64-bit blend attempted, falling back to 32-bit");
4420 return blend_untransformed_generic(count, spans, userData);
4421 }
4422
4423 const int image_width = data->texture.width;
4424 const int image_height = data->texture.height;
4425 const int const_alpha = data->texture.const_alpha;
4426 const int xoff = -qRound(d: -data->dx);
4427 const int yoff = -qRound(d: -data->dy);
4428 const bool solidSource = op.mode == QPainter::CompositionMode_Source && const_alpha == 256 && op.destFetch64 != destFetchRGB64;
4429
4430 auto function = [=, &op] (int cStart, int cEnd)
4431 {
4432 alignas(16) Q_DECL_UNINITIALIZED QRgba64 buffer[BufferSize];
4433 alignas(16) Q_DECL_UNINITIALIZED QRgba64 src_buffer[BufferSize];
4434 for (int c = cStart; c < cEnd; ++c) {
4435 if (!spans[c].len)
4436 continue;
4437 int x = spans[c].x;
4438 int length = spans[c].len;
4439 int sx = xoff + x;
4440 int sy = yoff + spans[c].y;
4441 const bool fetchDest = !solidSource || spans[c].coverage < 255;
4442 if (sy >= 0 && sy < image_height && sx < image_width) {
4443 if (sx < 0) {
4444 x -= sx;
4445 length += sx;
4446 sx = 0;
4447 }
4448 if (sx + length > image_width)
4449 length = image_width - sx;
4450 if (length > 0) {
4451 const int coverage = (spans[c].coverage * const_alpha) >> 8;
4452 while (length) {
4453 int l = qMin(a: BufferSize, b: length);
4454 const QRgba64 *src = op.srcFetch64(src_buffer, &op, data, sy, sx, l);
4455 QRgba64 *dest = fetchDest ? op.destFetch64(buffer, data->rasterBuffer, x, spans[c].y, l) : buffer;
4456 op.func64(dest, src, l, coverage);
4457 if (op.destStore64)
4458 op.destStore64(data->rasterBuffer, x, spans[c].y, dest, l);
4459 x += l;
4460 sx += l;
4461 length -= l;
4462 }
4463 }
4464 }
4465 }
4466 };
4467 QT_THREAD_PARALLEL_FILLS(function);
4468}
4469#endif
4470
4471#if QT_CONFIG(raster_fp)
4472static void blend_untransformed_generic_fp(int count, const QT_FT_Span *spans, void *userData)
4473{
4474 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4475
4476 const Operator op = getOperator(data, spans, spanCount: count);
4477 if (!op.funcFP) {
4478 qCDebug(lcQtGuiDrawHelper, "blend_untransformed_generic_rgbaf16: unsupported 4xFP16 blend attempted, falling back to 32-bit");
4479 return blend_untransformed_generic(count, spans, userData);
4480 }
4481
4482 const int image_width = data->texture.width;
4483 const int image_height = data->texture.height;
4484 const int xoff = -qRound(d: -data->dx);
4485 const int yoff = -qRound(d: -data->dy);
4486 const bool solidSource = op.mode == QPainter::CompositionMode_Source && data->texture.const_alpha == 256 && op.destFetchFP != destFetchRGBFP;
4487
4488 auto function = [=, &op] (int cStart, int cEnd)
4489 {
4490 alignas(16) Q_DECL_UNINITIALIZED QRgbaFloat32 buffer[BufferSize];
4491 alignas(16) Q_DECL_UNINITIALIZED QRgbaFloat32 src_buffer[BufferSize];
4492 for (int c = cStart; c < cEnd; ++c) {
4493 if (!spans[c].len)
4494 continue;
4495 int x = spans[c].x;
4496 int length = spans[c].len;
4497 int sx = xoff + x;
4498 int sy = yoff + spans[c].y;
4499 const bool fetchDest = !solidSource || spans[c].coverage < 255;
4500 if (sy >= 0 && sy < image_height && sx < image_width) {
4501 if (sx < 0) {
4502 x -= sx;
4503 length += sx;
4504 sx = 0;
4505 }
4506 if (sx + length > image_width)
4507 length = image_width - sx;
4508 if (length > 0) {
4509 const int coverage = (spans[c].coverage * data->texture.const_alpha) >> 8;
4510 while (length) {
4511 int l = qMin(a: BufferSize, b: length);
4512 const QRgbaFloat32 *src = op.srcFetchFP(src_buffer, &op, data, sy, sx, l);
4513 QRgbaFloat32 *dest = fetchDest ? op.destFetchFP(buffer, data->rasterBuffer, x, spans[c].y, l) : buffer;
4514 op.funcFP(dest, src, l, coverage);
4515 if (op.destStoreFP)
4516 op.destStoreFP(data->rasterBuffer, x, spans[c].y, dest, l);
4517 x += l;
4518 sx += l;
4519 length -= l;
4520 }
4521 }
4522 }
4523 }
4524 };
4525 QT_THREAD_PARALLEL_FILLS(function);
4526}
4527#endif
4528
4529static void blend_untransformed_argb(int count, const QT_FT_Span *spans, void *userData)
4530{
4531 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4532 if (data->texture.format != QImage::Format_ARGB32_Premultiplied
4533 && data->texture.format != QImage::Format_RGB32) {
4534 blend_untransformed_generic(count, spans, userData);
4535 return;
4536 }
4537
4538 const Operator op = getOperator(data, spans, spanCount: count);
4539
4540 const int image_width = data->texture.width;
4541 const int image_height = data->texture.height;
4542 const int const_alpha = data->texture.const_alpha;
4543 const int xoff = -qRound(d: -data->dx);
4544 const int yoff = -qRound(d: -data->dy);
4545
4546 auto function = [=, &op] (int cStart, int cEnd)
4547 {
4548 for (int c = cStart; c < cEnd; ++c) {
4549 if (!spans[c].len)
4550 continue;
4551 int x = spans[c].x;
4552 int length = spans[c].len;
4553 int sx = xoff + x;
4554 int sy = yoff + spans[c].y;
4555 if (sy >= 0 && sy < image_height && sx < image_width) {
4556 if (sx < 0) {
4557 x -= sx;
4558 length += sx;
4559 sx = 0;
4560 }
4561 if (sx + length > image_width)
4562 length = image_width - sx;
4563 if (length > 0) {
4564 const int coverage = (spans[c].coverage * const_alpha) >> 8;
4565 const uint *src = (const uint *)data->texture.scanLine(y: sy) + sx;
4566 uint *dest = ((uint *)data->rasterBuffer->scanLine(y: spans[c].y)) + x;
4567 op.func(dest, src, length, coverage);
4568 }
4569 }
4570 }
4571 };
4572 QT_THREAD_PARALLEL_FILLS(function);
4573}
4574
4575static inline quint16 interpolate_pixel_rgb16_255(quint16 x, quint8 a,
4576 quint16 y, quint8 b)
4577{
4578 quint16 t = ((((x & 0x07e0) * a) + ((y & 0x07e0) * b)) >> 5) & 0x07e0;
4579 t |= ((((x & 0xf81f) * a) + ((y & 0xf81f) * b)) >> 5) & 0xf81f;
4580
4581 return t;
4582}
4583
4584static inline quint32 interpolate_pixel_rgb16x2_255(quint32 x, quint8 a,
4585 quint32 y, quint8 b)
4586{
4587 uint t;
4588 t = ((((x & 0xf81f07e0) >> 5) * a) + (((y & 0xf81f07e0) >> 5) * b)) & 0xf81f07e0;
4589 t |= ((((x & 0x07e0f81f) * a) + ((y & 0x07e0f81f) * b)) >> 5) & 0x07e0f81f;
4590 return t;
4591}
4592
4593static inline void blend_sourceOver_rgb16_rgb16(quint16 *Q_DECL_RESTRICT dest,
4594 const quint16 *Q_DECL_RESTRICT src,
4595 int length,
4596 const quint8 alpha,
4597 const quint8 ialpha)
4598{
4599 const int dstAlign = ((quintptr)dest) & 0x3;
4600 if (dstAlign) {
4601 *dest = interpolate_pixel_rgb16_255(x: *src, a: alpha, y: *dest, b: ialpha);
4602 ++dest;
4603 ++src;
4604 --length;
4605 }
4606 const int srcAlign = ((quintptr)src) & 0x3;
4607 int length32 = length >> 1;
4608 if (length32 && srcAlign == 0) {
4609 while (length32--) {
4610 const quint32 *src32 = reinterpret_cast<const quint32*>(src);
4611 quint32 *dest32 = reinterpret_cast<quint32*>(dest);
4612 *dest32 = interpolate_pixel_rgb16x2_255(x: *src32, a: alpha,
4613 y: *dest32, b: ialpha);
4614 dest += 2;
4615 src += 2;
4616 }
4617 length &= 0x1;
4618 }
4619 while (length--) {
4620 *dest = interpolate_pixel_rgb16_255(x: *src, a: alpha, y: *dest, b: ialpha);
4621 ++dest;
4622 ++src;
4623 }
4624}
4625
4626static void blend_untransformed_rgb565(int count, const QT_FT_Span *spans, void *userData)
4627{
4628 QSpanData *data = reinterpret_cast<QSpanData*>(userData);
4629 QPainter::CompositionMode mode = data->rasterBuffer->compositionMode;
4630
4631 if (data->texture.format != QImage::Format_RGB16
4632 || (mode != QPainter::CompositionMode_SourceOver
4633 && mode != QPainter::CompositionMode_Source))
4634 {
4635 blend_untransformed_generic(count, spans, userData);
4636 return;
4637 }
4638
4639 const int image_width = data->texture.width;
4640 const int image_height = data->texture.height;
4641 int xoff = -qRound(d: -data->dx);
4642 int yoff = -qRound(d: -data->dy);
4643
4644 auto function = [=](int cStart, int cEnd)
4645 {
4646 for (int c = cStart; c < cEnd; ++c) {
4647 if (!spans[c].len)
4648 continue;
4649 const quint8 coverage = (data->texture.const_alpha * spans[c].coverage) >> 8;
4650 if (coverage == 0)
4651 continue;
4652
4653 int x = spans[c].x;
4654 int length = spans[c].len;
4655 int sx = xoff + x;
4656 int sy = yoff + spans[c].y;
4657 if (sy >= 0 && sy < image_height && sx < image_width) {
4658 if (sx < 0) {
4659 x -= sx;
4660 length += sx;
4661 sx = 0;
4662 }
4663 if (sx + length > image_width)
4664 length = image_width - sx;
4665 if (length > 0) {
4666 quint16 *dest = (quint16 *)data->rasterBuffer->scanLine(y: spans[c].y) + x;
4667 const quint16 *src = (const quint16 *)data->texture.scanLine(y: sy) + sx;
4668 if (coverage == 255) {
4669 memcpy(dest: dest, src: src, n: length * sizeof(quint16));
4670 } else {
4671 const quint8 alpha = (coverage + 1) >> 3;
4672 const quint8 ialpha = 0x20 - alpha;
4673 if (alpha > 0)
4674 blend_sourceOver_rgb16_rgb16(dest, src, length, alpha, ialpha);
4675 }
4676 }
4677 }
4678 }
4679 };
4680 QT_THREAD_PARALLEL_FILLS(function);
4681}
4682
4683static void blend_tiled_generic(int count, const QT_FT_Span *spans, void *userData)
4684{
4685 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4686
4687 const Operator op = getOperator(data, spans, spanCount: count);
4688
4689 const int image_width = data->texture.width;
4690 const int image_height = data->texture.height;
4691 const int const_alpha = data->texture.const_alpha;
4692 int xoff = -qRound(d: -data->dx) % image_width;
4693 int yoff = -qRound(d: -data->dy) % image_height;
4694
4695 if (xoff < 0)
4696 xoff += image_width;
4697 if (yoff < 0)
4698 yoff += image_height;
4699
4700 auto function = [=, &op](int cStart, int cEnd)
4701 {
4702 alignas(16) Q_DECL_UNINITIALIZED uint buffer[BufferSize];
4703 alignas(16) Q_DECL_UNINITIALIZED uint src_buffer[BufferSize];
4704 for (int c = cStart; c < cEnd; ++c) {
4705 int x = spans[c].x;
4706 int length = spans[c].len;
4707 int sx = (xoff + spans[c].x) % image_width;
4708 int sy = (spans[c].y + yoff) % image_height;
4709 if (sx < 0)
4710 sx += image_width;
4711 if (sy < 0)
4712 sy += image_height;
4713
4714 const int coverage = (spans[c].coverage * const_alpha) >> 8;
4715 while (length) {
4716 int l = qMin(a: image_width - sx, b: length);
4717 if (BufferSize < l)
4718 l = BufferSize;
4719 const uint *src = op.srcFetch(src_buffer, &op, data, sy, sx, l);
4720 uint *dest = op.destFetch(buffer, data->rasterBuffer, x, spans[c].y, l);
4721 op.func(dest, src, l, coverage);
4722 if (op.destStore)
4723 op.destStore(data->rasterBuffer, x, spans[c].y, dest, l);
4724 x += l;
4725 sx += l;
4726 length -= l;
4727 if (sx >= image_width)
4728 sx = 0;
4729 }
4730 }
4731 };
4732 QT_THREAD_PARALLEL_FILLS(function);
4733}
4734
4735#if QT_CONFIG(raster_64bit)
4736static void blend_tiled_generic_rgb64(int count, const QT_FT_Span *spans, void *userData)
4737{
4738 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4739
4740 const Operator op = getOperator(data, spans, spanCount: count);
4741 if (!op.func64) {
4742 qCDebug(lcQtGuiDrawHelper, "blend_tiled_generic_rgb64: unsupported 64-bit blend attempted, falling back to 32-bit");
4743 return blend_tiled_generic(count, spans, userData);
4744 }
4745
4746 const int image_width = data->texture.width;
4747 const int image_height = data->texture.height;
4748 int xoff = -qRound(d: -data->dx) % image_width;
4749 int yoff = -qRound(d: -data->dy) % image_height;
4750
4751 if (xoff < 0)
4752 xoff += image_width;
4753 if (yoff < 0)
4754 yoff += image_height;
4755
4756 bool isBpp32 = qPixelLayouts[data->rasterBuffer->format].bpp == QPixelLayout::BPP32;
4757 bool isBpp64 = qPixelLayouts[data->rasterBuffer->format].bpp == QPixelLayout::BPP64;
4758 if (op.destFetch64 == destFetch64Undefined && image_width <= BufferSize && (isBpp32 || isBpp64)) {
4759 alignas(16) Q_DECL_UNINITIALIZED QRgba64 src_buffer[BufferSize];
4760 // If destination isn't blended into the result, we can do the tiling directly on destination pixels.
4761 while (count--) {
4762 int x = spans->x;
4763 int y = spans->y;
4764 int length = spans->len;
4765 int sx = (xoff + spans->x) % image_width;
4766 int sy = (spans->y + yoff) % image_height;
4767 if (sx < 0)
4768 sx += image_width;
4769 if (sy < 0)
4770 sy += image_height;
4771
4772 int sl = qMin(a: image_width, b: length);
4773 if (sx > 0 && sl > 0) {
4774 int l = qMin(a: image_width - sx, b: sl);
4775 const QRgba64 *src = op.srcFetch64(src_buffer, &op, data, sy, sx, l);
4776 op.destStore64(data->rasterBuffer, x, y, src, l);
4777 x += l;
4778 sx += l;
4779 sl -= l;
4780 if (sx >= image_width)
4781 sx = 0;
4782 }
4783 if (sl > 0) {
4784 Q_ASSERT(sx == 0);
4785 const QRgba64 *src = op.srcFetch64(src_buffer, &op, data, sy, sx, sl);
4786 op.destStore64(data->rasterBuffer, x, y, src, sl);
4787 x += sl;
4788 sx += sl;
4789 sl -= sl;
4790 if (sx >= image_width)
4791 sx = 0;
4792 }
4793 if (isBpp32) {
4794 uint *dest = reinterpret_cast<uint *>(data->rasterBuffer->scanLine(y)) + x - image_width;
4795 for (int i = image_width; i < length; ++i)
4796 dest[i] = dest[i - image_width];
4797 } else {
4798 quint64 *dest = reinterpret_cast<quint64 *>(data->rasterBuffer->scanLine(y)) + x - image_width;
4799 for (int i = image_width; i < length; ++i)
4800 dest[i] = dest[i - image_width];
4801 }
4802 ++spans;
4803 }
4804 return;
4805 }
4806
4807 auto function = [=, &op](int cStart, int cEnd)
4808 {
4809 alignas(16) Q_DECL_UNINITIALIZED QRgba64 buffer[BufferSize];
4810 alignas(16) Q_DECL_UNINITIALIZED QRgba64 src_buffer[BufferSize];
4811 for (int c = cStart; c < cEnd; ++c) {
4812 int x = spans[c].x;
4813 int length = spans[c].len;
4814 int sx = (xoff + spans[c].x) % image_width;
4815 int sy = (spans[c].y + yoff) % image_height;
4816 if (sx < 0)
4817 sx += image_width;
4818 if (sy < 0)
4819 sy += image_height;
4820
4821 const int coverage = (spans[c].coverage * data->texture.const_alpha) >> 8;
4822 while (length) {
4823 int l = qMin(a: image_width - sx, b: length);
4824 if (BufferSize < l)
4825 l = BufferSize;
4826 const QRgba64 *src = op.srcFetch64(src_buffer, &op, data, sy, sx, l);
4827 QRgba64 *dest = op.destFetch64(buffer, data->rasterBuffer, x, spans[c].y, l);
4828 op.func64(dest, src, l, coverage);
4829 if (op.destStore64)
4830 op.destStore64(data->rasterBuffer, x, spans[c].y, dest, l);
4831 x += l;
4832 sx += l;
4833 length -= l;
4834 if (sx >= image_width)
4835 sx = 0;
4836 }
4837 }
4838 };
4839 QT_THREAD_PARALLEL_FILLS(function);
4840}
4841#endif
4842
4843#if QT_CONFIG(raster_fp)
4844static void blend_tiled_generic_fp(int count, const QT_FT_Span *spans, void *userData)
4845{
4846 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4847
4848 const Operator op = getOperator(data, spans, spanCount: count);
4849 if (!op.funcFP) {
4850 qCDebug(lcQtGuiDrawHelper, "blend_tiled_generic_fp: unsupported 4xFP blend attempted, falling back to 32-bit");
4851 return blend_tiled_generic(count, spans, userData);
4852 }
4853
4854 const int image_width = data->texture.width;
4855 const int image_height = data->texture.height;
4856 int xoff = -qRound(d: -data->dx) % image_width;
4857 int yoff = -qRound(d: -data->dy) % image_height;
4858
4859 if (xoff < 0)
4860 xoff += image_width;
4861 if (yoff < 0)
4862 yoff += image_height;
4863
4864 // Consider tiling optimizing like the other versions.
4865
4866 auto function = [=, &op](int cStart, int cEnd)
4867 {
4868 alignas(16) Q_DECL_UNINITIALIZED QRgbaFloat32 buffer[BufferSize];
4869 alignas(16) Q_DECL_UNINITIALIZED QRgbaFloat32 src_buffer[BufferSize];
4870 for (int c = cStart; c < cEnd; ++c) {
4871 int x = spans[c].x;
4872 int length = spans[c].len;
4873 int sx = (xoff + spans[c].x) % image_width;
4874 int sy = (spans[c].y + yoff) % image_height;
4875 if (sx < 0)
4876 sx += image_width;
4877 if (sy < 0)
4878 sy += image_height;
4879
4880 const int coverage = (spans[c].coverage * data->texture.const_alpha) >> 8;
4881 while (length) {
4882 int l = qMin(a: image_width - sx, b: length);
4883 if (BufferSize < l)
4884 l = BufferSize;
4885 const QRgbaFloat32 *src = op.srcFetchFP(src_buffer, &op, data, sy, sx, l);
4886 QRgbaFloat32 *dest = op.destFetchFP(buffer, data->rasterBuffer, x, spans[c].y, l);
4887 op.funcFP(dest, src, l, coverage);
4888 if (op.destStoreFP)
4889 op.destStoreFP(data->rasterBuffer, x, spans[c].y, dest, l);
4890 x += l;
4891 sx += l;
4892 length -= l;
4893 if (sx >= image_width)
4894 sx = 0;
4895 }
4896 }
4897 };
4898 QT_THREAD_PARALLEL_FILLS(function);
4899}
4900#endif
4901
4902static void blend_tiled_argb(int count, const QT_FT_Span *spans, void *userData)
4903{
4904 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4905 if (data->texture.format != QImage::Format_ARGB32_Premultiplied
4906 && data->texture.format != QImage::Format_RGB32) {
4907 blend_tiled_generic(count, spans, userData);
4908 return;
4909 }
4910
4911 const Operator op = getOperator(data, spans, spanCount: count);
4912
4913 const int image_width = data->texture.width;
4914 const int image_height = data->texture.height;
4915 int xoff = -qRound(d: -data->dx) % image_width;
4916 int yoff = -qRound(d: -data->dy) % image_height;
4917
4918 if (xoff < 0)
4919 xoff += image_width;
4920 if (yoff < 0)
4921 yoff += image_height;
4922 const auto func = op.func;
4923 const int const_alpha = data->texture.const_alpha;
4924
4925 auto function = [=] (int cStart, int cEnd) {
4926 for (int c = cStart; c < cEnd; ++c) {
4927 int x = spans[c].x;
4928 int length = spans[c].len;
4929 int sx = (xoff + spans[c].x) % image_width;
4930 int sy = (spans[c].y + yoff) % image_height;
4931 if (sx < 0)
4932 sx += image_width;
4933 if (sy < 0)
4934 sy += image_height;
4935
4936 const int coverage = (spans[c].coverage * const_alpha) >> 8;
4937 while (length) {
4938 int l = qMin(a: image_width - sx, b: length);
4939 if (BufferSize < l)
4940 l = BufferSize;
4941 const uint *src = (const uint *)data->texture.scanLine(y: sy) + sx;
4942 uint *dest = ((uint *)data->rasterBuffer->scanLine(y: spans[c].y)) + x;
4943 func(dest, src, l, coverage);
4944 x += l;
4945 sx += l;
4946 length -= l;
4947 if (sx >= image_width)
4948 sx = 0;
4949 }
4950 }
4951 };
4952 QT_THREAD_PARALLEL_FILLS(function);
4953}
4954
4955static void blend_tiled_rgb565(int count, const QT_FT_Span *spans, void *userData)
4956{
4957 QSpanData *data = reinterpret_cast<QSpanData*>(userData);
4958 QPainter::CompositionMode mode = data->rasterBuffer->compositionMode;
4959
4960 if (data->texture.format != QImage::Format_RGB16
4961 || (mode != QPainter::CompositionMode_SourceOver
4962 && mode != QPainter::CompositionMode_Source))
4963 {
4964 blend_tiled_generic(count, spans, userData);
4965 return;
4966 }
4967
4968 const int image_width = data->texture.width;
4969 const int image_height = data->texture.height;
4970 int xoff = -qRound(d: -data->dx) % image_width;
4971 int yoff = -qRound(d: -data->dy) % image_height;
4972
4973 if (xoff < 0)
4974 xoff += image_width;
4975 if (yoff < 0)
4976 yoff += image_height;
4977
4978 const int const_alpha = data->texture.const_alpha;
4979 auto function = [=] (int cStart, int cEnd) {
4980 for (int c = cStart; c < cEnd; ++c) {
4981 const quint8 coverage = (const_alpha * spans[c].coverage) >> 8;
4982 if (coverage == 0)
4983 continue;
4984
4985 int x = spans[c].x;
4986 int length = spans[c].len;
4987 int sx = (xoff + spans[c].x) % image_width;
4988 int sy = (spans[c].y + yoff) % image_height;
4989 if (sx < 0)
4990 sx += image_width;
4991 if (sy < 0)
4992 sy += image_height;
4993
4994 if (coverage == 255) {
4995 // Copy the first texture block
4996 length = qMin(a: image_width,b: length);
4997 int tx = x;
4998 while (length) {
4999 int l = qMin(a: image_width - sx, b: length);
5000 if (BufferSize < l)
5001 l = BufferSize;
5002 quint16 *dest = ((quint16 *)data->rasterBuffer->scanLine(y: spans[c].y)) + tx;
5003 const quint16 *src = (const quint16 *)data->texture.scanLine(y: sy) + sx;
5004 memcpy(dest: dest, src: src, n: l * sizeof(quint16));
5005 length -= l;
5006 tx += l;
5007 sx += l;
5008 if (sx >= image_width)
5009 sx = 0;
5010 }
5011
5012 // Now use the rasterBuffer as the source of the texture,
5013 // We can now progressively copy larger blocks
5014 // - Less cpu time in code figuring out what to copy
5015 // We are dealing with one block of data
5016 // - More likely to fit in the cache
5017 // - can use memcpy
5018 int copy_image_width = qMin(a: image_width, b: int(spans[c].len));
5019 length = spans[c].len - copy_image_width;
5020 quint16 *src = ((quint16 *)data->rasterBuffer->scanLine(y: spans[c].y)) + x;
5021 quint16 *dest = src + copy_image_width;
5022 while (copy_image_width < length) {
5023 memcpy(dest: dest, src: src, n: copy_image_width * sizeof(quint16));
5024 dest += copy_image_width;
5025 length -= copy_image_width;
5026 copy_image_width *= 2;
5027 }
5028 if (length > 0)
5029 memcpy(dest: dest, src: src, n: length * sizeof(quint16));
5030 } else {
5031 const quint8 alpha = (coverage + 1) >> 3;
5032 const quint8 ialpha = 0x20 - alpha;
5033 if (alpha > 0) {
5034 while (length) {
5035 int l = qMin(a: image_width - sx, b: length);
5036 if (BufferSize < l)
5037 l = BufferSize;
5038 quint16 *dest = ((quint16 *)data->rasterBuffer->scanLine(y: spans[c].y)) + x;
5039 const quint16 *src = (const quint16 *)data->texture.scanLine(y: sy) + sx;
5040 blend_sourceOver_rgb16_rgb16(dest, src, length: l, alpha, ialpha);
5041 x += l;
5042 sx += l;
5043 length -= l;
5044 if (sx >= image_width)
5045 sx = 0;
5046 }
5047 }
5048 }
5049 }
5050 };
5051 QT_THREAD_PARALLEL_FILLS(function);
5052}
5053
5054/* Image formats here are target formats */
5055static const ProcessSpans processTextureSpansARGB32PM[NBlendTypes] = {
5056 blend_untransformed_argb, // Untransformed
5057 blend_tiled_argb, // Tiled
5058 blend_src_generic, // Transformed
5059 blend_src_generic, // TransformedTiled
5060 blend_src_generic, // TransformedBilinear
5061 blend_src_generic // TransformedBilinearTiled
5062};
5063
5064static const ProcessSpans processTextureSpansRGB16[NBlendTypes] = {
5065 blend_untransformed_rgb565, // Untransformed
5066 blend_tiled_rgb565, // Tiled
5067 blend_src_generic, // Transformed
5068 blend_src_generic, // TransformedTiled
5069 blend_src_generic, // TransformedBilinear
5070 blend_src_generic // TransformedBilinearTiled
5071};
5072
5073static const ProcessSpans processTextureSpansGeneric[NBlendTypes] = {
5074 blend_untransformed_generic, // Untransformed
5075 blend_tiled_generic, // Tiled
5076 blend_src_generic, // Transformed
5077 blend_src_generic, // TransformedTiled
5078 blend_src_generic, // TransformedBilinear
5079 blend_src_generic // TransformedBilinearTiled
5080};
5081
5082#if QT_CONFIG(raster_64bit)
5083static const ProcessSpans processTextureSpansGeneric64[NBlendTypes] = {
5084 blend_untransformed_generic_rgb64, // Untransformed
5085 blend_tiled_generic_rgb64, // Tiled
5086 blend_src_generic_rgb64, // Transformed
5087 blend_src_generic_rgb64, // TransformedTiled
5088 blend_src_generic_rgb64, // TransformedBilinear
5089 blend_src_generic_rgb64 // TransformedBilinearTiled
5090};
5091#endif
5092
5093#if QT_CONFIG(raster_fp)
5094static const ProcessSpans processTextureSpansGenericFP[NBlendTypes] = {
5095 blend_untransformed_generic_fp, // Untransformed
5096 blend_tiled_generic_fp, // Tiled
5097 blend_src_generic_fp, // Transformed
5098 blend_src_generic_fp, // TransformedTiled
5099 blend_src_generic_fp, // TransformedBilinear
5100 blend_src_generic_fp // TransformedBilinearTiled
5101};
5102#endif
5103void qBlendTexture(int count, const QT_FT_Span *spans, void *userData)
5104{
5105 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
5106 TextureBlendType blendType = getBlendType(data);
5107 ProcessSpans proc;
5108 switch (data->rasterBuffer->format) {
5109 case QImage::Format_Invalid:
5110 Q_UNREACHABLE_RETURN();
5111 case QImage::Format_ARGB32_Premultiplied:
5112 proc = processTextureSpansARGB32PM[blendType];
5113 break;
5114 case QImage::Format_RGB16:
5115 proc = processTextureSpansRGB16[blendType];
5116 break;
5117#if defined(__SSE2__) || defined(__ARM_NEON__) || defined(QT_COMPILER_SUPPORTS_LSX) || (Q_PROCESSOR_WORDSIZE == 8)
5118 case QImage::Format_ARGB32:
5119 case QImage::Format_RGBA8888:
5120#endif
5121 case QImage::Format_BGR30:
5122 case QImage::Format_A2BGR30_Premultiplied:
5123 case QImage::Format_RGB30:
5124 case QImage::Format_A2RGB30_Premultiplied:
5125 case QImage::Format_RGBX64:
5126 case QImage::Format_RGBA64:
5127 case QImage::Format_RGBA64_Premultiplied:
5128 case QImage::Format_Grayscale16:
5129#if !QT_CONFIG(raster_fp)
5130 case QImage::Format_RGBX16FPx4:
5131 case QImage::Format_RGBA16FPx4:
5132 case QImage::Format_RGBA16FPx4_Premultiplied:
5133 case QImage::Format_RGBX32FPx4:
5134 case QImage::Format_RGBA32FPx4:
5135 case QImage::Format_RGBA32FPx4_Premultiplied:
5136#endif
5137#if QT_CONFIG(raster_64bit)
5138 proc = processTextureSpansGeneric64[blendType];
5139 break;
5140#endif // QT_CONFIG(raster_64bit)
5141#if QT_CONFIG(raster_fp)
5142 case QImage::Format_RGBX16FPx4:
5143 case QImage::Format_RGBA16FPx4:
5144 case QImage::Format_RGBA16FPx4_Premultiplied:
5145 case QImage::Format_RGBX32FPx4:
5146 case QImage::Format_RGBA32FPx4:
5147 case QImage::Format_RGBA32FPx4_Premultiplied:
5148 proc = processTextureSpansGenericFP[blendType];
5149 break;
5150#endif
5151 default:
5152 proc = processTextureSpansGeneric[blendType];
5153 break;
5154 }
5155 proc(count, spans, userData);
5156}
5157
5158static inline bool calculate_fixed_gradient_factors(int count, const QT_FT_Span *spans,
5159 const QSpanData *data,
5160 const LinearGradientValues &linear,
5161 int *pyinc, int *poff)
5162{
5163 /*
5164 The logic for vertical gradient calculations is a mathematically
5165 reduced copy of that in fetchLinearGradient() - which is basically:
5166
5167 qreal ry = data->m22 * (y + 0.5) + data->dy;
5168 qreal t = linear.dy*ry + linear.off;
5169 t *= (GRADIENT_STOPTABLE_SIZE - 1);
5170 quint32 color =
5171 qt_gradient_pixel_fixed(&data->gradient,
5172 int(t * FIXPT_SIZE));
5173
5174 This has then been converted to fixed point to improve performance.
5175 */
5176 const int gss = GRADIENT_STOPTABLE_SIZE - 1;
5177 qreal ryinc = linear.dy * data->m22 * gss * FIXPT_SIZE;
5178 qreal roff = (linear.dy * (data->m22 * qreal(0.5) + data->dy) + linear.off) * gss * FIXPT_SIZE;
5179 const int limit = std::numeric_limits<int>::max() - FIXPT_SIZE;
5180 if (count && (std::fabs(x: ryinc) < limit) && (std::fabs(x: roff) < limit)
5181 && (std::fabs(x: ryinc * spans->y + roff) < limit)
5182 && (std::fabs(x: ryinc * (spans + count - 1)->y + roff) < limit)) {
5183 *pyinc = int(ryinc);
5184 *poff = int(roff);
5185 return true;
5186 }
5187 return false;
5188}
5189
5190static bool blend_vertical_gradient_argb(int count, const QT_FT_Span *spans, void *userData)
5191{
5192 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
5193
5194 LinearGradientValues linear;
5195 getLinearGradientValues(v: &linear, data);
5196
5197 CompositionFunctionSolid funcSolid =
5198 functionForModeSolid[data->rasterBuffer->compositionMode];
5199
5200 int yinc(0), off(0);
5201 if (!calculate_fixed_gradient_factors(count, spans, data, linear, pyinc: &yinc, poff: &off))
5202 return false;
5203
5204 while (count--) {
5205 int y = spans->y;
5206 int x = spans->x;
5207
5208 quint32 *dst = (quint32 *)(data->rasterBuffer->scanLine(y)) + x;
5209 quint32 color =
5210 qt_gradient_pixel_fixed(data: &data->gradient, fixed_pos: yinc * y + off);
5211
5212 funcSolid(dst, spans->len, color, spans->coverage);
5213 ++spans;
5214 }
5215 return true;
5216}
5217
5218template<ProcessSpans blend_color>
5219static bool blend_vertical_gradient(int count, const QT_FT_Span *spans, void *userData)
5220{
5221 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
5222
5223 LinearGradientValues linear;
5224 getLinearGradientValues(v: &linear, data);
5225
5226 int yinc(0), off(0);
5227 if (!calculate_fixed_gradient_factors(count, spans, data, linear, pyinc: &yinc, poff: &off))
5228 return false;
5229
5230 while (count--) {
5231 int y = spans->y;
5232
5233#if QT_CONFIG(raster_64bit)
5234 data->solidColor = qt_gradient_pixel64_fixed(data: &data->gradient, fixed_pos: yinc * y + off);
5235#else
5236 data->solidColor = qt_gradient_pixel_fixed(&data->gradient, yinc * y + off);
5237#endif
5238 blend_color(1, spans, userData);
5239 ++spans;
5240 }
5241 return true;
5242}
5243
5244void qBlendGradient(int count, const QT_FT_Span *spans, void *userData)
5245{
5246 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
5247 bool isVerticalGradient =
5248 data->txop <= QTransform::TxScale &&
5249 data->type == QSpanData::LinearGradient &&
5250 data->gradient.linear.end.x == data->gradient.linear.origin.x;
5251 switch (data->rasterBuffer->format) {
5252 case QImage::Format_Invalid:
5253 break;
5254 case QImage::Format_RGB32:
5255 case QImage::Format_ARGB32_Premultiplied:
5256 if (isVerticalGradient && blend_vertical_gradient_argb(count, spans, userData))
5257 return;
5258 return blend_src_generic(count, spans, userData);
5259#if defined(__SSE2__) || defined(__ARM_NEON__) || defined(QT_COMPILER_SUPPORTS_LSX) || (Q_PROCESSOR_WORDSIZE == 8)
5260 case QImage::Format_ARGB32:
5261 case QImage::Format_RGBA8888:
5262#endif
5263 case QImage::Format_BGR30:
5264 case QImage::Format_A2BGR30_Premultiplied:
5265 case QImage::Format_RGB30:
5266 case QImage::Format_A2RGB30_Premultiplied:
5267 case QImage::Format_RGBX64:
5268 case QImage::Format_RGBA64:
5269 case QImage::Format_RGBA64_Premultiplied:
5270#if !QT_CONFIG(raster_fp)
5271 case QImage::Format_RGBX16FPx4:
5272 case QImage::Format_RGBA16FPx4:
5273 case QImage::Format_RGBA16FPx4_Premultiplied:
5274 case QImage::Format_RGBX32FPx4:
5275 case QImage::Format_RGBA32FPx4:
5276 case QImage::Format_RGBA32FPx4_Premultiplied:
5277#endif
5278#if QT_CONFIG(raster_64bit)
5279 if (isVerticalGradient && blend_vertical_gradient<blend_color_generic_rgb64>(count, spans, userData))
5280 return;
5281 return blend_src_generic_rgb64(count, spans, userData);
5282#endif // QT_CONFIG(raster_64bit)
5283#if QT_CONFIG(raster_fp)
5284 case QImage::Format_RGBX16FPx4:
5285 case QImage::Format_RGBA16FPx4:
5286 case QImage::Format_RGBA16FPx4_Premultiplied:
5287 case QImage::Format_RGBX32FPx4:
5288 case QImage::Format_RGBA32FPx4:
5289 case QImage::Format_RGBA32FPx4_Premultiplied:
5290 if (isVerticalGradient && blend_vertical_gradient<blend_color_generic_fp>(count, spans, userData))
5291 return;
5292 return blend_src_generic_fp(count, spans, userData);
5293#endif
5294 default:
5295 if (isVerticalGradient && blend_vertical_gradient<blend_color_generic>(count, spans, userData))
5296 return;
5297 return blend_src_generic(count, spans, userData);
5298 }
5299 Q_UNREACHABLE();
5300}
5301
5302template <class DST> static
5303inline void qt_bitmapblit_template(QRasterBuffer *rasterBuffer,
5304 int x, int y, DST color,
5305 const uchar *map,
5306 int mapWidth, int mapHeight, int mapStride)
5307{
5308 DST *dest = reinterpret_cast<DST *>(rasterBuffer->scanLine(y)) + x;
5309 const int destStride = rasterBuffer->stride<DST>();
5310
5311 if (mapWidth > 8) {
5312 while (--mapHeight >= 0) {
5313 int x0 = 0;
5314 int n = 0;
5315 for (int x = 0; x < mapWidth; x += 8) {
5316 uchar s = map[x >> 3];
5317 for (int i = 0; i < 8; ++i) {
5318 if (s & 0x80) {
5319 ++n;
5320 } else {
5321 if (n) {
5322 qt_memfill(dest + x0, color, n);
5323 x0 += n + 1;
5324 n = 0;
5325 } else {
5326 ++x0;
5327 }
5328 if (!s) {
5329 x0 += 8 - 1 - i;
5330 break;
5331 }
5332 }
5333 s <<= 1;
5334 }
5335 }
5336 if (n)
5337 qt_memfill(dest + x0, color, n);
5338 dest += destStride;
5339 map += mapStride;
5340 }
5341 } else {
5342 while (--mapHeight >= 0) {
5343 int x0 = 0;
5344 int n = 0;
5345 for (uchar s = *map; s; s <<= 1) {
5346 if (s & 0x80) {
5347 ++n;
5348 } else if (n) {
5349 qt_memfill(dest + x0, color, n);
5350 x0 += n + 1;
5351 n = 0;
5352 } else {
5353 ++x0;
5354 }
5355 }
5356 if (n)
5357 qt_memfill(dest + x0, color, n);
5358 dest += destStride;
5359 map += mapStride;
5360 }
5361 }
5362}
5363
5364inline static void qt_bitmapblit_argb32(QRasterBuffer *rasterBuffer,
5365 int x, int y, const QRgba64 &color,
5366 const uchar *map,
5367 int mapWidth, int mapHeight, int mapStride)
5368{
5369 qt_bitmapblit_template<quint32>(rasterBuffer, x, y, color: color.toArgb32(),
5370 map, mapWidth, mapHeight, mapStride);
5371}
5372
5373inline static void qt_bitmapblit_rgba8888(QRasterBuffer *rasterBuffer,
5374 int x, int y, const QRgba64 &color,
5375 const uchar *map,
5376 int mapWidth, int mapHeight, int mapStride)
5377{
5378 qt_bitmapblit_template<quint32>(rasterBuffer, x, y, color: ARGB2RGBA(x: color.toArgb32()),
5379 map, mapWidth, mapHeight, mapStride);
5380}
5381
5382template<QtPixelOrder PixelOrder>
5383inline static void qt_bitmapblit_rgb30(QRasterBuffer *rasterBuffer,
5384 int x, int y, const QRgba64 &color,
5385 const uchar *map,
5386 int mapWidth, int mapHeight, int mapStride)
5387{
5388 qt_bitmapblit_template<quint32>(rasterBuffer, x, y, qConvertRgb64ToRgb30<PixelOrder>(color),
5389 map, mapWidth, mapHeight, mapStride);
5390}
5391
5392inline static void qt_bitmapblit_quint16(QRasterBuffer *rasterBuffer,
5393 int x, int y, const QRgba64 &color,
5394 const uchar *map,
5395 int mapWidth, int mapHeight, int mapStride)
5396{
5397 qt_bitmapblit_template<quint16>(rasterBuffer, x, y, color: color.toRgb16(),
5398 map, mapWidth, mapHeight, mapStride);
5399}
5400
5401static inline void grayBlendPixel(quint32 *dst, int coverage, QRgba64 srcLinear, const QColorTrcLut *colorProfile)
5402{
5403 // Do a gammacorrected gray alphablend...
5404 const QRgba64 dstLinear = colorProfile ? colorProfile->toLinear64(rgb32: *dst) : QRgba64::fromArgb32(rgb: *dst);
5405
5406 QRgba64 blend = interpolate255(x: srcLinear, alpha1: coverage, y: dstLinear, alpha2: 255 - coverage);
5407
5408 *dst = colorProfile ? colorProfile->fromLinear64(rgb64: blend) : toArgb32(rgba64: blend);
5409}
5410
5411static inline void alphamapblend_argb32(quint32 *dst, int coverage, QRgba64 srcLinear, quint32 src, const QColorTrcLut *colorProfile)
5412{
5413 if (coverage == 0) {
5414 // nothing
5415 } else if (coverage == 255 || !colorProfile) {
5416 blend_pixel(dst&: *dst, src, const_alpha: coverage);
5417 } else if (*dst < 0xff000000) {
5418 // Give up and do a naive gray alphablend. Needed to deal with ARGB32 and invalid ARGB32_premultiplied, see QTBUG-60571
5419 blend_pixel(dst&: *dst, src, const_alpha: coverage);
5420 } else if (src >= 0xff000000) {
5421 grayBlendPixel(dst, coverage, srcLinear, colorProfile);
5422 } else {
5423 // First do naive blend with text-color
5424 QRgb s = *dst;
5425 blend_pixel(dst&: s, src);
5426 // Then gamma-corrected blend with glyph shape
5427 QRgba64 s64 = colorProfile ? colorProfile->toLinear64(rgb32: s) : QRgba64::fromArgb32(rgb: s);
5428 grayBlendPixel(dst, coverage, srcLinear: s64, colorProfile);
5429 }
5430}
5431
5432#if QT_CONFIG(raster_64bit)
5433
5434static inline void grayBlendPixel(QRgba64 &dst, int coverage, QRgba64 srcLinear, const QColorTrcLut *colorProfile)
5435{
5436 // Do a gammacorrected gray alphablend...
5437 QRgba64 dstColor = dst;
5438 if (colorProfile) {
5439 if (dstColor.isOpaque())
5440 dstColor = colorProfile->toLinear(rgb64: dstColor);
5441 else if (!dstColor.isTransparent())
5442 dstColor = colorProfile->toLinear(rgb64: dstColor.unpremultiplied()).premultiplied();
5443 }
5444
5445 blend_pixel(dst&: dstColor, src: srcLinear, const_alpha: coverage);
5446
5447 if (colorProfile) {
5448 if (dstColor.isOpaque())
5449 dstColor = colorProfile->fromLinear(rgb64: dstColor);
5450 else if (!dstColor.isTransparent())
5451 dstColor = colorProfile->fromLinear(rgb64: dstColor.unpremultiplied()).premultiplied();
5452 }
5453 dst = dstColor;
5454}
5455
5456static inline void alphamapblend_generic(int coverage, QRgba64 *dest, int x, const QRgba64 &srcLinear, const QRgba64 &src, const QColorTrcLut *colorProfile)
5457{
5458 if (coverage == 0) {
5459 // nothing
5460 } else if (coverage == 255) {
5461 blend_pixel(dst&: dest[x], src);
5462 } else if (src.isOpaque()) {
5463 grayBlendPixel(dst&: dest[x], coverage, srcLinear, colorProfile);
5464 } else {
5465 // First do naive blend with text-color
5466 QRgba64 s = dest[x];
5467 blend_pixel(dst&: s, src);
5468 // Then gamma-corrected blend with glyph shape
5469 if (colorProfile)
5470 s = colorProfile->toLinear(rgb64: s);
5471 grayBlendPixel(dst&: dest[x], coverage, srcLinear: s, colorProfile);
5472 }
5473}
5474
5475static void qt_alphamapblit_generic_oneline(const uchar *map, int len,
5476 const QRgba64 srcColor, QRgba64 *dest,
5477 const QRgba64 color,
5478 const QColorTrcLut *colorProfile)
5479{
5480 for (int j = 0; j < len; ++j)
5481 alphamapblend_generic(coverage: map[j], dest, x: j, srcLinear: srcColor, src: color, colorProfile);
5482}
5483
5484static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer,
5485 int x, int y, const QRgba64 &color,
5486 const uchar *map,
5487 int mapWidth, int mapHeight, int mapStride,
5488 const QClipData *clip, bool useGammaCorrection)
5489{
5490 if (color.isTransparent())
5491 return;
5492
5493 const QColorTrcLut *colorProfile = nullptr;
5494
5495 if (useGammaCorrection)
5496 colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA8Text();
5497
5498 QRgba64 srcColor = color;
5499 if (colorProfile && color.isOpaque())
5500 srcColor = colorProfile->toLinear(rgb64: srcColor);
5501
5502 alignas(8) Q_DECL_UNINITIALIZED QRgba64 buffer[BufferSize];
5503 const DestFetchProc64 destFetch64 = destFetchProc64[rasterBuffer->format];
5504 const DestStoreProc64 destStore64 = destStoreProc64[rasterBuffer->format];
5505
5506 if (!clip) {
5507 for (int ly = 0; ly < mapHeight; ++ly) {
5508 int i = x;
5509 int length = mapWidth;
5510 while (length > 0) {
5511 int l = qMin(a: BufferSize, b: length);
5512
5513 QRgba64 *dest = destFetch64(buffer, rasterBuffer, i, y + ly, l);
5514 qt_alphamapblit_generic_oneline(map: map + i - x, len: l,
5515 srcColor, dest, color,
5516 colorProfile);
5517 if (destStore64)
5518 destStore64(rasterBuffer, i, y + ly, dest, l);
5519 length -= l;
5520 i += l;
5521 }
5522 map += mapStride;
5523 }
5524 } else {
5525 int bottom = qMin(a: y + mapHeight, b: rasterBuffer->height());
5526
5527 int top = qMax(a: y, b: 0);
5528 map += (top - y) * mapStride;
5529
5530 const_cast<QClipData *>(clip)->initialize();
5531 for (int yp = top; yp<bottom; ++yp) {
5532 const QClipData::ClipLine &line = clip->m_clipLines[yp];
5533
5534 for (int i=0; i<line.count; ++i) {
5535 const QT_FT_Span &clip = line.spans[i];
5536
5537 int start = qMax<int>(a: x, b: clip.x);
5538 int end = qMin<int>(a: x + mapWidth, b: clip.x + clip.len);
5539 if (end <= start)
5540 continue;
5541 Q_ASSERT(end - start <= BufferSize);
5542 QRgba64 *dest = destFetch64(buffer, rasterBuffer, start, clip.y, end - start);
5543 qt_alphamapblit_generic_oneline(map: map + start - x, len: end - start,
5544 srcColor, dest, color,
5545 colorProfile);
5546 if (destStore64)
5547 destStore64(rasterBuffer, start, clip.y, dest, end - start);
5548 } // for (i -> line.count)
5549 map += mapStride;
5550 } // for (yp -> bottom)
5551 }
5552}
5553#else
5554static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer,
5555 int x, int y, const QRgba64 &color,
5556 const uchar *map,
5557 int mapWidth, int mapHeight, int mapStride,
5558 const QClipData *clip, bool useGammaCorrection)
5559{
5560 if (color.isTransparent())
5561 return;
5562
5563 const quint32 c = color.toArgb32();
5564
5565 const QColorTrcLut *colorProfile = nullptr;
5566
5567 if (useGammaCorrection)
5568 colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA8Text();
5569
5570 QRgba64 srcColor = color;
5571 if (colorProfile && color.isOpaque())
5572 srcColor = colorProfile->toLinear(srcColor);
5573
5574 quint32 buffer[BufferSize];
5575 const DestFetchProc destFetch = destFetchProc[rasterBuffer->format];
5576 const DestStoreProc destStore = destStoreProc[rasterBuffer->format];
5577
5578 if (!clip) {
5579 for (int ly = 0; ly < mapHeight; ++ly) {
5580 int i = x;
5581 int length = mapWidth;
5582 while (length > 0) {
5583 int l = qMin(BufferSize, length);
5584 quint32 *dest = destFetch(buffer, rasterBuffer, i, y + ly, l);
5585 for (int j=0; j < l; ++j) {
5586 const int coverage = map[j + (i - x)];
5587 alphamapblend_argb32(dest + j, coverage, srcColor, c, colorProfile);
5588 }
5589 if (destStore)
5590 destStore(rasterBuffer, i, y + ly, dest, l);
5591 length -= l;
5592 i += l;
5593 }
5594 map += mapStride;
5595 }
5596 } else {
5597 int bottom = qMin(y + mapHeight, rasterBuffer->height());
5598
5599 int top = qMax(y, 0);
5600 map += (top - y) * mapStride;
5601
5602 const_cast<QClipData *>(clip)->initialize();
5603 for (int yp = top; yp<bottom; ++yp) {
5604 const QClipData::ClipLine &line = clip->m_clipLines[yp];
5605
5606 for (int i=0; i<line.count; ++i) {
5607 const QT_FT_Span &clip = line.spans[i];
5608
5609 int start = qMax<int>(x, clip.x);
5610 int end = qMin<int>(x + mapWidth, clip.x + clip.len);
5611 if (end <= start)
5612 continue;
5613 Q_ASSERT(end - start <= BufferSize);
5614 quint32 *dest = destFetch(buffer, rasterBuffer, start, clip.y, end - start);
5615
5616 for (int xp=start; xp<end; ++xp) {
5617 const int coverage = map[xp - x];
5618 alphamapblend_argb32(dest + xp - x, coverage, srcColor, color, colorProfile);
5619 }
5620 if (destStore)
5621 destStore(rasterBuffer, start, clip.y, dest, end - start);
5622 } // for (i -> line.count)
5623 map += mapStride;
5624 } // for (yp -> bottom)
5625 }
5626}
5627#endif
5628
5629static inline void alphamapblend_quint16(int coverage, quint16 *dest, int x, const quint16 srcColor)
5630{
5631 if (coverage == 0) {
5632 // nothing
5633 } else if (coverage == 255) {
5634 dest[x] = srcColor;
5635 } else {
5636 dest[x] = BYTE_MUL_RGB16(x: srcColor, a: coverage)
5637 + BYTE_MUL_RGB16(x: dest[x], a: 255 - coverage);
5638 }
5639}
5640
5641void qt_alphamapblit_quint16(QRasterBuffer *rasterBuffer,
5642 int x, int y, const QRgba64 &color,
5643 const uchar *map,
5644 int mapWidth, int mapHeight, int mapStride,
5645 const QClipData *clip, bool useGammaCorrection)
5646{
5647 if (useGammaCorrection || !color.isOpaque()) {
5648 qt_alphamapblit_generic(rasterBuffer, x, y, color, map, mapWidth, mapHeight, mapStride, clip, useGammaCorrection);
5649 return;
5650 }
5651
5652 const quint16 c = color.toRgb16();
5653
5654 if (!clip) {
5655 quint16 *dest = reinterpret_cast<quint16*>(rasterBuffer->scanLine(y)) + x;
5656 const int destStride = rasterBuffer->stride<quint16>();
5657 while (--mapHeight >= 0) {
5658 for (int i = 0; i < mapWidth; ++i)
5659 alphamapblend_quint16(coverage: map[i], dest, x: i, srcColor: c);
5660 dest += destStride;
5661 map += mapStride;
5662 }
5663 } else {
5664 int top = qMax(a: y, b: 0);
5665 int bottom = qMin(a: y + mapHeight, b: rasterBuffer->height());
5666 map += (top - y) * mapStride;
5667
5668 const_cast<QClipData *>(clip)->initialize();
5669 for (int yp = top; yp<bottom; ++yp) {
5670 const QClipData::ClipLine &line = clip->m_clipLines[yp];
5671
5672 quint16 *dest = reinterpret_cast<quint16*>(rasterBuffer->scanLine(y: yp));
5673
5674 for (int i=0; i<line.count; ++i) {
5675 const QT_FT_Span &clip = line.spans[i];
5676
5677 int start = qMax<int>(a: x, b: clip.x);
5678 int end = qMin<int>(a: x + mapWidth, b: clip.x + clip.len);
5679
5680 for (int xp=start; xp<end; ++xp)
5681 alphamapblend_quint16(coverage: map[xp - x], dest, x: xp, srcColor: c);
5682 } // for (i -> line.count)
5683 map += mapStride;
5684 } // for (yp -> bottom)
5685 }
5686}
5687
5688static void qt_alphamapblit_argb32_oneline(const uchar *map,
5689 int mapWidth, const QRgba64 &srcColor,
5690 quint32 *dest, const quint32 c,
5691 const QColorTrcLut *colorProfile)
5692{
5693 for (int i = 0; i < mapWidth; ++i)
5694 alphamapblend_argb32(dst: dest + i, coverage: map[i], srcLinear: srcColor, src: c, colorProfile);
5695}
5696
5697static void qt_alphamapblit_argb32(QRasterBuffer *rasterBuffer,
5698 int x, int y, const QRgba64 &color,
5699 const uchar *map,
5700 int mapWidth, int mapHeight, int mapStride,
5701 const QClipData *clip, bool useGammaCorrection)
5702{
5703 const quint32 c = color.toArgb32();
5704 const int destStride = rasterBuffer->stride<quint32>();
5705
5706 if (color.isTransparent())
5707 return;
5708
5709 const QColorTrcLut *colorProfile = nullptr;
5710
5711 if (useGammaCorrection)
5712 colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA8Text();
5713
5714 QRgba64 srcColor = color;
5715 if (colorProfile && color.isOpaque())
5716 srcColor = colorProfile->toLinear(rgb64: srcColor);
5717
5718 if (!clip) {
5719 quint32 *dest = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y)) + x;
5720 while (--mapHeight >= 0) {
5721 qt_alphamapblit_argb32_oneline(map, mapWidth, srcColor, dest, c, colorProfile);
5722 dest += destStride;
5723 map += mapStride;
5724 }
5725 } else {
5726 int bottom = qMin(a: y + mapHeight, b: rasterBuffer->height());
5727
5728 int top = qMax(a: y, b: 0);
5729 map += (top - y) * mapStride;
5730
5731 const_cast<QClipData *>(clip)->initialize();
5732 for (int yp = top; yp<bottom; ++yp) {
5733 const QClipData::ClipLine &line = clip->m_clipLines[yp];
5734
5735 quint32 *dest = reinterpret_cast<quint32 *>(rasterBuffer->scanLine(y: yp));
5736
5737 for (int i=0; i<line.count; ++i) {
5738 const QT_FT_Span &clip = line.spans[i];
5739 int start = qMax<int>(a: x, b: clip.x);
5740 int end = qMin<int>(a: x + mapWidth, b: clip.x + clip.len);
5741 qt_alphamapblit_argb32_oneline(map: map + start - x, mapWidth: end - start, srcColor, dest: dest + start, c, colorProfile);
5742 } // for (yp -> bottom)
5743 map += mapStride;
5744 }
5745 }
5746}
5747
5748#if QT_CONFIG(raster_64bit)
5749static void qt_alphamapblit_nonpremul_argb32(QRasterBuffer *rasterBuffer,
5750 int x, int y, const QRgba64 &color,
5751 const uchar *map,
5752 int mapWidth, int mapHeight, int mapStride,
5753 const QClipData *clip, bool useGammaCorrection)
5754{
5755 if (clip)
5756 return qt_alphamapblit_generic(rasterBuffer, x, y, color, map, mapWidth, mapHeight,
5757 mapStride, clip, useGammaCorrection);
5758
5759 if (color.isTransparent())
5760 return;
5761
5762 const QColorTrcLut *colorProfile = nullptr;
5763
5764 if (useGammaCorrection)
5765 colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA8Text();
5766
5767 const quint32 c = color.toArgb32();
5768 QRgba64 srcColor = color;
5769 if (colorProfile && color.isOpaque())
5770 srcColor = colorProfile->toLinear(rgb64: srcColor);
5771
5772 alignas(8) Q_DECL_UNINITIALIZED QRgba64 buffer[BufferSize];
5773 const DestFetchProc64 destFetch64 = destFetchProc64[rasterBuffer->format];
5774 const DestStoreProc64 destStore64 = destStoreProc64[rasterBuffer->format];
5775
5776 for (int ly = 0; ly < mapHeight; ++ly) {
5777 bool dstFullyOpaque = true;
5778 int i = x;
5779 int length = mapWidth;
5780 while (length > 0) {
5781 int l = qMin(a: BufferSize, b: length);
5782 quint32 *dest = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y: y + ly)) + i;
5783 for (int j = 0; j < l && dstFullyOpaque; ++j)
5784 dstFullyOpaque = (dest[j] & 0xff000000) == 0xff000000;
5785 if (dstFullyOpaque) {
5786 // Use RGB/ARGB32PM optimized version
5787 qt_alphamapblit_argb32_oneline(map: map + i - x, mapWidth: l, srcColor, dest, c, colorProfile);
5788 } else {
5789 // Use generic version
5790 QRgba64 *dest64 = destFetch64(buffer, rasterBuffer, i, y + ly, l);
5791 qt_alphamapblit_generic_oneline(map: map + i - x, len: l,
5792 srcColor, dest: dest64, color,
5793 colorProfile);
5794 if (destStore64)
5795 destStore64(rasterBuffer, i, y + ly, dest64, l);
5796 }
5797 length -= l;
5798 i += l;
5799 }
5800 map += mapStride;
5801 }
5802}
5803#endif
5804
5805static inline int qRgbAvg(QRgb rgb)
5806{
5807 return (qRed(rgb) * 5 + qGreen(rgb) * 6 + qBlue(rgb) * 5) / 16;
5808}
5809
5810static inline void rgbBlendPixel(quint32 *dst, int coverage, QRgba64 slinear, const QColorTrcLut *colorProfile)
5811{
5812 // Do a gammacorrected RGB alphablend...
5813 const QRgba64 dlinear = colorProfile ? colorProfile->toLinear64(rgb32: *dst) : QRgba64::fromArgb32(rgb: *dst);
5814
5815 QRgba64 blend = rgbBlend(d: dlinear, s: slinear, rgbAlpha: coverage);
5816
5817 *dst = colorProfile ? colorProfile->fromLinear64(rgb64: blend) : toArgb32(rgba64: blend);
5818}
5819
5820static inline QRgb rgbBlend(QRgb d, QRgb s, uint rgbAlpha)
5821{
5822#if defined(__SSE2__)
5823 __m128i vd = _mm_cvtsi32_si128(a: d);
5824 __m128i vs = _mm_cvtsi32_si128(a: s);
5825 __m128i va = _mm_cvtsi32_si128(a: rgbAlpha);
5826 const __m128i vz = _mm_setzero_si128();
5827 vd = _mm_unpacklo_epi8(a: vd, b: vz);
5828 vs = _mm_unpacklo_epi8(a: vs, b: vz);
5829 va = _mm_unpacklo_epi8(a: va, b: vz);
5830 __m128i vb = _mm_xor_si128(a: _mm_set1_epi16(w: 255), b: va);
5831 vs = _mm_mullo_epi16(a: vs, b: va);
5832 vd = _mm_mullo_epi16(a: vd, b: vb);
5833 vd = _mm_add_epi16(a: vd, b: vs);
5834 vd = _mm_add_epi16(a: vd, b: _mm_srli_epi16(a: vd, count: 8));
5835 vd = _mm_add_epi16(a: vd, b: _mm_set1_epi16(w: 0x80));
5836 vd = _mm_srli_epi16(a: vd, count: 8);
5837 vd = _mm_packus_epi16(a: vd, b: vd);
5838 return _mm_cvtsi128_si32(a: vd);
5839#else
5840 const int dr = qRed(d);
5841 const int dg = qGreen(d);
5842 const int db = qBlue(d);
5843
5844 const int sr = qRed(s);
5845 const int sg = qGreen(s);
5846 const int sb = qBlue(s);
5847
5848 const int mr = qRed(rgbAlpha);
5849 const int mg = qGreen(rgbAlpha);
5850 const int mb = qBlue(rgbAlpha);
5851
5852 const int nr = qt_div_255(sr * mr + dr * (255 - mr));
5853 const int ng = qt_div_255(sg * mg + dg * (255 - mg));
5854 const int nb = qt_div_255(sb * mb + db * (255 - mb));
5855
5856 return 0xff000000 | (nr << 16) | (ng << 8) | nb;
5857#endif
5858}
5859
5860static inline void alphargbblend_argb32(quint32 *dst, uint coverage, const QRgba64 &srcLinear, quint32 src, const QColorTrcLut *colorProfile)
5861{
5862 if (coverage == 0xff000000) {
5863 // nothing
5864 } else if (coverage == 0xffffffff && qAlpha(rgb: src) == 255) {
5865 blend_pixel(dst&: *dst, src);
5866 } else if (*dst < 0xff000000) {
5867 // Give up and do a naive gray alphablend. Needed to deal with ARGB32 and invalid ARGB32_premultiplied, see QTBUG-60571
5868 blend_pixel(dst&: *dst, src, const_alpha: qRgbAvg(rgb: coverage));
5869 } else if (!colorProfile) {
5870 // First do naive blend with text-color
5871 QRgb s = *dst;
5872 blend_pixel(dst&: s, src);
5873 // Then a naive blend with glyph shape
5874 *dst = rgbBlend(d: *dst, s, rgbAlpha: coverage);
5875 } else if (srcLinear.isOpaque()) {
5876 rgbBlendPixel(dst, coverage, slinear: srcLinear, colorProfile);
5877 } else {
5878 // First do naive blend with text-color
5879 QRgb s = *dst;
5880 blend_pixel(dst&: s, src);
5881 // Then gamma-corrected blend with glyph shape
5882 QRgba64 s64 = colorProfile ? colorProfile->toLinear64(rgb32: s) : QRgba64::fromArgb32(rgb: s);
5883 rgbBlendPixel(dst, coverage, slinear: s64, colorProfile);
5884 }
5885}
5886
5887#if QT_CONFIG(raster_64bit)
5888static inline void rgbBlendPixel(QRgba64 &dst, int coverage, QRgba64 slinear, const QColorTrcLut *colorProfile)
5889{
5890 // Do a gammacorrected RGB alphablend...
5891 const QRgba64 dlinear = colorProfile ? colorProfile->toLinear(rgb64: dst) : dst;
5892
5893 QRgba64 blend = rgbBlend(d: dlinear, s: slinear, rgbAlpha: coverage);
5894
5895 dst = colorProfile ? colorProfile->fromLinear(rgb64: blend) : blend;
5896}
5897
5898static inline void alphargbblend_generic(uint coverage, QRgba64 *dest, int x, const QRgba64 &srcLinear, const QRgba64 &src, const QColorTrcLut *colorProfile)
5899{
5900 if (coverage == 0xff000000) {
5901 // nothing
5902 } else if (coverage == 0xffffffff) {
5903 blend_pixel(dst&: dest[x], src);
5904 } else if (!dest[x].isOpaque()) {
5905 // Do a gray alphablend.
5906 alphamapblend_generic(coverage: qRgbAvg(rgb: coverage), dest, x, srcLinear, src, colorProfile);
5907 } else if (src.isOpaque()) {
5908 rgbBlendPixel(dst&: dest[x], coverage, slinear: srcLinear, colorProfile);
5909 } else {
5910 // First do naive blend with text-color
5911 QRgba64 s = dest[x];
5912 blend_pixel(dst&: s, src);
5913 // Then gamma-corrected blend with glyph shape
5914 if (colorProfile)
5915 s = colorProfile->toLinear(rgb64: s);
5916 rgbBlendPixel(dst&: dest[x], coverage, slinear: s, colorProfile);
5917 }
5918}
5919
5920static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer,
5921 int x, int y, const QRgba64 &color,
5922 const uint *src, int mapWidth, int mapHeight, int srcStride,
5923 const QClipData *clip, bool useGammaCorrection)
5924{
5925 if (color.isTransparent())
5926 return;
5927
5928 const QColorTrcLut *colorProfile = nullptr;
5929
5930 if (useGammaCorrection)
5931 colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA32Text();
5932
5933 QRgba64 srcColor = color;
5934 if (colorProfile && color.isOpaque())
5935 srcColor = colorProfile->toLinear(rgb64: srcColor);
5936
5937 alignas(8) Q_DECL_UNINITIALIZED QRgba64 buffer[BufferSize];
5938 const DestFetchProc64 destFetch64 = destFetchProc64[rasterBuffer->format];
5939 const DestStoreProc64 destStore64 = destStoreProc64[rasterBuffer->format];
5940
5941 if (!clip) {
5942 for (int ly = 0; ly < mapHeight; ++ly) {
5943 int i = x;
5944 int length = mapWidth;
5945 while (length > 0) {
5946 int l = qMin(a: BufferSize, b: length);
5947 QRgba64 *dest = destFetch64(buffer, rasterBuffer, i, y + ly, l);
5948 for (int j=0; j < l; ++j) {
5949 const uint coverage = src[j + (i - x)];
5950 alphargbblend_generic(coverage, dest, x: j, srcLinear: srcColor, src: color, colorProfile);
5951 }
5952 if (destStore64)
5953 destStore64(rasterBuffer, i, y + ly, dest, l);
5954 length -= l;
5955 i += l;
5956 }
5957 src += srcStride;
5958 }
5959 } else {
5960 int bottom = qMin(a: y + mapHeight, b: rasterBuffer->height());
5961
5962 int top = qMax(a: y, b: 0);
5963 src += (top - y) * srcStride;
5964
5965 const_cast<QClipData *>(clip)->initialize();
5966 for (int yp = top; yp<bottom; ++yp) {
5967 const QClipData::ClipLine &line = clip->m_clipLines[yp];
5968
5969 for (int i=0; i<line.count; ++i) {
5970 const QT_FT_Span &clip = line.spans[i];
5971
5972 int start = qMax<int>(a: x, b: clip.x);
5973 int end = qMin<int>(a: x + mapWidth, b: clip.x + clip.len);
5974 if (end <= start)
5975 continue;
5976 Q_ASSERT(end - start <= BufferSize);
5977 QRgba64 *dest = destFetch64(buffer, rasterBuffer, start, clip.y, end - start);
5978
5979 for (int xp=start; xp<end; ++xp) {
5980 const uint coverage = src[xp - x];
5981 alphargbblend_generic(coverage, dest, x: xp - start, srcLinear: srcColor, src: color, colorProfile);
5982 }
5983 if (destStore64)
5984 destStore64(rasterBuffer, start, clip.y, dest, end - start);
5985 } // for (i -> line.count)
5986 src += srcStride;
5987 } // for (yp -> bottom)
5988 }
5989}
5990#else
5991static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer,
5992 int x, int y, const QRgba64 &color,
5993 const uint *src, int mapWidth, int mapHeight, int srcStride,
5994 const QClipData *clip, bool useGammaCorrection)
5995{
5996 if (color.isTransparent())
5997 return;
5998
5999 const quint32 c = color.toArgb32();
6000
6001 const QColorTrcLut *colorProfile = nullptr;
6002
6003 if (useGammaCorrection)
6004 colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA32Text();
6005
6006 QRgba64 srcColor = color;
6007 if (colorProfile && color.isOpaque())
6008 srcColor = colorProfile->toLinear(srcColor);
6009
6010 Q_DECL_UNINITIALIZED quint32 buffer[BufferSize];
6011 const DestFetchProc destFetch = destFetchProc[rasterBuffer->format];
6012 const DestStoreProc destStore = destStoreProc[rasterBuffer->format];
6013
6014 if (!clip) {
6015 for (int ly = 0; ly < mapHeight; ++ly) {
6016 int i = x;
6017 int length = mapWidth;
6018 while (length > 0) {
6019 int l = qMin(BufferSize, length);
6020 quint32 *dest = destFetch(buffer, rasterBuffer, i, y + ly, l);
6021 for (int j=0; j < l; ++j) {
6022 const uint coverage = src[j + (i - x)];
6023 alphargbblend_argb32(dest + j, coverage, srcColor, c, colorProfile);
6024 }
6025 if (destStore)
6026 destStore(rasterBuffer, i, y + ly, dest, l);
6027 length -= l;
6028 i += l;
6029 }
6030 src += srcStride;
6031 }
6032 } else {
6033 int bottom = qMin(y + mapHeight, rasterBuffer->height());
6034
6035 int top = qMax(y, 0);
6036 src += (top - y) * srcStride;
6037
6038 const_cast<QClipData *>(clip)->initialize();
6039 for (int yp = top; yp<bottom; ++yp) {
6040 const QClipData::ClipLine &line = clip->m_clipLines[yp];
6041
6042 for (int i=0; i<line.count; ++i) {
6043 const QT_FT_Span &clip = line.spans[i];
6044
6045 int start = qMax<int>(x, clip.x);
6046 int end = qMin<int>(x + mapWidth, clip.x + clip.len);
6047 if (end <= start)
6048 continue;
6049 Q_ASSERT(end - start <= BufferSize);
6050 quint32 *dest = destFetch(buffer, rasterBuffer, start, clip.y, end - start);
6051
6052 for (int xp=start; xp<end; ++xp) {
6053 const uint coverage = src[xp - x];
6054 alphargbblend_argb32(dest + xp - start, coverage, srcColor, c, colorProfile);
6055 }
6056 if (destStore)
6057 destStore(rasterBuffer, start, clip.y, dest, end - start);
6058 } // for (i -> line.count)
6059 src += srcStride;
6060 } // for (yp -> bottom)
6061 }
6062}
6063#endif
6064
6065static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer,
6066 int x, int y, const QRgba64 &color,
6067 const uint *src, int mapWidth, int mapHeight, int srcStride,
6068 const QClipData *clip, bool useGammaCorrection)
6069{
6070 if (color.isTransparent())
6071 return;
6072
6073 const quint32 c = color.toArgb32();
6074
6075 const QColorTrcLut *colorProfile = nullptr;
6076
6077 if (useGammaCorrection)
6078 colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA32Text();
6079
6080 QRgba64 srcColor = color;
6081 if (colorProfile && color.isOpaque())
6082 srcColor = colorProfile->toLinear(rgb64: srcColor);
6083
6084 if (!clip) {
6085 quint32 *dst = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y)) + x;
6086 const int destStride = rasterBuffer->stride<quint32>();
6087 while (--mapHeight >= 0) {
6088 for (int i = 0; i < mapWidth; ++i) {
6089 const uint coverage = src[i];
6090 alphargbblend_argb32(dst: dst + i, coverage, srcLinear: srcColor, src: c, colorProfile);
6091 }
6092
6093 dst += destStride;
6094 src += srcStride;
6095 }
6096 } else {
6097 int bottom = qMin(a: y + mapHeight, b: rasterBuffer->height());
6098
6099 int top = qMax(a: y, b: 0);
6100 src += (top - y) * srcStride;
6101
6102 const_cast<QClipData *>(clip)->initialize();
6103 for (int yp = top; yp<bottom; ++yp) {
6104 const QClipData::ClipLine &line = clip->m_clipLines[yp];
6105
6106 quint32 *dst = reinterpret_cast<quint32 *>(rasterBuffer->scanLine(y: yp));
6107
6108 for (int i=0; i<line.count; ++i) {
6109 const QT_FT_Span &clip = line.spans[i];
6110
6111 int start = qMax<int>(a: x, b: clip.x);
6112 int end = qMin<int>(a: x + mapWidth, b: clip.x + clip.len);
6113
6114 for (int xp=start; xp<end; ++xp) {
6115 const uint coverage = src[xp - x];
6116 alphargbblend_argb32(dst: dst + xp, coverage, srcLinear: srcColor, src: c, colorProfile);
6117 }
6118 } // for (i -> line.count)
6119 src += srcStride;
6120 } // for (yp -> bottom)
6121
6122 }
6123}
6124
6125static void qt_rectfill_argb32(QRasterBuffer *rasterBuffer,
6126 int x, int y, int width, int height,
6127 const QRgba64 &color)
6128{
6129 qt_rectfill<quint32>(dest: reinterpret_cast<quint32 *>(rasterBuffer->buffer()),
6130 value: color.toArgb32(), x, y, width, height, stride: rasterBuffer->bytesPerLine());
6131}
6132
6133static void qt_rectfill_quint16(QRasterBuffer *rasterBuffer,
6134 int x, int y, int width, int height,
6135 const QRgba64 &color)
6136{
6137 const QPixelLayout &layout = qPixelLayouts[rasterBuffer->format];
6138 quint32 c32 = color.toArgb32();
6139 quint16 c16;
6140 layout.storeFromARGB32PM(reinterpret_cast<uchar *>(&c16), &c32, 0, 1, nullptr, nullptr);
6141 qt_rectfill<quint16>(dest: reinterpret_cast<quint16 *>(rasterBuffer->buffer()),
6142 value: c16, x, y, width, height, stride: rasterBuffer->bytesPerLine());
6143}
6144
6145static void qt_rectfill_quint24(QRasterBuffer *rasterBuffer,
6146 int x, int y, int width, int height,
6147 const QRgba64 &color)
6148{
6149 const QPixelLayout &layout = qPixelLayouts[rasterBuffer->format];
6150 quint32 c32 = color.toArgb32();
6151 quint24 c24;
6152 layout.storeFromARGB32PM(reinterpret_cast<uchar *>(&c24), &c32, 0, 1, nullptr, nullptr);
6153 qt_rectfill<quint24>(dest: reinterpret_cast<quint24 *>(rasterBuffer->buffer()),
6154 value: c24, x, y, width, height, stride: rasterBuffer->bytesPerLine());
6155}
6156
6157static void qt_rectfill_nonpremul_argb32(QRasterBuffer *rasterBuffer,
6158 int x, int y, int width, int height,
6159 const QRgba64 &color)
6160{
6161 qt_rectfill<quint32>(dest: reinterpret_cast<quint32 *>(rasterBuffer->buffer()),
6162 value: color.unpremultiplied().toArgb32(), x, y, width, height, stride: rasterBuffer->bytesPerLine());
6163}
6164
6165static void qt_rectfill_rgba(QRasterBuffer *rasterBuffer,
6166 int x, int y, int width, int height,
6167 const QRgba64 &color)
6168{
6169 qt_rectfill<quint32>(dest: reinterpret_cast<quint32 *>(rasterBuffer->buffer()),
6170 value: ARGB2RGBA(x: color.toArgb32()), x, y, width, height, stride: rasterBuffer->bytesPerLine());
6171}
6172
6173static void qt_rectfill_nonpremul_rgba(QRasterBuffer *rasterBuffer,
6174 int x, int y, int width, int height,
6175 const QRgba64 &color)
6176{
6177 qt_rectfill<quint32>(dest: reinterpret_cast<quint32 *>(rasterBuffer->buffer()),
6178 value: ARGB2RGBA(x: color.unpremultiplied().toArgb32()), x, y, width, height, stride: rasterBuffer->bytesPerLine());
6179}
6180
6181template<QtPixelOrder PixelOrder>
6182static void qt_rectfill_rgb30(QRasterBuffer *rasterBuffer,
6183 int x, int y, int width, int height,
6184 const QRgba64 &color)
6185{
6186 qt_rectfill<quint32>(reinterpret_cast<quint32 *>(rasterBuffer->buffer()),
6187 qConvertRgb64ToRgb30<PixelOrder>(color), x, y, width, height, rasterBuffer->bytesPerLine());
6188}
6189
6190static void qt_rectfill_alpha(QRasterBuffer *rasterBuffer,
6191 int x, int y, int width, int height,
6192 const QRgba64 &color)
6193{
6194 qt_rectfill<quint8>(dest: reinterpret_cast<quint8 *>(rasterBuffer->buffer()),
6195 value: color.alpha() >> 8, x, y, width, height, stride: rasterBuffer->bytesPerLine());
6196}
6197
6198static void qt_rectfill_gray(QRasterBuffer *rasterBuffer,
6199 int x, int y, int width, int height,
6200 const QRgba64 &color)
6201{
6202 qt_rectfill<quint8>(dest: reinterpret_cast<quint8 *>(rasterBuffer->buffer()),
6203 value: qGray(rgb: color.toArgb32()), x, y, width, height, stride: rasterBuffer->bytesPerLine());
6204}
6205
6206static void qt_rectfill_quint64(QRasterBuffer *rasterBuffer,
6207 int x, int y, int width, int height,
6208 const QRgba64 &color)
6209{
6210 const auto store = qStoreFromRGBA64PM[rasterBuffer->format];
6211 quint64 c64;
6212 store(reinterpret_cast<uchar *>(&c64), &color, 0, 1, nullptr, nullptr);
6213 qt_rectfill<quint64>(dest: reinterpret_cast<quint64 *>(rasterBuffer->buffer()),
6214 value: c64, x, y, width, height, stride: rasterBuffer->bytesPerLine());
6215}
6216
6217static void qt_rectfill_fp32x4(QRasterBuffer *rasterBuffer,
6218 int x, int y, int width, int height,
6219 const QRgba64 &color)
6220{
6221 const auto store = qStoreFromRGBA64PM[rasterBuffer->format];
6222 QRgbaFloat32 c;
6223 store(reinterpret_cast<uchar *>(&c), &color, 0, 1, nullptr, nullptr);
6224 qt_rectfill<QRgbaFloat32>(dest: reinterpret_cast<QRgbaFloat32 *>(rasterBuffer->buffer()),
6225 value: c, x, y, width, height, stride: rasterBuffer->bytesPerLine());
6226}
6227
6228// Map table for destination image format. Contains function pointers
6229// for blends of various types unto the destination
6230
6231DrawHelper qDrawHelper[] =
6232{
6233 // Format_Invalid,
6234 { .blendColor: nullptr, .bitmapBlit: nullptr, .alphamapBlit: nullptr, .alphaRGBBlit: nullptr, .fillRect: nullptr },
6235 // Format_Mono,
6236 {
6237 .blendColor: blend_color_generic,
6238 .bitmapBlit: nullptr, .alphamapBlit: nullptr, .alphaRGBBlit: nullptr, .fillRect: nullptr
6239 },
6240 // Format_MonoLSB,
6241 {
6242 .blendColor: blend_color_generic,
6243 .bitmapBlit: nullptr, .alphamapBlit: nullptr, .alphaRGBBlit: nullptr, .fillRect: nullptr
6244 },
6245 // Format_Indexed8,
6246 {
6247 .blendColor: blend_color_generic,
6248 .bitmapBlit: nullptr, .alphamapBlit: nullptr, .alphaRGBBlit: nullptr, .fillRect: nullptr
6249 },
6250 // Format_RGB32,
6251 {
6252 .blendColor: blend_color_argb,
6253 .bitmapBlit: qt_bitmapblit_argb32,
6254 .alphamapBlit: qt_alphamapblit_argb32,
6255 .alphaRGBBlit: qt_alphargbblit_argb32,
6256 .fillRect: qt_rectfill_argb32
6257 },
6258 // Format_ARGB32,
6259 {
6260 .blendColor: blend_color_generic,
6261 .bitmapBlit: qt_bitmapblit_argb32,
6262#if QT_CONFIG(raster_64bit)
6263 .alphamapBlit: qt_alphamapblit_nonpremul_argb32,
6264#else
6265 qt_alphamapblit_generic,
6266#endif
6267 .alphaRGBBlit: qt_alphargbblit_generic,
6268 .fillRect: qt_rectfill_nonpremul_argb32
6269 },
6270 // Format_ARGB32_Premultiplied
6271 {
6272 .blendColor: blend_color_argb,
6273 .bitmapBlit: qt_bitmapblit_argb32,
6274 .alphamapBlit: qt_alphamapblit_argb32,
6275 .alphaRGBBlit: qt_alphargbblit_argb32,
6276 .fillRect: qt_rectfill_argb32
6277 },
6278 // Format_RGB16
6279 {
6280 .blendColor: blend_color_generic,
6281 .bitmapBlit: qt_bitmapblit_quint16,
6282 .alphamapBlit: qt_alphamapblit_quint16,
6283 .alphaRGBBlit: qt_alphargbblit_generic,
6284 .fillRect: qt_rectfill_quint16
6285 },
6286 // Format_ARGB8565_Premultiplied
6287 {
6288 .blendColor: blend_color_generic,
6289 .bitmapBlit: nullptr,
6290 .alphamapBlit: qt_alphamapblit_generic,
6291 .alphaRGBBlit: qt_alphargbblit_generic,
6292 .fillRect: qt_rectfill_quint24
6293 },
6294 // Format_RGB666
6295 {
6296 .blendColor: blend_color_generic,
6297 .bitmapBlit: nullptr,
6298 .alphamapBlit: qt_alphamapblit_generic,
6299 .alphaRGBBlit: qt_alphargbblit_generic,
6300 .fillRect: qt_rectfill_quint24
6301 },
6302 // Format_ARGB6666_Premultiplied
6303 {
6304 .blendColor: blend_color_generic,
6305 .bitmapBlit: nullptr,
6306 .alphamapBlit: qt_alphamapblit_generic,
6307 .alphaRGBBlit: qt_alphargbblit_generic,
6308 .fillRect: qt_rectfill_quint24
6309 },
6310 // Format_RGB555
6311 {
6312 .blendColor: blend_color_generic,
6313 .bitmapBlit: nullptr,
6314 .alphamapBlit: qt_alphamapblit_generic,
6315 .alphaRGBBlit: qt_alphargbblit_generic,
6316 .fillRect: qt_rectfill_quint16
6317 },
6318 // Format_ARGB8555_Premultiplied
6319 {
6320 .blendColor: blend_color_generic,
6321 .bitmapBlit: nullptr,
6322 .alphamapBlit: qt_alphamapblit_generic,
6323 .alphaRGBBlit: qt_alphargbblit_generic,
6324 .fillRect: qt_rectfill_quint24
6325 },
6326 // Format_RGB888
6327 {
6328 .blendColor: blend_color_generic,
6329 .bitmapBlit: nullptr,
6330 .alphamapBlit: qt_alphamapblit_generic,
6331 .alphaRGBBlit: qt_alphargbblit_generic,
6332 .fillRect: qt_rectfill_quint24
6333 },
6334 // Format_RGB444
6335 {
6336 .blendColor: blend_color_generic,
6337 .bitmapBlit: nullptr,
6338 .alphamapBlit: qt_alphamapblit_generic,
6339 .alphaRGBBlit: qt_alphargbblit_generic,
6340 .fillRect: qt_rectfill_quint16
6341 },
6342 // Format_ARGB4444_Premultiplied
6343 {
6344 .blendColor: blend_color_generic,
6345 .bitmapBlit: nullptr,
6346 .alphamapBlit: qt_alphamapblit_generic,
6347 .alphaRGBBlit: qt_alphargbblit_generic,
6348 .fillRect: qt_rectfill_quint16
6349 },
6350 // Format_RGBX8888
6351 {
6352 .blendColor: blend_color_generic,
6353 .bitmapBlit: qt_bitmapblit_rgba8888,
6354 .alphamapBlit: qt_alphamapblit_generic,
6355 .alphaRGBBlit: qt_alphargbblit_generic,
6356 .fillRect: qt_rectfill_rgba
6357 },
6358 // Format_RGBA8888
6359 {
6360 .blendColor: blend_color_generic,
6361 .bitmapBlit: qt_bitmapblit_rgba8888,
6362 .alphamapBlit: qt_alphamapblit_generic,
6363 .alphaRGBBlit: qt_alphargbblit_generic,
6364 .fillRect: qt_rectfill_nonpremul_rgba
6365 },
6366 // Format_RGB8888_Premultiplied
6367 {
6368 .blendColor: blend_color_generic,
6369 .bitmapBlit: qt_bitmapblit_rgba8888,
6370 .alphamapBlit: qt_alphamapblit_generic,
6371 .alphaRGBBlit: qt_alphargbblit_generic,
6372 .fillRect: qt_rectfill_rgba
6373 },
6374 // Format_BGR30
6375 {
6376 .blendColor: blend_color_generic_rgb64,
6377 .bitmapBlit: qt_bitmapblit_rgb30<PixelOrderBGR>,
6378 .alphamapBlit: qt_alphamapblit_generic,
6379 .alphaRGBBlit: qt_alphargbblit_generic,
6380 .fillRect: qt_rectfill_rgb30<PixelOrderBGR>
6381 },
6382 // Format_A2BGR30_Premultiplied
6383 {
6384 .blendColor: blend_color_generic_rgb64,
6385 .bitmapBlit: qt_bitmapblit_rgb30<PixelOrderBGR>,
6386 .alphamapBlit: qt_alphamapblit_generic,
6387 .alphaRGBBlit: qt_alphargbblit_generic,
6388 .fillRect: qt_rectfill_rgb30<PixelOrderBGR>
6389 },
6390 // Format_RGB30
6391 {
6392 .blendColor: blend_color_generic_rgb64,
6393 .bitmapBlit: qt_bitmapblit_rgb30<PixelOrderRGB>,
6394 .alphamapBlit: qt_alphamapblit_generic,
6395 .alphaRGBBlit: qt_alphargbblit_generic,
6396 .fillRect: qt_rectfill_rgb30<PixelOrderRGB>
6397 },
6398 // Format_A2RGB30_Premultiplied
6399 {
6400 .blendColor: blend_color_generic_rgb64,
6401 .bitmapBlit: qt_bitmapblit_rgb30<PixelOrderRGB>,
6402 .alphamapBlit: qt_alphamapblit_generic,
6403 .alphaRGBBlit: qt_alphargbblit_generic,
6404 .fillRect: qt_rectfill_rgb30<PixelOrderRGB>
6405 },
6406 // Format_Alpha8
6407 {
6408 .blendColor: blend_color_generic,
6409 .bitmapBlit: nullptr,
6410 .alphamapBlit: qt_alphamapblit_generic,
6411 .alphaRGBBlit: qt_alphargbblit_generic,
6412 .fillRect: qt_rectfill_alpha
6413 },
6414 // Format_Grayscale8
6415 {
6416 .blendColor: blend_color_generic,
6417 .bitmapBlit: nullptr,
6418 .alphamapBlit: qt_alphamapblit_generic,
6419 .alphaRGBBlit: qt_alphargbblit_generic,
6420 .fillRect: qt_rectfill_gray
6421 },
6422 // Format_RGBX64
6423 {
6424 .blendColor: blend_color_generic_rgb64,
6425 .bitmapBlit: nullptr,
6426 .alphamapBlit: qt_alphamapblit_generic,
6427 .alphaRGBBlit: qt_alphargbblit_generic,
6428 .fillRect: qt_rectfill_quint64
6429 },
6430 // Format_RGBA64
6431 {
6432 .blendColor: blend_color_generic_rgb64,
6433 .bitmapBlit: nullptr,
6434 .alphamapBlit: qt_alphamapblit_generic,
6435 .alphaRGBBlit: qt_alphargbblit_generic,
6436 .fillRect: qt_rectfill_quint64
6437 },
6438 // Format_RGBA64_Premultiplied
6439 {
6440 .blendColor: blend_color_generic_rgb64,
6441 .bitmapBlit: nullptr,
6442 .alphamapBlit: qt_alphamapblit_generic,
6443 .alphaRGBBlit: qt_alphargbblit_generic,
6444 .fillRect: qt_rectfill_quint64
6445 },
6446 // Format_Grayscale16
6447 {
6448 .blendColor: blend_color_generic_rgb64,
6449 .bitmapBlit: nullptr,
6450 .alphamapBlit: qt_alphamapblit_generic,
6451 .alphaRGBBlit: qt_alphargbblit_generic,
6452 .fillRect: qt_rectfill_quint16
6453 },
6454 // Format_BGR888
6455 {
6456 .blendColor: blend_color_generic,
6457 .bitmapBlit: nullptr,
6458 .alphamapBlit: qt_alphamapblit_generic,
6459 .alphaRGBBlit: qt_alphargbblit_generic,
6460 .fillRect: qt_rectfill_quint24
6461 },
6462 // Format_RGBX16FPx4
6463 {
6464 .blendColor: blend_color_generic_fp,
6465 .bitmapBlit: nullptr,
6466 .alphamapBlit: qt_alphamapblit_generic,
6467 .alphaRGBBlit: qt_alphargbblit_generic,
6468 .fillRect: qt_rectfill_quint64
6469 },
6470 // Format_RGBA16FPx4
6471 {
6472 .blendColor: blend_color_generic_fp,
6473 .bitmapBlit: nullptr,
6474 .alphamapBlit: qt_alphamapblit_generic,
6475 .alphaRGBBlit: qt_alphargbblit_generic,
6476 .fillRect: qt_rectfill_quint64
6477 },
6478 // Format_RGBA16FPx4_Premultiplied
6479 {
6480 .blendColor: blend_color_generic_fp,
6481 .bitmapBlit: nullptr,
6482 .alphamapBlit: qt_alphamapblit_generic,
6483 .alphaRGBBlit: qt_alphargbblit_generic,
6484 .fillRect: qt_rectfill_quint64
6485 },
6486 // Format_RGBX32FPx4
6487 {
6488 .blendColor: blend_color_generic_fp,
6489 .bitmapBlit: nullptr,
6490 .alphamapBlit: qt_alphamapblit_generic,
6491 .alphaRGBBlit: qt_alphargbblit_generic,
6492 .fillRect: qt_rectfill_fp32x4
6493 },
6494 // Format_RGBA32FPx4
6495 {
6496 .blendColor: blend_color_generic_fp,
6497 .bitmapBlit: nullptr,
6498 .alphamapBlit: qt_alphamapblit_generic,
6499 .alphaRGBBlit: qt_alphargbblit_generic,
6500 .fillRect: qt_rectfill_fp32x4
6501 },
6502 // Format_RGBA32FPx4_Premultiplied
6503 {
6504 .blendColor: blend_color_generic_fp,
6505 .bitmapBlit: nullptr,
6506 .alphamapBlit: qt_alphamapblit_generic,
6507 .alphaRGBBlit: qt_alphargbblit_generic,
6508 .fillRect: qt_rectfill_fp32x4
6509 },
6510};
6511
6512static_assert(std::size(qDrawHelper) == QImage::NImageFormats);
6513
6514#if !defined(Q_PROCESSOR_X86) && !defined(QT_COMPILER_SUPPORTS_LSX)
6515void qt_memfill64(quint64 *dest, quint64 color, qsizetype count)
6516{
6517 qt_memfill_template<quint64>(dest, color, count);
6518}
6519#endif
6520
6521#if defined(QT_COMPILER_SUPPORTS_SSSE3) && defined(Q_CC_GNU) && !defined(Q_CC_CLANG)
6522__attribute__((optimize("no-tree-vectorize")))
6523#endif
6524void qt_memfill24(quint24 *dest, quint24 color, qsizetype count)
6525{
6526# ifdef QT_COMPILER_SUPPORTS_SSSE3
6527 extern void qt_memfill24_ssse3(quint24 *, quint24, qsizetype);
6528 if (qCpuHasFeature(SSSE3))
6529 return qt_memfill24_ssse3(dest, color, count);
6530# elif defined QT_COMPILER_SUPPORTS_LSX
6531 extern void qt_memfill24_lsx(quint24 *, quint24, qsizetype);
6532 if (qCpuHasFeature(LSX))
6533 return qt_memfill24_lsx(dest, color, count);
6534# endif
6535
6536 const quint32 v = color;
6537 quint24 *end = dest + count;
6538
6539 // prolog: align dest to 32bit
6540 while ((quintptr(dest) & 0x3) && dest < end) {
6541 *dest++ = v;
6542 }
6543 if (dest >= end)
6544 return;
6545
6546 const uint val1 = qFromBigEndian(source: (v << 8) | (v >> 16));
6547 const uint val2 = qFromBigEndian(source: (v << 16) | (v >> 8));
6548 const uint val3 = qFromBigEndian(source: (v << 24) | (v >> 0));
6549
6550 for ( ; dest <= (end - 4); dest += 4) {
6551 quint32 *dst = reinterpret_cast<quint32 *>(dest);
6552 dst[0] = val1;
6553 dst[1] = val2;
6554 dst[2] = val3;
6555 }
6556
6557 // less than 4px left
6558 switch (end - dest) {
6559 case 3:
6560 *dest++ = v;
6561 Q_FALLTHROUGH();
6562 case 2:
6563 *dest++ = v;
6564 Q_FALLTHROUGH();
6565 case 1:
6566 *dest++ = v;
6567 }
6568}
6569
6570void qt_memfill16(quint16 *dest, quint16 value, qsizetype count)
6571{
6572 const int align = quintptr(dest) & 0x3;
6573 if (align) {
6574 *dest++ = value;
6575 --count;
6576 }
6577
6578 if (count & 0x1)
6579 dest[count - 1] = value;
6580
6581 const quint32 value32 = (value << 16) | value;
6582 qt_memfill32(reinterpret_cast<quint32*>(dest), value32, count / 2);
6583}
6584
6585#if defined(Q_PROCESSOR_X86) || defined(QT_COMPILER_SUPPORTS_LSX)
6586void (*qt_memfill32)(quint32 *dest, quint32 value, qsizetype count) = nullptr;
6587void (*qt_memfill64)(quint64 *dest, quint64 value, qsizetype count) = nullptr;
6588#elif !defined(__ARM_NEON__) && !defined(__MIPS_DSP__)
6589void qt_memfill32(quint32 *dest, quint32 color, qsizetype count)
6590{
6591 qt_memfill_template<quint32>(dest, color, count);
6592}
6593#endif
6594
6595#ifdef QT_COMPILER_SUPPORTS_SSE4_1
6596template<QtPixelOrder> void QT_FASTCALL storeA2RGB30PMFromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6597#elif defined(QT_COMPILER_SUPPORTS_LSX)
6598template<QtPixelOrder> void QT_FASTCALL storeA2RGB30PMFromARGB32PM_lsx(uchar *dest, const uint *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6599#endif
6600
6601extern void qInitBlendFunctions();
6602
6603static void qInitDrawhelperFunctions()
6604{
6605 // Set up basic blend function tables.
6606 qInitBlendFunctions();
6607
6608#if defined(Q_PROCESSOR_X86) && !defined(__SSE2__)
6609 qt_memfill32 = qt_memfill_template<quint32>;
6610 qt_memfill64 = qt_memfill_template<quint64>;
6611#elif defined(__SSE2__)
6612# ifndef __haswell__
6613 qt_memfill32 = qt_memfill32_sse2;
6614 qt_memfill64 = qt_memfill64_sse2;
6615# endif
6616 qDrawHelper[QImage::Format_RGB32].bitmapBlit = qt_bitmapblit32_sse2;
6617 qDrawHelper[QImage::Format_ARGB32].bitmapBlit = qt_bitmapblit32_sse2;
6618 qDrawHelper[QImage::Format_ARGB32_Premultiplied].bitmapBlit = qt_bitmapblit32_sse2;
6619 qDrawHelper[QImage::Format_RGB16].bitmapBlit = qt_bitmapblit16_sse2;
6620 qDrawHelper[QImage::Format_RGBX8888].bitmapBlit = qt_bitmapblit8888_sse2;
6621 qDrawHelper[QImage::Format_RGBA8888].bitmapBlit = qt_bitmapblit8888_sse2;
6622 qDrawHelper[QImage::Format_RGBA8888_Premultiplied].bitmapBlit = qt_bitmapblit8888_sse2;
6623
6624 extern void qt_scale_image_argb32_on_argb32_sse2(uchar *destPixels, int dbpl,
6625 const uchar *srcPixels, int sbpl, int srch,
6626 const QRectF &targetRect,
6627 const QRectF &sourceRect,
6628 const QRect &clip,
6629 int const_alpha);
6630 qScaleFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_scale_image_argb32_on_argb32_sse2;
6631 qScaleFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_scale_image_argb32_on_argb32_sse2;
6632 qScaleFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_scale_image_argb32_on_argb32_sse2;
6633 qScaleFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_scale_image_argb32_on_argb32_sse2;
6634
6635 extern void qt_blend_rgb32_on_rgb32_sse2(uchar *destPixels, int dbpl,
6636 const uchar *srcPixels, int sbpl,
6637 int w, int h,
6638 int const_alpha);
6639 extern void qt_blend_argb32_on_argb32_sse2(uchar *destPixels, int dbpl,
6640 const uchar *srcPixels, int sbpl,
6641 int w, int h,
6642 int const_alpha);
6643
6644 qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse2;
6645 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse2;
6646 qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse2;
6647 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse2;
6648 qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_sse2;
6649 qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_sse2;
6650 qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_sse2;
6651 qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_sse2;
6652
6653 extern const uint * QT_FASTCALL qt_fetch_radial_gradient_sse2(uint *buffer, const Operator *op, const QSpanData *data,
6654 int y, int x, int length);
6655
6656 qt_fetch_radial_gradient = qt_fetch_radial_gradient_sse2;
6657
6658 extern void QT_FASTCALL comp_func_SourceOver_sse2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
6659 extern void QT_FASTCALL comp_func_solid_SourceOver_sse2(uint *destPixels, int length, uint color, uint const_alpha);
6660 extern void QT_FASTCALL comp_func_Source_sse2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
6661 extern void QT_FASTCALL comp_func_solid_Source_sse2(uint *destPixels, int length, uint color, uint const_alpha);
6662 extern void QT_FASTCALL comp_func_Plus_sse2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
6663 qt_functionForMode_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_sse2;
6664 qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_sse2;
6665 qt_functionForMode_C[QPainter::CompositionMode_Source] = comp_func_Source_sse2;
6666 qt_functionForModeSolid_C[QPainter::CompositionMode_Source] = comp_func_solid_Source_sse2;
6667 qt_functionForMode_C[QPainter::CompositionMode_Plus] = comp_func_Plus_sse2;
6668
6669#ifdef QT_COMPILER_SUPPORTS_SSSE3
6670 if (qCpuHasFeature(SSSE3)) {
6671 extern void qt_blend_argb32_on_argb32_ssse3(uchar *destPixels, int dbpl,
6672 const uchar *srcPixels, int sbpl,
6673 int w, int h,
6674 int const_alpha);
6675
6676 extern const uint * QT_FASTCALL qt_fetchUntransformed_888_ssse3(uint *buffer, const Operator *, const QSpanData *data,
6677 int y, int x, int length);
6678 qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_ssse3;
6679 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_ssse3;
6680 qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_ssse3;
6681 qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_ssse3;
6682 sourceFetchUntransformed[QImage::Format_RGB888] = qt_fetchUntransformed_888_ssse3;
6683 extern void QT_FASTCALL rbSwap_888_ssse3(uchar *dst, const uchar *src, int count);
6684 qPixelLayouts[QImage::Format_RGB888].rbSwap = rbSwap_888_ssse3;
6685 qPixelLayouts[QImage::Format_BGR888].rbSwap = rbSwap_888_ssse3;
6686 }
6687#endif // SSSE3
6688
6689#if defined(QT_COMPILER_SUPPORTS_SSE4_1)
6690 if (qCpuHasFeature(SSE4_1)) {
6691 extern void QT_FASTCALL convertARGB32ToARGB32PM_sse4(uint *buffer, int count, const QList<QRgb> *);
6692 extern void QT_FASTCALL convertRGBA8888ToARGB32PM_sse4(uint *buffer, int count, const QList<QRgb> *);
6693 extern const uint *QT_FASTCALL fetchARGB32ToARGB32PM_sse4(uint *buffer, const uchar *src, int index, int count,
6694 const QList<QRgb> *, QDitherInfo *);
6695 extern const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_sse4(uint *buffer, const uchar *src, int index, int count,
6696 const QList<QRgb> *, QDitherInfo *);
6697 extern const QRgba64 * QT_FASTCALL convertARGB32ToRGBA64PM_sse4(QRgba64 *buffer, const uint *src, int count,
6698 const QList<QRgb> *, QDitherInfo *);
6699 extern const QRgba64 * QT_FASTCALL convertRGBA8888ToRGBA64PM_sse4(QRgba64 *buffer, const uint *src, int count,
6700 const QList<QRgb> *, QDitherInfo *);
6701 extern const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM_sse4(QRgba64 *buffer, const uchar *src, int index, int count,
6702 const QList<QRgb> *, QDitherInfo *);
6703 extern const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM_sse4(QRgba64 *buffer, const uchar *src, int index, int count,
6704 const QList<QRgb> *, QDitherInfo *);
6705 extern void QT_FASTCALL storeARGB32FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count,
6706 const QList<QRgb> *, QDitherInfo *);
6707 extern void QT_FASTCALL storeRGBA8888FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count,
6708 const QList<QRgb> *, QDitherInfo *);
6709 extern void QT_FASTCALL storeRGBXFromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count,
6710 const QList<QRgb> *, QDitherInfo *);
6711 extern void QT_FASTCALL storeARGB32FromRGBA64PM_sse4(uchar *dest, const QRgba64 *src, int index, int count,
6712 const QList<QRgb> *, QDitherInfo *);
6713 extern void QT_FASTCALL storeRGBA8888FromRGBA64PM_sse4(uchar *dest, const QRgba64 *src, int index, int count,
6714 const QList<QRgb> *, QDitherInfo *);
6715 extern void QT_FASTCALL storeRGBA64FromRGBA64PM_sse4(uchar *, const QRgba64 *, int, int, const QList<QRgb> *, QDitherInfo *);
6716 extern void QT_FASTCALL storeRGBx64FromRGBA64PM_sse4(uchar *, const QRgba64 *, int, int, const QList<QRgb> *, QDitherInfo *);
6717 extern void QT_FASTCALL destStore64ARGB32_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length);
6718 extern void QT_FASTCALL destStore64RGBA8888_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length);
6719# ifndef __haswell__
6720 qPixelLayouts[QImage::Format_ARGB32].fetchToARGB32PM = fetchARGB32ToARGB32PM_sse4;
6721 qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_sse4;
6722 qPixelLayouts[QImage::Format_RGBA8888].fetchToARGB32PM = fetchRGBA8888ToARGB32PM_sse4;
6723 qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_sse4;
6724 qPixelLayouts[QImage::Format_ARGB32].fetchToRGBA64PM = fetchARGB32ToRGBA64PM_sse4;
6725 qPixelLayouts[QImage::Format_ARGB32].convertToRGBA64PM = convertARGB32ToRGBA64PM_sse4;
6726 qPixelLayouts[QImage::Format_RGBA8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_sse4;
6727 qPixelLayouts[QImage::Format_RGBA8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_sse4;
6728 qPixelLayouts[QImage::Format_RGBX8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_sse4;
6729 qPixelLayouts[QImage::Format_RGBX8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_sse4;
6730# endif
6731 qPixelLayouts[QImage::Format_ARGB32].storeFromARGB32PM = storeARGB32FromARGB32PM_sse4;
6732 qPixelLayouts[QImage::Format_RGBA8888].storeFromARGB32PM = storeRGBA8888FromARGB32PM_sse4;
6733 qPixelLayouts[QImage::Format_RGBX8888].storeFromARGB32PM = storeRGBXFromARGB32PM_sse4;
6734 qPixelLayouts[QImage::Format_A2BGR30_Premultiplied].storeFromARGB32PM = storeA2RGB30PMFromARGB32PM_sse4<PixelOrderBGR>;
6735 qPixelLayouts[QImage::Format_A2RGB30_Premultiplied].storeFromARGB32PM = storeA2RGB30PMFromARGB32PM_sse4<PixelOrderRGB>;
6736 qStoreFromRGBA64PM[QImage::Format_ARGB32] = storeARGB32FromRGBA64PM_sse4;
6737 qStoreFromRGBA64PM[QImage::Format_RGBA8888] = storeRGBA8888FromRGBA64PM_sse4;
6738 qStoreFromRGBA64PM[QImage::Format_RGBX64] = storeRGBx64FromRGBA64PM_sse4;
6739 qStoreFromRGBA64PM[QImage::Format_RGBA64] = storeRGBA64FromRGBA64PM_sse4;
6740#if QT_CONFIG(raster_64bit)
6741 destStoreProc64[QImage::Format_ARGB32] = destStore64ARGB32_sse4;
6742 destStoreProc64[QImage::Format_RGBA8888] = destStore64RGBA8888_sse4;
6743#endif
6744#if QT_CONFIG(raster_fp)
6745 extern const QRgbaFloat32 *QT_FASTCALL fetchRGBA32FToRGBA32F_sse4(QRgbaFloat32 *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6746 extern void QT_FASTCALL storeRGBX32FFromRGBA32F_sse4(uchar *dest, const QRgbaFloat32 *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6747 extern void QT_FASTCALL storeRGBA32FFromRGBA32F_sse4(uchar *dest, const QRgbaFloat32 *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6748 qFetchToRGBA32F[QImage::Format_RGBA32FPx4] = fetchRGBA32FToRGBA32F_sse4;
6749 qStoreFromRGBA32F[QImage::Format_RGBX32FPx4] = storeRGBX32FFromRGBA32F_sse4;
6750 qStoreFromRGBA32F[QImage::Format_RGBA32FPx4] = storeRGBA32FFromRGBA32F_sse4;
6751#endif // QT_CONFIG(raster_fp)
6752 }
6753#endif
6754
6755#if defined(QT_COMPILER_SUPPORTS_AVX2)
6756 if (qCpuHasFeature(ArchHaswell)) {
6757 qt_memfill32 = qt_memfill32_avx2;
6758 qt_memfill64 = qt_memfill64_avx2;
6759 extern void qt_blend_rgb32_on_rgb32_avx2(uchar *destPixels, int dbpl,
6760 const uchar *srcPixels, int sbpl,
6761 int w, int h, int const_alpha);
6762 extern void qt_blend_argb32_on_argb32_avx2(uchar *destPixels, int dbpl,
6763 const uchar *srcPixels, int sbpl,
6764 int w, int h, int const_alpha);
6765 qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_avx2;
6766 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_avx2;
6767 qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_avx2;
6768 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_avx2;
6769 qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_avx2;
6770 qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_avx2;
6771 qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_avx2;
6772 qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_avx2;
6773
6774 extern void QT_FASTCALL comp_func_Source_avx2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
6775 extern void QT_FASTCALL comp_func_SourceOver_avx2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
6776 extern void QT_FASTCALL comp_func_solid_SourceOver_avx2(uint *destPixels, int length, uint color, uint const_alpha);
6777 qt_functionForMode_C[QPainter::CompositionMode_Source] = comp_func_Source_avx2;
6778 qt_functionForMode_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_avx2;
6779 qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_avx2;
6780#if QT_CONFIG(raster_64bit)
6781 extern void QT_FASTCALL comp_func_Source_rgb64_avx2(QRgba64 *destPixels, const QRgba64 *srcPixels, int length, uint const_alpha);
6782 extern void QT_FASTCALL comp_func_SourceOver_rgb64_avx2(QRgba64 *destPixels, const QRgba64 *srcPixels, int length, uint const_alpha);
6783 extern void QT_FASTCALL comp_func_solid_SourceOver_rgb64_avx2(QRgba64 *destPixels, int length, QRgba64 color, uint const_alpha);
6784 qt_functionForMode64_C[QPainter::CompositionMode_Source] = comp_func_Source_rgb64_avx2;
6785 qt_functionForMode64_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_rgb64_avx2;
6786 qt_functionForModeSolid64_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_rgb64_avx2;
6787#endif
6788#if QT_CONFIG(raster_fp)
6789 extern void QT_FASTCALL comp_func_Source_rgbafp_avx2(QRgbaFloat32 *destPixels, const QRgbaFloat32 *srcPixels, int length, uint const_alpha);
6790 extern void QT_FASTCALL comp_func_SourceOver_rgbafp_avx2(QRgbaFloat32 *destPixels, const QRgbaFloat32 *srcPixels, int length, uint const_alpha);
6791 extern void QT_FASTCALL comp_func_solid_Source_rgbafp_avx2(QRgbaFloat32 *destPixels, int length, QRgbaFloat32 color, uint const_alpha);
6792 extern void QT_FASTCALL comp_func_solid_SourceOver_rgbafp_avx2(QRgbaFloat32 *destPixels, int length, QRgbaFloat32 color, uint const_alpha);
6793 qt_functionForModeFP_C[QPainter::CompositionMode_Source] = comp_func_Source_rgbafp_avx2;
6794 qt_functionForModeFP_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_rgbafp_avx2;
6795 qt_functionForModeSolidFP_C[QPainter::CompositionMode_Source] = comp_func_solid_Source_rgbafp_avx2;
6796 qt_functionForModeSolidFP_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_rgbafp_avx2;
6797#endif
6798
6799 extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_scale_helper_avx2(uint *b, uint *end, const QTextureData &image,
6800 int &fx, int &fy, int fdx, int /*fdy*/);
6801 extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_downscale_helper_avx2(uint *b, uint *end, const QTextureData &image,
6802 int &fx, int &fy, int fdx, int /*fdy*/);
6803 extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_fast_rotate_helper_avx2(uint *b, uint *end, const QTextureData &image,
6804 int &fx, int &fy, int fdx, int fdy);
6805
6806 bilinearFastTransformHelperARGB32PM[0][SimpleScaleTransform] = fetchTransformedBilinearARGB32PM_simple_scale_helper_avx2;
6807 bilinearFastTransformHelperARGB32PM[0][DownscaleTransform] = fetchTransformedBilinearARGB32PM_downscale_helper_avx2;
6808 bilinearFastTransformHelperARGB32PM[0][FastRotateTransform] = fetchTransformedBilinearARGB32PM_fast_rotate_helper_avx2;
6809
6810 extern void QT_FASTCALL convertARGB32ToARGB32PM_avx2(uint *buffer, int count, const QList<QRgb> *);
6811 extern void QT_FASTCALL convertRGBA8888ToARGB32PM_avx2(uint *buffer, int count, const QList<QRgb> *);
6812 extern const uint *QT_FASTCALL fetchARGB32ToARGB32PM_avx2(uint *buffer, const uchar *src, int index, int count,
6813 const QList<QRgb> *, QDitherInfo *);
6814 extern const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_avx2(uint *buffer, const uchar *src, int index, int count,
6815 const QList<QRgb> *, QDitherInfo *);
6816 qPixelLayouts[QImage::Format_ARGB32].fetchToARGB32PM = fetchARGB32ToARGB32PM_avx2;
6817 qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_avx2;
6818 qPixelLayouts[QImage::Format_RGBA8888].fetchToARGB32PM = fetchRGBA8888ToARGB32PM_avx2;
6819 qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_avx2;
6820
6821 extern const QRgba64 *QT_FASTCALL convertARGB32ToRGBA64PM_avx2(QRgba64 *, const uint *, int, const QList<QRgb> *, QDitherInfo *);
6822 extern const QRgba64 *QT_FASTCALL convertRGBA8888ToRGBA64PM_avx2(QRgba64 *, const uint *, int count, const QList<QRgb> *, QDitherInfo *);
6823 extern const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM_avx2(QRgba64 *, const uchar *, int, int, const QList<QRgb> *, QDitherInfo *);
6824 extern const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM_avx2(QRgba64 *, const uchar *, int, int, const QList<QRgb> *, QDitherInfo *);
6825 extern const QRgba64 *QT_FASTCALL fetchRGBA64ToRGBA64PM_avx2(QRgba64 *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6826 qPixelLayouts[QImage::Format_ARGB32].convertToRGBA64PM = convertARGB32ToRGBA64PM_avx2;
6827 qPixelLayouts[QImage::Format_RGBX8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_avx2;
6828 qPixelLayouts[QImage::Format_ARGB32].fetchToRGBA64PM = fetchARGB32ToRGBA64PM_avx2;
6829 qPixelLayouts[QImage::Format_RGBX8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_avx2;
6830 qPixelLayouts[QImage::Format_RGBA64].fetchToRGBA64PM = fetchRGBA64ToRGBA64PM_avx2;
6831
6832 extern const uint *QT_FASTCALL fetchRGB16FToRGB32_avx2(uint *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6833 extern const uint *QT_FASTCALL fetchRGBA16FToARGB32PM_avx2(uint *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6834 extern const QRgba64 *QT_FASTCALL fetchRGBA16FPMToRGBA64PM_avx2(QRgba64 *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6835 extern const QRgba64 *QT_FASTCALL fetchRGBA16FToRGBA64PM_avx2(QRgba64 *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6836 extern void QT_FASTCALL storeRGB16FFromRGB32_avx2(uchar *dest, const uint *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6837 extern void QT_FASTCALL storeRGBA16FFromARGB32PM_avx2(uchar *dest, const uint *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6838 qPixelLayouts[QImage::Format_RGBX16FPx4].fetchToARGB32PM = fetchRGB16FToRGB32_avx2;
6839 qPixelLayouts[QImage::Format_RGBX16FPx4].fetchToRGBA64PM = fetchRGBA16FPMToRGBA64PM_avx2;
6840 qPixelLayouts[QImage::Format_RGBX16FPx4].storeFromARGB32PM = storeRGB16FFromRGB32_avx2;
6841 qPixelLayouts[QImage::Format_RGBX16FPx4].storeFromRGB32 = storeRGB16FFromRGB32_avx2;
6842 qPixelLayouts[QImage::Format_RGBA16FPx4].fetchToARGB32PM = fetchRGBA16FToARGB32PM_avx2;
6843 qPixelLayouts[QImage::Format_RGBA16FPx4].fetchToRGBA64PM = fetchRGBA16FToRGBA64PM_avx2;
6844 qPixelLayouts[QImage::Format_RGBA16FPx4].storeFromARGB32PM = storeRGBA16FFromARGB32PM_avx2;
6845 qPixelLayouts[QImage::Format_RGBA16FPx4].storeFromRGB32 = storeRGB16FFromRGB32_avx2;
6846 qPixelLayouts[QImage::Format_RGBA16FPx4_Premultiplied].fetchToARGB32PM = fetchRGB16FToRGB32_avx2;
6847 qPixelLayouts[QImage::Format_RGBA16FPx4_Premultiplied].fetchToRGBA64PM = fetchRGBA16FPMToRGBA64PM_avx2;
6848 qPixelLayouts[QImage::Format_RGBA16FPx4_Premultiplied].storeFromARGB32PM = storeRGB16FFromRGB32_avx2;
6849 qPixelLayouts[QImage::Format_RGBA16FPx4_Premultiplied].storeFromRGB32 = storeRGB16FFromRGB32_avx2;
6850#if QT_CONFIG(raster_fp)
6851 extern const QRgbaFloat32 *QT_FASTCALL fetchRGBA16FToRGBA32F_avx2(QRgbaFloat32 *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6852 extern void QT_FASTCALL storeRGBX16FFromRGBA32F_avx2(uchar *dest, const QRgbaFloat32 *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6853 extern void QT_FASTCALL storeRGBA16FFromRGBA32F_avx2(uchar *dest, const QRgbaFloat32 *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6854 qFetchToRGBA32F[QImage::Format_RGBA16FPx4] = fetchRGBA16FToRGBA32F_avx2;
6855 qStoreFromRGBA32F[QImage::Format_RGBX16FPx4] = storeRGBX16FFromRGBA32F_avx2;
6856 qStoreFromRGBA32F[QImage::Format_RGBA16FPx4] = storeRGBA16FFromRGBA32F_avx2;
6857#endif // QT_CONFIG(raster_fp)
6858 }
6859
6860#endif
6861
6862#endif // SSE2
6863
6864#if defined(QT_COMPILER_SUPPORTS_LSX)
6865 if (qCpuHasFeature(LSX)) {
6866 qt_memfill32 = qt_memfill32_lsx;
6867 qt_memfill64 = qt_memfill64_lsx;
6868
6869 qDrawHelper[QImage::Format_RGB32].bitmapBlit = qt_bitmapblit32_lsx;
6870 qDrawHelper[QImage::Format_ARGB32].bitmapBlit = qt_bitmapblit32_lsx;
6871 qDrawHelper[QImage::Format_ARGB32_Premultiplied].bitmapBlit = qt_bitmapblit32_lsx;
6872 qDrawHelper[QImage::Format_RGB16].bitmapBlit = qt_bitmapblit16_lsx;
6873 qDrawHelper[QImage::Format_RGBX8888].bitmapBlit = qt_bitmapblit8888_lsx;
6874 qDrawHelper[QImage::Format_RGBA8888].bitmapBlit = qt_bitmapblit8888_lsx;
6875 qDrawHelper[QImage::Format_RGBA8888_Premultiplied].bitmapBlit = qt_bitmapblit8888_lsx;
6876
6877 extern void qt_scale_image_argb32_on_argb32_lsx(uchar *destPixels, int dbpl,
6878 const uchar *srcPixels, int sbpl, int srch,
6879 const QRectF &targetRect,
6880 const QRectF &sourceRect,
6881 const QRect &clip,
6882 int const_alpha);
6883
6884 qScaleFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_scale_image_argb32_on_argb32_lsx;
6885 qScaleFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_scale_image_argb32_on_argb32_lsx;
6886 qScaleFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_scale_image_argb32_on_argb32_lsx;
6887 qScaleFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_scale_image_argb32_on_argb32_lsx;
6888
6889 extern void qt_blend_rgb32_on_rgb32_lsx(uchar *destPixels, int dbpl,
6890 const uchar *srcPixels, int sbpl,
6891 int w, int h,
6892 int const_alpha);
6893
6894 extern void qt_blend_argb32_on_argb32_lsx(uchar *destPixels, int dbpl,
6895 const uchar *srcPixels, int sbpl,
6896 int w, int h,
6897 int const_alpha);
6898
6899 qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_lsx;
6900 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_lsx;
6901 qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_lsx;
6902 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_lsx;
6903 qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_lsx;
6904 qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_lsx;
6905 qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_lsx;
6906 qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_lsx;
6907
6908 extern const uint * QT_FASTCALL qt_fetch_radial_gradient_lsx(uint *buffer, const Operator *op, const QSpanData *data,
6909 int y, int x, int length);
6910
6911 qt_fetch_radial_gradient = qt_fetch_radial_gradient_lsx;
6912
6913 extern void QT_FASTCALL comp_func_SourceOver_lsx(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
6914 extern void QT_FASTCALL comp_func_solid_SourceOver_lsx(uint *destPixels, int length, uint color, uint const_alpha);
6915 extern void QT_FASTCALL comp_func_Source_lsx(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
6916 extern void QT_FASTCALL comp_func_solid_Source_lsx(uint *destPixels, int length, uint color, uint const_alpha);
6917 extern void QT_FASTCALL comp_func_Plus_lsx(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
6918 qt_functionForMode_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_lsx;
6919 qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_lsx;
6920 qt_functionForMode_C[QPainter::CompositionMode_Source] = comp_func_Source_lsx;
6921 qt_functionForModeSolid_C[QPainter::CompositionMode_Source] = comp_func_solid_Source_lsx;
6922 qt_functionForMode_C[QPainter::CompositionMode_Plus] = comp_func_Plus_lsx;
6923
6924 extern const uint * QT_FASTCALL qt_fetchUntransformed_888_lsx(uint *buffer, const Operator *, const QSpanData *data,
6925 int y, int x, int length);
6926 sourceFetchUntransformed[QImage::Format_RGB888] = qt_fetchUntransformed_888_lsx;
6927 extern void QT_FASTCALL rbSwap_888_lsx(uchar *dst, const uchar *src, int count);
6928 qPixelLayouts[QImage::Format_RGB888].rbSwap = rbSwap_888_lsx;
6929 qPixelLayouts[QImage::Format_BGR888].rbSwap = rbSwap_888_lsx;
6930
6931 extern void QT_FASTCALL convertARGB32ToARGB32PM_lsx(uint *buffer, int count, const QList<QRgb> *);
6932 extern void QT_FASTCALL convertRGBA8888ToARGB32PM_lsx(uint *buffer, int count, const QList<QRgb> *);
6933 extern const uint *QT_FASTCALL fetchARGB32ToARGB32PM_lsx(uint *buffer, const uchar *src, int index, int count,
6934 const QList<QRgb> *, QDitherInfo *);
6935 extern const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_lsx(uint *buffer, const uchar *src, int index, int count,
6936 const QList<QRgb> *, QDitherInfo *);
6937 extern const QRgba64 * QT_FASTCALL convertARGB32ToRGBA64PM_lsx(QRgba64 *buffer, const uint *src, int count,
6938 const QList<QRgb> *, QDitherInfo *);
6939 extern const QRgba64 * QT_FASTCALL convertRGBA8888ToRGBA64PM_lsx(QRgba64 *buffer, const uint *src, int count,
6940 const QList<QRgb> *, QDitherInfo *);
6941 extern const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM_lsx(QRgba64 *buffer, const uchar *src, int index, int count,
6942 const QList<QRgb> *, QDitherInfo *);
6943 extern const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM_lsx(QRgba64 *buffer, const uchar *src, int index, int count,
6944 const QList<QRgb> *, QDitherInfo *);
6945 extern void QT_FASTCALL storeARGB32FromARGB32PM_lsx(uchar *dest, const uint *src, int index, int count,
6946 const QList<QRgb> *, QDitherInfo *);
6947 extern void QT_FASTCALL storeRGBA8888FromARGB32PM_lsx(uchar *dest, const uint *src, int index, int count,
6948 const QList<QRgb> *, QDitherInfo *);
6949 extern void QT_FASTCALL storeRGBXFromARGB32PM_lsx(uchar *dest, const uint *src, int index, int count,
6950 const QList<QRgb> *, QDitherInfo *);
6951 extern void QT_FASTCALL storeARGB32FromRGBA64PM_lsx(uchar *dest, const QRgba64 *src, int index, int count,
6952 const QList<QRgb> *, QDitherInfo *);
6953 extern void QT_FASTCALL storeRGBA8888FromRGBA64PM_lsx(uchar *dest, const QRgba64 *src, int index, int count,
6954 const QList<QRgb> *, QDitherInfo *);
6955 extern void QT_FASTCALL destStore64ARGB32_lsx(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length);
6956 extern void QT_FASTCALL destStore64RGBA8888_lsx(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length);
6957 extern void QT_FASTCALL storeRGBA64FromRGBA64PM_lsx(uchar *, const QRgba64 *, int, int, const QList<QRgb> *, QDitherInfo *);
6958 extern void QT_FASTCALL storeRGBx64FromRGBA64PM_lsx(uchar *, const QRgba64 *, int, int, const QList<QRgb> *, QDitherInfo *);
6959 qPixelLayouts[QImage::Format_ARGB32].fetchToARGB32PM = fetchARGB32ToARGB32PM_lsx;
6960 qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_lsx;
6961 qPixelLayouts[QImage::Format_RGBA8888].fetchToARGB32PM = fetchRGBA8888ToARGB32PM_lsx;
6962 qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_lsx;
6963 qPixelLayouts[QImage::Format_ARGB32].fetchToRGBA64PM = fetchARGB32ToRGBA64PM_lsx;
6964 qPixelLayouts[QImage::Format_ARGB32].convertToRGBA64PM = convertARGB32ToRGBA64PM_lsx;
6965 qPixelLayouts[QImage::Format_RGBA8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_lsx;
6966 qPixelLayouts[QImage::Format_RGBA8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_lsx;
6967 qPixelLayouts[QImage::Format_RGBX8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_lsx;
6968 qPixelLayouts[QImage::Format_RGBX8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_lsx;
6969 qPixelLayouts[QImage::Format_ARGB32].storeFromARGB32PM = storeARGB32FromARGB32PM_lsx;
6970 qPixelLayouts[QImage::Format_RGBA8888].storeFromARGB32PM = storeRGBA8888FromARGB32PM_lsx;
6971 qPixelLayouts[QImage::Format_RGBX8888].storeFromARGB32PM = storeRGBXFromARGB32PM_lsx;
6972 qPixelLayouts[QImage::Format_A2BGR30_Premultiplied].storeFromARGB32PM = storeA2RGB30PMFromARGB32PM_lsx<PixelOrderBGR>;
6973 qPixelLayouts[QImage::Format_A2RGB30_Premultiplied].storeFromARGB32PM = storeA2RGB30PMFromARGB32PM_lsx<PixelOrderRGB>;
6974 qStoreFromRGBA64PM[QImage::Format_ARGB32] = storeARGB32FromRGBA64PM_lsx;
6975 qStoreFromRGBA64PM[QImage::Format_RGBA8888] = storeRGBA8888FromRGBA64PM_lsx;
6976 qStoreFromRGBA64PM[QImage::Format_RGBX64] = storeRGBx64FromRGBA64PM_lsx;
6977 qStoreFromRGBA64PM[QImage::Format_RGBA64] = storeRGBA64FromRGBA64PM_lsx;
6978#if QT_CONFIG(raster_64bit)
6979 destStoreProc64[QImage::Format_ARGB32] = destStore64ARGB32_lsx;
6980 destStoreProc64[QImage::Format_RGBA8888] = destStore64RGBA8888_lsx;
6981#endif
6982#if QT_CONFIG(raster_fp)
6983 extern const QRgbaFloat32 *QT_FASTCALL fetchRGBA32FToRGBA32F_lsx(QRgbaFloat32 *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6984 extern void QT_FASTCALL storeRGBX32FFromRGBA32F_lsx(uchar *dest, const QRgbaFloat32 *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6985 extern void QT_FASTCALL storeRGBA32FFromRGBA32F_lsx(uchar *dest, const QRgbaFloat32 *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6986 qFetchToRGBA32F[QImage::Format_RGBA32FPx4] = fetchRGBA32FToRGBA32F_lsx;
6987 qStoreFromRGBA32F[QImage::Format_RGBX32FPx4] = storeRGBX32FFromRGBA32F_lsx;
6988 qStoreFromRGBA32F[QImage::Format_RGBA32FPx4] = storeRGBA32FFromRGBA32F_lsx;
6989#endif // QT_CONFIG(raster_fp)
6990 }
6991
6992#if defined(QT_COMPILER_SUPPORTS_LASX)
6993 if (qCpuHasFeature(LASX)) {
6994 qt_memfill32 = qt_memfill32_lasx;
6995 qt_memfill64 = qt_memfill64_lasx;
6996
6997 extern void qt_blend_rgb32_on_rgb32_lasx(uchar *destPixels, int dbpl,
6998 const uchar *srcPixels, int sbpl,
6999 int w, int h, int const_alpha);
7000 extern void qt_blend_argb32_on_argb32_lasx(uchar *destPixels, int dbpl,
7001 const uchar *srcPixels, int sbpl,
7002 int w, int h, int const_alpha);
7003 qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_lasx;
7004 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_lasx;
7005 qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_lasx;
7006 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_lasx;
7007 qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_lasx;
7008 qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_lasx;
7009 qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_lasx;
7010 qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_lasx;
7011
7012 extern void QT_FASTCALL comp_func_Source_lasx(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
7013 extern void QT_FASTCALL comp_func_SourceOver_lasx(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
7014 extern void QT_FASTCALL comp_func_solid_SourceOver_lasx(uint *destPixels, int length, uint color, uint const_alpha);
7015 qt_functionForMode_C[QPainter::CompositionMode_Source] = comp_func_Source_lasx;
7016 qt_functionForMode_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_lasx;
7017 qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_lasx;
7018#if QT_CONFIG(raster_64bit)
7019 extern void QT_FASTCALL comp_func_Source_rgb64_lasx(QRgba64 *destPixels, const QRgba64 *srcPixels, int length, uint const_alpha);
7020 extern void QT_FASTCALL comp_func_solid_SourceOver_rgb64_lasx(QRgba64 *destPixels, int length, QRgba64 color, uint const_alpha);
7021 qt_functionForMode64_C[QPainter::CompositionMode_Source] = comp_func_Source_rgb64_lasx;
7022 qt_functionForModeSolid64_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_rgb64_lasx;
7023#endif
7024
7025 extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_scale_helper_lasx(uint *b, uint *end, const QTextureData &image,
7026 int &fx, int &fy, int fdx, int /*fdy*/);
7027 extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_downscale_helper_lasx(uint *b, uint *end, const QTextureData &image,
7028 int &fx, int &fy, int fdx, int /*fdy*/);
7029 extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_fast_rotate_helper_lasx(uint *b, uint *end, const QTextureData &image,
7030 int &fx, int &fy, int fdx, int fdy);
7031
7032 bilinearFastTransformHelperARGB32PM[0][SimpleScaleTransform] = fetchTransformedBilinearARGB32PM_simple_scale_helper_lasx;
7033 bilinearFastTransformHelperARGB32PM[0][DownscaleTransform] = fetchTransformedBilinearARGB32PM_downscale_helper_lasx;
7034 bilinearFastTransformHelperARGB32PM[0][FastRotateTransform] = fetchTransformedBilinearARGB32PM_fast_rotate_helper_lasx;
7035
7036 extern void QT_FASTCALL convertARGB32ToARGB32PM_lasx(uint *buffer, int count, const QList<QRgb> *);
7037 extern void QT_FASTCALL convertRGBA8888ToARGB32PM_lasx(uint *buffer, int count, const QList<QRgb> *);
7038 extern const uint *QT_FASTCALL fetchARGB32ToARGB32PM_lasx(uint *buffer, const uchar *src, int index, int count,
7039 const QList<QRgb> *, QDitherInfo *);
7040 extern const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_lasx(uint *buffer, const uchar *src, int index, int count,
7041 const QList<QRgb> *, QDitherInfo *);
7042 qPixelLayouts[QImage::Format_ARGB32].fetchToARGB32PM = fetchARGB32ToARGB32PM_lasx;
7043 qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_lasx;
7044 qPixelLayouts[QImage::Format_RGBA8888].fetchToARGB32PM = fetchRGBA8888ToARGB32PM_lasx;
7045 qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_lasx;
7046
7047 extern const QRgba64 * QT_FASTCALL convertARGB32ToRGBA64PM_lasx(QRgba64 *, const uint *, int, const QList<QRgb> *, QDitherInfo *);
7048 extern const QRgba64 * QT_FASTCALL convertRGBA8888ToRGBA64PM_lasx(QRgba64 *, const uint *, int count, const QList<QRgb> *, QDitherInfo *);
7049 extern const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM_lasx(QRgba64 *, const uchar *, int, int, const QList<QRgb> *, QDitherInfo *);
7050 extern const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM_lasx(QRgba64 *, const uchar *, int, int, const QList<QRgb> *, QDitherInfo *);
7051 qPixelLayouts[QImage::Format_ARGB32].convertToRGBA64PM = convertARGB32ToRGBA64PM_lasx;
7052 qPixelLayouts[QImage::Format_RGBX8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_lasx;
7053 qPixelLayouts[QImage::Format_ARGB32].fetchToRGBA64PM = fetchARGB32ToRGBA64PM_lasx;
7054 qPixelLayouts[QImage::Format_RGBX8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_lasx;
7055 }
7056#endif //QT_COMPILER_SUPPORTS_LASX
7057
7058#endif //QT_COMPILER_SUPPORTS_LSX
7059
7060#if defined(__ARM_NEON__)
7061 qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_neon;
7062 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_neon;
7063 qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_neon;
7064 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_neon;
7065#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
7066 qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_neon;
7067 qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_neon;
7068 qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_neon;
7069 qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_neon;
7070#endif
7071
7072 qt_functionForMode_C[QPainter::CompositionMode_SourceOver] = qt_blend_argb32_on_argb32_scanline_neon;
7073 qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_neon;
7074 qt_functionForMode_C[QPainter::CompositionMode_Plus] = comp_func_Plus_neon;
7075
7076 extern const uint * QT_FASTCALL qt_fetch_radial_gradient_neon(uint *buffer, const Operator *op, const QSpanData *data,
7077 int y, int x, int length);
7078
7079 qt_fetch_radial_gradient = qt_fetch_radial_gradient_neon;
7080
7081 sourceFetchUntransformed[QImage::Format_RGB888] = qt_fetchUntransformed_888_neon;
7082
7083#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
7084 extern void QT_FASTCALL convertARGB32ToARGB32PM_neon(uint *buffer, int count, const QList<QRgb> *);
7085 extern void QT_FASTCALL convertRGBA8888ToARGB32PM_neon(uint *buffer, int count, const QList<QRgb> *);
7086 extern const uint *QT_FASTCALL fetchARGB32ToARGB32PM_neon(uint *buffer, const uchar *src, int index, int count,
7087 const QList<QRgb> *, QDitherInfo *);
7088 extern const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_neon(uint *buffer, const uchar *src, int index, int count,
7089 const QList<QRgb> *, QDitherInfo *);
7090 extern const QRgba64 * QT_FASTCALL convertARGB32ToRGBA64PM_neon(QRgba64 *buffer, const uint *src, int count,
7091 const QList<QRgb> *, QDitherInfo *);
7092 extern const QRgba64 * QT_FASTCALL convertRGBA8888ToRGBA64PM_neon(QRgba64 *buffer, const uint *src, int count,
7093 const QList<QRgb> *, QDitherInfo *);
7094 extern const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM_neon(QRgba64 *buffer, const uchar *src, int index, int count,
7095 const QList<QRgb> *, QDitherInfo *);
7096 extern const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM_neon(QRgba64 *buffer, const uchar *src, int index, int count,
7097 const QList<QRgb> *, QDitherInfo *);
7098 extern void QT_FASTCALL storeARGB32FromARGB32PM_neon(uchar *dest, const uint *src, int index, int count,
7099 const QList<QRgb> *, QDitherInfo *);
7100 extern void QT_FASTCALL storeRGBA8888FromARGB32PM_neon(uchar *dest, const uint *src, int index, int count,
7101 const QList<QRgb> *, QDitherInfo *);
7102 extern void QT_FASTCALL storeRGBXFromARGB32PM_neon(uchar *dest, const uint *src, int index, int count,
7103 const QList<QRgb> *, QDitherInfo *);
7104 qPixelLayouts[QImage::Format_ARGB32].fetchToARGB32PM = fetchARGB32ToARGB32PM_neon;
7105 qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_neon;
7106 qPixelLayouts[QImage::Format_ARGB32].storeFromARGB32PM = storeARGB32FromARGB32PM_neon;
7107 qPixelLayouts[QImage::Format_ARGB32].fetchToRGBA64PM = fetchARGB32ToRGBA64PM_neon;
7108 qPixelLayouts[QImage::Format_ARGB32].convertToRGBA64PM = convertARGB32ToRGBA64PM_neon;
7109 qPixelLayouts[QImage::Format_RGBA8888].fetchToARGB32PM = fetchRGBA8888ToARGB32PM_neon;
7110 qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_neon;
7111 qPixelLayouts[QImage::Format_RGBA8888].storeFromARGB32PM = storeRGBA8888FromARGB32PM_neon;
7112 qPixelLayouts[QImage::Format_RGBA8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_neon;
7113 qPixelLayouts[QImage::Format_RGBA8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_neon;
7114 qPixelLayouts[QImage::Format_RGBX8888].storeFromARGB32PM = storeRGBXFromARGB32PM_neon;
7115 qPixelLayouts[QImage::Format_RGBX8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_neon;
7116 qPixelLayouts[QImage::Format_RGBX8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_neon;
7117#endif
7118
7119#if defined(ENABLE_PIXMAN_DRAWHELPERS)
7120 // The RGB16 helpers are using Arm32 assemblythat has not been ported to AArch64
7121 qBlendFunctions[QImage::Format_RGB16][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_rgb16_neon;
7122 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB16] = qt_blend_rgb16_on_argb32_neon;
7123 qBlendFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_blend_rgb16_on_rgb16_neon;
7124
7125 qScaleFunctions[QImage::Format_RGB16][QImage::Format_ARGB32_Premultiplied] = qt_scale_image_argb32_on_rgb16_neon;
7126 qScaleFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_scale_image_rgb16_on_rgb16_neon;
7127
7128 qTransformFunctions[QImage::Format_RGB16][QImage::Format_ARGB32_Premultiplied] = qt_transform_image_argb32_on_rgb16_neon;
7129 qTransformFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_transform_image_rgb16_on_rgb16_neon;
7130
7131 qDrawHelper[QImage::Format_RGB16].alphamapBlit = qt_alphamapblit_quint16_neon;
7132
7133 destFetchProc[QImage::Format_RGB16] = qt_destFetchRGB16_neon;
7134 destStoreProc[QImage::Format_RGB16] = qt_destStoreRGB16_neon;
7135
7136 qMemRotateFunctions[QPixelLayout::BPP16][0] = qt_memrotate90_16_neon;
7137 qMemRotateFunctions[QPixelLayout::BPP16][2] = qt_memrotate270_16_neon;
7138#endif
7139#endif // defined(__ARM_NEON__)
7140
7141#if defined(__MIPS_DSP__)
7142 // Composition functions are all DSP r1
7143 qt_functionForMode_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_asm_mips_dsp;
7144 qt_functionForMode_C[QPainter::CompositionMode_Source] = comp_func_Source_mips_dsp;
7145 qt_functionForMode_C[QPainter::CompositionMode_DestinationOver] = comp_func_DestinationOver_mips_dsp;
7146 qt_functionForMode_C[QPainter::CompositionMode_SourceIn] = comp_func_SourceIn_mips_dsp;
7147 qt_functionForMode_C[QPainter::CompositionMode_DestinationIn] = comp_func_DestinationIn_mips_dsp;
7148 qt_functionForMode_C[QPainter::CompositionMode_DestinationOut] = comp_func_DestinationOut_mips_dsp;
7149 qt_functionForMode_C[QPainter::CompositionMode_SourceAtop] = comp_func_SourceAtop_mips_dsp;
7150 qt_functionForMode_C[QPainter::CompositionMode_DestinationAtop] = comp_func_DestinationAtop_mips_dsp;
7151 qt_functionForMode_C[QPainter::CompositionMode_Xor] = comp_func_XOR_mips_dsp;
7152 qt_functionForMode_C[QPainter::CompositionMode_SourceOut] = comp_func_SourceOut_mips_dsp;
7153
7154 qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_mips_dsp;
7155 qt_functionForModeSolid_C[QPainter::CompositionMode_DestinationOver] = comp_func_solid_DestinationOver_mips_dsp;
7156 qt_functionForModeSolid_C[QPainter::CompositionMode_SourceIn] = comp_func_solid_SourceIn_mips_dsp;
7157 qt_functionForModeSolid_C[QPainter::CompositionMode_DestinationIn] = comp_func_solid_DestinationIn_mips_dsp;
7158 qt_functionForModeSolid_C[QPainter::CompositionMode_SourceAtop] = comp_func_solid_SourceAtop_mips_dsp;
7159 qt_functionForModeSolid_C[QPainter::CompositionMode_DestinationAtop] = comp_func_solid_DestinationAtop_mips_dsp;
7160 qt_functionForModeSolid_C[QPainter::CompositionMode_Xor] = comp_func_solid_XOR_mips_dsp;
7161 qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOut] = comp_func_solid_SourceOut_mips_dsp;
7162
7163 qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_mips_dsp;
7164 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_mips_dsp;
7165 qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_mips_dsp;
7166 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_mips_dsp;
7167
7168 destFetchProc[QImage::Format_ARGB32] = qt_destFetchARGB32_mips_dsp;
7169
7170 destStoreProc[QImage::Format_ARGB32] = qt_destStoreARGB32_mips_dsp;
7171
7172 sourceFetchUntransformed[QImage::Format_RGB888] = qt_fetchUntransformed_888_mips_dsp;
7173 sourceFetchUntransformed[QImage::Format_RGB444] = qt_fetchUntransformed_444_mips_dsp;
7174 sourceFetchUntransformed[QImage::Format_ARGB8565_Premultiplied] = qt_fetchUntransformed_argb8565_premultiplied_mips_dsp;
7175
7176#if defined(__MIPS_DSPR2__)
7177 qBlendFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_blend_rgb16_on_rgb16_mips_dspr2;
7178 sourceFetchUntransformed[QImage::Format_RGB16] = qt_fetchUntransformedRGB16_mips_dspr2;
7179#else
7180 qBlendFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_blend_rgb16_on_rgb16_mips_dsp;
7181#endif // defined(__MIPS_DSPR2__)
7182#endif // defined(__MIPS_DSP__)
7183}
7184
7185// Ensure initialization if this object file is linked.
7186Q_CONSTRUCTOR_FUNCTION(qInitDrawhelperFunctions);
7187
7188QT_END_NAMESPACE
7189

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