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