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