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 */
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
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; }
3438 {
3439 Q_ASSERT(std::isfinite(v));
3440 return qt_gradient_pixel(&gradient, v);
3441 }
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// Handing a fill to the thread pool only pays off when the spans cover enough
3973// pixels, and the limit is higher for solid fills than gradient/texture blends
3974static constexpr int MinParallelSolidPixels = 1 << 17;
3975static constexpr int MinParallelBlendPixels = 1 << 15;
3976
3977// Estimates whether the spans cover at least _limit_ pixels
3978static inline bool qt_pixelCountAtLeast(int count, const QT_FT_Span *spans, int limit)
3979{
3980 constexpr int MaxProbeSpans = 32;
3981 const int numProbeSpans = qMin(count, MaxProbeSpans);
3982 qint64 numPixels = 0;
3983 for (int i = 0; i < numProbeSpans; ++i)
3984 numPixels += spans[i].len;
3985 return numPixels * count >= qint64(limit) * numProbeSpans;
3986}
3987
3988// qtGuiThreadPool() locks mutex, so only call it if the fill is worth splitting
3989#define QT_THREAD_PARALLEL_FILLS(function, minPixels)
3990 const int segments = (count + 32) / 64;
3991 QThreadPool *threadPool = (segments > 1
3992 && qPixelLayouts[data->rasterBuffer->format].bpp >= QPixelLayout::BPP8
3993 && qt_pixelCountAtLeast(count, spans, minPixels))
3994 ? QGuiApplicationPrivate::qtGuiThreadPool() : nullptr;
3995 if (threadPool && !threadPool->contains(QThread::currentThread())) {
3996 QLatch latch(segments);
3997 int c = 0;
3998 for (int i = 0; i < segments; ++i) {
3999 int cn = (count - c) / (segments - i);
4000 threadPool->start([&, c, cn]() {
4001 function(c, c + cn);
4002 latch.countDown();
4003 }, 1);
4004 c += cn;
4005 }
4006 latch.wait();
4007 } else
4008 function(0, count)
4009#else
4010#define QT_THREAD_PARALLEL_FILLS(function, minPixels) function(0, count)
4011#endif
4012
4013static void blend_color_generic(int count, const QT_FT_Span *spans, void *userData)
4014{
4015 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4016 const Operator op = getOperator(data, nullptr, 0);
4017 const uint color = data->solidColor.rgba();
4018 const bool solidFill = op.mode == QPainter::CompositionMode_Source;
4019 const QPixelLayout::BPP bpp = qPixelLayouts[data->rasterBuffer->format].bpp;
4020
4021 auto function = [=] (int cStart, int cEnd) {
4022 alignas(16) Q_DECL_UNINITIALIZED uint buffer[BufferSize];
4023 for (int c = cStart; c < cEnd; ++c) {
4024 int x = spans[c].x;
4025 int length = spans[c].len;
4026 if (solidFill && bpp >= QPixelLayout::BPP8 && spans[c].coverage == 255 && length && op.destStore) {
4027 // If dest doesn't matter we don't need to bother with blending or converting all the identical pixels
4028 op.destStore(data->rasterBuffer, x, spans[c].y, &color, 1);
4029 spanfill_from_first(data->rasterBuffer, bpp, x, spans[c].y, length);
4030 length = 0;
4031 }
4032
4033 while (length) {
4034 int l = qMin(BufferSize, length);
4035 uint *dest = op.destFetch(buffer, data->rasterBuffer, x, spans[c].y, l);
4036 op.funcSolid(dest, l, color, spans[c].coverage);
4037 if (op.destStore)
4038 op.destStore(data->rasterBuffer, x, spans[c].y, dest, l);
4039 length -= l;
4040 x += l;
4041 }
4042 }
4043 };
4044 QT_THREAD_PARALLEL_FILLS(function, MinParallelSolidPixels);
4045}
4046
4047static void blend_color_argb(int count, const QT_FT_Span *spans, void *userData)
4048{
4049 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4050
4051 const Operator op = getOperator(data, nullptr, 0);
4052 const uint color = data->solidColor.rgba();
4053
4054 if (op.mode == QPainter::CompositionMode_Source) {
4055 // inline for performance
4056 while (count--) {
4057 uint *target = ((uint *)data->rasterBuffer->scanLine(spans->y)) + spans->x;
4058 if (spans->coverage == 255) {
4059 qt_memfill(target, color, spans->len);
4060#ifdef __SSE2__
4061 } else if (spans->len > 16) {
4062 op.funcSolid(target, spans->len, color, spans->coverage);
4063#endif
4064 } else {
4065 uint c = BYTE_MUL(color, spans->coverage);
4066 int ialpha = 255 - spans->coverage;
4067 for (int i = 0; i < spans->len; ++i)
4068 target[i] = c + BYTE_MUL(target[i], ialpha);
4069 }
4070 ++spans;
4071 }
4072 return;
4073 }
4074 const auto funcSolid = op.funcSolid;
4075 auto function = [=] (int cStart, int cEnd) {
4076 for (int c = cStart; c < cEnd; ++c) {
4077 uint *target = ((uint *)data->rasterBuffer->scanLine(spans[c].y)) + spans[c].x;
4078 funcSolid(target, spans[c].len, color, spans[c].coverage);
4079 }
4080 };
4081 QT_THREAD_PARALLEL_FILLS(function, MinParallelSolidPixels);
4082}
4083
4084static void blend_color_generic_rgb64(int count, const QT_FT_Span *spans, void *userData)
4085{
4086#if QT_CONFIG(raster_64bit)
4087 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4088 const Operator op = getOperator(data, nullptr, 0);
4089 if (!op.funcSolid64) {
4090 qCDebug(lcQtGuiDrawHelper, "blend_color_generic_rgb64: unsupported 64bit blend attempted, falling back to 32-bit");
4091 return blend_color_generic(count, spans, userData);
4092 }
4093
4094 const QRgba64 color = data->solidColor.rgba64();
4095 const bool solidFill = op.mode == QPainter::CompositionMode_Source;
4096 const QPixelLayout::BPP bpp = qPixelLayouts[data->rasterBuffer->format].bpp;
4097
4098 auto function = [=, &op] (int cStart, int cEnd)
4099 {
4100 alignas(16) Q_DECL_UNINITIALIZED QRgba64 buffer[BufferSize];
4101 for (int c = cStart; c < cEnd; ++c) {
4102 int x = spans[c].x;
4103 int length = spans[c].len;
4104 if (solidFill && bpp >= QPixelLayout::BPP8 && spans[c].coverage == 255 && length && op.destStore64) {
4105 // If dest doesn't matter we don't need to bother with blending or converting all the identical pixels
4106 op.destStore64(data->rasterBuffer, x, spans[c].y, &color, 1);
4107 spanfill_from_first(data->rasterBuffer, bpp, x, spans[c].y, length);
4108 length = 0;
4109 }
4110
4111 while (length) {
4112 int l = qMin(BufferSize, length);
4113 QRgba64 *dest = op.destFetch64(buffer, data->rasterBuffer, x, spans[c].y, l);
4114 op.funcSolid64(dest, l, color, spans[c].coverage);
4115 if (op.destStore64)
4116 op.destStore64(data->rasterBuffer, x, spans[c].y, dest, l);
4117 length -= l;
4118 x += l;
4119 }
4120 }
4121 };
4122 QT_THREAD_PARALLEL_FILLS(function, MinParallelSolidPixels);
4123#else
4124 blend_color_generic(count, spans, userData);
4125#endif
4126}
4127
4128static void blend_color_generic_fp(int count, const QT_FT_Span *spans, void *userData)
4129{
4130#if QT_CONFIG(raster_fp)
4131 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4132 const Operator op = getOperator(data, nullptr, 0);
4133 if (!op.funcSolidFP || !op.destFetchFP) {
4134 qCDebug(lcQtGuiDrawHelper, "blend_color_generic_fp: unsupported 4xF16 blend attempted, falling back to 32-bit");
4135 return blend_color_generic(count, spans, userData);
4136 }
4137
4138 float r, g, b, a;
4139 data->solidColor.getRgbF(&r, &g, &b, &a);
4140 const QRgbaFloat32 color{r, g, b, a};
4141 const bool solidFill = op.mode == QPainter::CompositionMode_Source;
4142 QPixelLayout::BPP bpp = qPixelLayouts[data->rasterBuffer->format].bpp;
4143
4144 auto function = [=, &op] (int cStart, int cEnd)
4145 {
4146 alignas(16) Q_DECL_UNINITIALIZED QRgbaFloat32 buffer[BufferSize];
4147 for (int c = cStart; c < cEnd; ++c) {
4148 int x = spans[c].x;
4149 int length = spans[c].len;
4150 if (solidFill && bpp >= QPixelLayout::BPP8 && spans[c].coverage == 255 && length && op.destStoreFP) {
4151 // If dest doesn't matter we don't need to bother with blending or converting all the identical pixels
4152 op.destStoreFP(data->rasterBuffer, x, spans[c].y, &color, 1);
4153 spanfill_from_first(data->rasterBuffer, bpp, x, spans[c].y, length);
4154 length = 0;
4155 }
4156
4157 while (length) {
4158 int l = qMin(BufferSize, length);
4159 QRgbaFloat32 *dest = op.destFetchFP(buffer, data->rasterBuffer, x, spans[c].y, l);
4160 op.funcSolidFP(dest, l, color, spans[c].coverage);
4161 if (op.destStoreFP)
4162 op.destStoreFP(data->rasterBuffer, x, spans[c].y, dest, l);
4163 length -= l;
4164 x += l;
4165 }
4166 }
4167 };
4168 QT_THREAD_PARALLEL_FILLS(function, MinParallelSolidPixels);
4169#else
4170 blend_color_generic_rgb64(count, spans, userData);
4171#endif
4172}
4173
4174template <typename T>
4175void handleSpans(int count, const QT_FT_Span *spans, const QSpanData *data, const Operator &op)
4176{
4177 const int const_alpha = (data->type == QSpanData::Texture) ? data->texture.const_alpha : 256;
4178 const bool solidSource = op.mode == QPainter::CompositionMode_Source && const_alpha == 256;
4179
4180 auto function = [=, &op] (int cStart, int cEnd)
4181 {
4182 T Q_DECL_UNINITIALIZED handler(data, op);
4183 int coverage = 0;
4184 for (int c = cStart; c < cEnd;) {
4185 if (!spans[c].len) {
4186 ++c;
4187 continue;
4188 }
4189 int x = spans[c].x;
4190 const int y = spans[c].y;
4191 int right = x + spans[c].len;
4192 const bool fetchDest = !solidSource || spans[c].coverage < 255;
4193
4194 // compute length of adjacent spans
4195 for (int i = c + 1; i < cEnd && spans[i].y == y && spans[i].x == right && fetchDest == (!solidSource || spans[i].coverage < 255); ++i)
4196 right += spans[i].len;
4197 int length = right - x;
4198
4199 while (length) {
4200 int l = qMin(BufferSize, length);
4201 length -= l;
4202
4203 int process_length = l;
4204 int process_x = x;
4205
4206 const auto *src = handler.fetch(process_x, y, process_length, fetchDest);
4207 int offset = 0;
4208 while (l > 0) {
4209 if (x == spans[c].x) // new span?
4210 coverage = (spans[c].coverage * const_alpha) >> 8;
4211
4212 int right = spans[c].x + spans[c].len;
4213 int len = qMin(l, right - x);
4214
4215 handler.process(x, y, len, coverage, src, offset);
4216
4217 l -= len;
4218 x += len;
4219 offset += len;
4220
4221 if (x == right) // done with current span?
4222 ++c;
4223 }
4224 handler.store(process_x, y, process_length);
4225 }
4226 }
4227 };
4228 QT_THREAD_PARALLEL_FILLS(function, MinParallelBlendPixels);
4229}
4230
4232{
4234 const Operator &op;
4235};
4236
4238{
4239public:
4240 uint *dest = nullptr;
4241 alignas(16) uint buffer[BufferSize];
4244 : QBlendBase{d, o}
4245 {
4246 }
4247
4248 const uint *fetch(int x, int y, int len, bool fetchDest)
4249 {
4250 if (fetchDest || op.destFetch == destFetchARGB32P)
4251 dest = op.destFetch(buffer, data->rasterBuffer, x, y, len);
4252 else
4253 dest = buffer;
4254 return op.srcFetch(src_buffer, &op, data, y, x, len);
4255 }
4256
4257 void process(int, int, int len, int coverage, const uint *src, int offset)
4258 {
4259 op.func(dest + offset, src + offset, len, coverage);
4260 }
4261
4262 void store(int x, int y, int len)
4263 {
4264 if (op.destStore)
4265 op.destStore(data->rasterBuffer, x, y, dest, len);
4266 }
4267};
4268
4269#if QT_CONFIG(raster_64bit)
4270class BlendSrcGenericRGB64 : public QBlendBase
4271{
4272public:
4273 QRgba64 *dest = nullptr;
4274 alignas(16) QRgba64 buffer[BufferSize];
4275 alignas(16) QRgba64 src_buffer[BufferSize];
4276 BlendSrcGenericRGB64(const QSpanData *d, const Operator &o)
4277 : QBlendBase{d, o}
4278 {
4279 }
4280
4281 bool isSupported() const
4282 {
4283 return op.func64 && op.destFetch64;
4284 }
4285
4286 const QRgba64 *fetch(int x, int y, int len, bool fetchDest)
4287 {
4288 if (fetchDest || op.destFetch64 == destFetchRGB64)
4289 dest = op.destFetch64(buffer, data->rasterBuffer, x, y, len);
4290 else
4291 dest = buffer;
4292 return op.srcFetch64(src_buffer, &op, data, y, x, len);
4293 }
4294
4295 void process(int, int, int len, int coverage, const QRgba64 *src, int offset)
4296 {
4297 op.func64(dest + offset, src + offset, len, coverage);
4298 }
4299
4300 void store(int x, int y, int len)
4301 {
4302 if (op.destStore64)
4303 op.destStore64(data->rasterBuffer, x, y, dest, len);
4304 }
4305};
4306#endif
4307
4308#if QT_CONFIG(raster_fp)
4309class BlendSrcGenericRGBFP : public QBlendBase
4310{
4311public:
4312 QRgbaFloat32 *dest = nullptr;
4313 alignas(16) QRgbaFloat32 buffer[BufferSize];
4314 alignas(16) QRgbaFloat32 src_buffer[BufferSize];
4315 BlendSrcGenericRGBFP(const QSpanData *d, const Operator &o)
4316 : QBlendBase{d, o}
4317 {
4318 }
4319
4320 bool isSupported() const
4321 {
4322 return op.funcFP && op.destFetchFP && op.srcFetchFP;
4323 }
4324
4325 const QRgbaFloat32 *fetch(int x, int y, int len, bool fetchDest)
4326 {
4327 if (fetchDest || op.destFetchFP == destFetchRGBFP)
4328 dest = op.destFetchFP(buffer, data->rasterBuffer, x, y, len);
4329 else
4330 dest = buffer;
4331 return op.srcFetchFP(src_buffer, &op, data, y, x, len);
4332 }
4333
4334 void process(int, int, int len, int coverage, const QRgbaFloat32 *src, int offset)
4335 {
4336 op.funcFP(dest + offset, src + offset, len, coverage);
4337 }
4338
4339 void store(int x, int y, int len)
4340 {
4341 if (op.destStoreFP)
4342 op.destStoreFP(data->rasterBuffer, x, y, dest, len);
4343 }
4344};
4345#endif
4346
4347static void blend_src_generic(int count, const QT_FT_Span *spans, void *userData)
4348{
4349 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4350 const Operator op = getOperator(data, nullptr, 0);
4351 handleSpans<BlendSrcGeneric>(count, spans, data, op);
4352}
4353
4354#if QT_CONFIG(raster_64bit)
4355static void blend_src_generic_rgb64(int count, const QT_FT_Span *spans, void *userData)
4356{
4357 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4358 const Operator op = getOperator(data, nullptr, 0);
4359 if (op.func64 && op.destFetch64) {
4360 handleSpans<BlendSrcGenericRGB64>(count, spans, data, op);
4361 } else {
4362 qCDebug(lcQtGuiDrawHelper, "blend_src_generic_rgb64: unsupported 64-bit blend attempted, falling back to 32-bit");
4363 handleSpans<BlendSrcGeneric>(count, spans, data, op);
4364 }
4365}
4366#endif
4367
4368#if QT_CONFIG(raster_fp)
4369static void blend_src_generic_fp(int count, const QT_FT_Span *spans, void *userData)
4370{
4371 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4372 const Operator op = getOperator(data, spans, count);
4373 if (op.funcFP && op.destFetchFP && op.srcFetchFP) {
4374 handleSpans<BlendSrcGenericRGBFP>(count, spans, data, op);
4375 } else {
4376 qCDebug(lcQtGuiDrawHelper, "blend_src_generic_fp: unsupported 4xFP blend attempted, falling back to 32-bit");
4377 handleSpans<BlendSrcGeneric>(count, spans, data, op);
4378 }
4379}
4380#endif
4381
4382static void blend_untransformed_generic(int count, const QT_FT_Span *spans, void *userData)
4383{
4384 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4385
4386 const Operator op = getOperator(data, spans, count);
4387
4388 const int image_width = data->texture.width;
4389 const int image_height = data->texture.height;
4390 const int const_alpha = data->texture.const_alpha;
4391 const int xoff = -qRound(-data->dx);
4392 const int yoff = -qRound(-data->dy);
4393 const bool solidSource = op.mode == QPainter::CompositionMode_Source && const_alpha == 256 && op.destFetch != destFetchARGB32P;
4394
4395 auto function = [=, &op] (int cStart, int cEnd)
4396 {
4397 alignas(16) Q_DECL_UNINITIALIZED uint buffer[BufferSize];
4398 alignas(16) Q_DECL_UNINITIALIZED uint src_buffer[BufferSize];
4399 for (int c = cStart; c < cEnd; ++c) {
4400 if (!spans[c].len)
4401 continue;
4402 int x = spans[c].x;
4403 int length = spans[c].len;
4404 int sx = xoff + x;
4405 int sy = yoff + spans[c].y;
4406 const bool fetchDest = !solidSource || spans[c].coverage < 255;
4407 if (sy >= 0 && sy < image_height && sx < image_width) {
4408 if (sx < 0) {
4409 x -= sx;
4410 length += sx;
4411 sx = 0;
4412 }
4413 if (sx + length > image_width)
4414 length = image_width - sx;
4415 if (length > 0) {
4416 const int coverage = (spans[c].coverage * const_alpha) >> 8;
4417 while (length) {
4418 int l = qMin(BufferSize, length);
4419 const uint *src = op.srcFetch(src_buffer, &op, data, sy, sx, l);
4420 uint *dest = fetchDest ? op.destFetch(buffer, data->rasterBuffer, x, spans[c].y, l) : buffer;
4421 op.func(dest, src, l, coverage);
4422 if (op.destStore)
4423 op.destStore(data->rasterBuffer, x, spans[c].y, dest, l);
4424 x += l;
4425 sx += l;
4426 length -= l;
4427 }
4428 }
4429 }
4430 }
4431 };
4432 QT_THREAD_PARALLEL_FILLS(function, MinParallelBlendPixels);
4433}
4434
4435#if QT_CONFIG(raster_64bit)
4436static void blend_untransformed_generic_rgb64(int count, const QT_FT_Span *spans, void *userData)
4437{
4438 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4439
4440 const Operator op = getOperator(data, spans, count);
4441 if (!op.func64) {
4442 qCDebug(lcQtGuiDrawHelper, "blend_untransformed_generic_rgb64: unsupported 64-bit blend attempted, falling back to 32-bit");
4443 return blend_untransformed_generic(count, spans, userData);
4444 }
4445
4446 const int image_width = data->texture.width;
4447 const int image_height = data->texture.height;
4448 const int const_alpha = data->texture.const_alpha;
4449 const int xoff = -qRound(-data->dx);
4450 const int yoff = -qRound(-data->dy);
4451 const bool solidSource = op.mode == QPainter::CompositionMode_Source && const_alpha == 256 && op.destFetch64 != destFetchRGB64;
4452
4453 auto function = [=, &op] (int cStart, int cEnd)
4454 {
4455 alignas(16) Q_DECL_UNINITIALIZED QRgba64 buffer[BufferSize];
4456 alignas(16) Q_DECL_UNINITIALIZED QRgba64 src_buffer[BufferSize];
4457 for (int c = cStart; c < cEnd; ++c) {
4458 if (!spans[c].len)
4459 continue;
4460 int x = spans[c].x;
4461 int length = spans[c].len;
4462 int sx = xoff + x;
4463 int sy = yoff + spans[c].y;
4464 const bool fetchDest = !solidSource || spans[c].coverage < 255;
4465 if (sy >= 0 && sy < image_height && sx < image_width) {
4466 if (sx < 0) {
4467 x -= sx;
4468 length += sx;
4469 sx = 0;
4470 }
4471 if (sx + length > image_width)
4472 length = image_width - sx;
4473 if (length > 0) {
4474 const int coverage = (spans[c].coverage * const_alpha) >> 8;
4475 while (length) {
4476 int l = qMin(BufferSize, length);
4477 const QRgba64 *src = op.srcFetch64(src_buffer, &op, data, sy, sx, l);
4478 QRgba64 *dest = fetchDest ? op.destFetch64(buffer, data->rasterBuffer, x, spans[c].y, l) : buffer;
4479 op.func64(dest, src, l, coverage);
4480 if (op.destStore64)
4481 op.destStore64(data->rasterBuffer, x, spans[c].y, dest, l);
4482 x += l;
4483 sx += l;
4484 length -= l;
4485 }
4486 }
4487 }
4488 }
4489 };
4490 QT_THREAD_PARALLEL_FILLS(function, MinParallelBlendPixels);
4491}
4492#endif
4493
4494#if QT_CONFIG(raster_fp)
4495static void blend_untransformed_generic_fp(int count, const QT_FT_Span *spans, void *userData)
4496{
4497 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4498
4499 const Operator op = getOperator(data, spans, count);
4500 if (!op.funcFP) {
4501 qCDebug(lcQtGuiDrawHelper, "blend_untransformed_generic_rgbaf16: unsupported 4xFP16 blend attempted, falling back to 32-bit");
4502 return blend_untransformed_generic(count, spans, userData);
4503 }
4504
4505 const int image_width = data->texture.width;
4506 const int image_height = data->texture.height;
4507 const int xoff = -qRound(-data->dx);
4508 const int yoff = -qRound(-data->dy);
4509 const bool solidSource = op.mode == QPainter::CompositionMode_Source && data->texture.const_alpha == 256 && op.destFetchFP != destFetchRGBFP;
4510
4511 auto function = [=, &op] (int cStart, int cEnd)
4512 {
4513 alignas(16) Q_DECL_UNINITIALIZED QRgbaFloat32 buffer[BufferSize];
4514 alignas(16) Q_DECL_UNINITIALIZED QRgbaFloat32 src_buffer[BufferSize];
4515 for (int c = cStart; c < cEnd; ++c) {
4516 if (!spans[c].len)
4517 continue;
4518 int x = spans[c].x;
4519 int length = spans[c].len;
4520 int sx = xoff + x;
4521 int sy = yoff + spans[c].y;
4522 const bool fetchDest = !solidSource || spans[c].coverage < 255;
4523 if (sy >= 0 && sy < image_height && sx < image_width) {
4524 if (sx < 0) {
4525 x -= sx;
4526 length += sx;
4527 sx = 0;
4528 }
4529 if (sx + length > image_width)
4530 length = image_width - sx;
4531 if (length > 0) {
4532 const int coverage = (spans[c].coverage * data->texture.const_alpha) >> 8;
4533 while (length) {
4534 int l = qMin(BufferSize, length);
4535 const QRgbaFloat32 *src = op.srcFetchFP(src_buffer, &op, data, sy, sx, l);
4536 QRgbaFloat32 *dest = fetchDest ? op.destFetchFP(buffer, data->rasterBuffer, x, spans[c].y, l) : buffer;
4537 op.funcFP(dest, src, l, coverage);
4538 if (op.destStoreFP)
4539 op.destStoreFP(data->rasterBuffer, x, spans[c].y, dest, l);
4540 x += l;
4541 sx += l;
4542 length -= l;
4543 }
4544 }
4545 }
4546 }
4547 };
4548 QT_THREAD_PARALLEL_FILLS(function, MinParallelBlendPixels);
4549}
4550#endif
4551
4552static void blend_untransformed_argb(int count, const QT_FT_Span *spans, void *userData)
4553{
4554 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4555 if (data->texture.format != QImage::Format_ARGB32_Premultiplied
4556 && data->texture.format != QImage::Format_RGB32) {
4557 blend_untransformed_generic(count, spans, userData);
4558 return;
4559 }
4560
4561 const Operator op = getOperator(data, spans, count);
4562
4563 const int image_width = data->texture.width;
4564 const int image_height = data->texture.height;
4565 const int const_alpha = data->texture.const_alpha;
4566 const int xoff = -qRound(-data->dx);
4567 const int yoff = -qRound(-data->dy);
4568
4569 auto function = [=, &op] (int cStart, int cEnd)
4570 {
4571 for (int c = cStart; c < cEnd; ++c) {
4572 if (!spans[c].len)
4573 continue;
4574 int x = spans[c].x;
4575 int length = spans[c].len;
4576 int sx = xoff + x;
4577 int sy = yoff + spans[c].y;
4578 if (sy >= 0 && sy < image_height && sx < image_width) {
4579 if (sx < 0) {
4580 x -= sx;
4581 length += sx;
4582 sx = 0;
4583 }
4584 if (sx + length > image_width)
4585 length = image_width - sx;
4586 if (length > 0) {
4587 const int coverage = (spans[c].coverage * const_alpha) >> 8;
4588 const uint *src = (const uint *)data->texture.scanLine(sy) + sx;
4589 uint *dest = ((uint *)data->rasterBuffer->scanLine(spans[c].y)) + x;
4590 op.func(dest, src, length, coverage);
4591 }
4592 }
4593 }
4594 };
4595 QT_THREAD_PARALLEL_FILLS(function, MinParallelBlendPixels);
4596}
4597
4598static inline quint16 interpolate_pixel_rgb16_255(quint16 x, quint8 a,
4599 quint16 y, quint8 b)
4600{
4601 quint16 t = ((((x & 0x07e0) * a) + ((y & 0x07e0) * b)) >> 5) & 0x07e0;
4602 t |= ((((x & 0xf81f) * a) + ((y & 0xf81f) * b)) >> 5) & 0xf81f;
4603
4604 return t;
4605}
4606
4607static inline quint32 interpolate_pixel_rgb16x2_255(quint32 x, quint8 a,
4608 quint32 y, quint8 b)
4609{
4610 uint t;
4611 t = ((((x & 0xf81f07e0) >> 5) * a) + (((y & 0xf81f07e0) >> 5) * b)) & 0xf81f07e0;
4612 t |= ((((x & 0x07e0f81f) * a) + ((y & 0x07e0f81f) * b)) >> 5) & 0x07e0f81f;
4613 return t;
4614}
4615
4616static inline void blend_sourceOver_rgb16_rgb16(quint16 *Q_DECL_RESTRICT dest,
4617 const quint16 *Q_DECL_RESTRICT src,
4618 int length,
4619 const quint8 alpha,
4620 const quint8 ialpha)
4621{
4622 const int dstAlign = ((quintptr)dest) & 0x3;
4623 if (dstAlign) {
4624 *dest = interpolate_pixel_rgb16_255(*src, alpha, *dest, ialpha);
4625 ++dest;
4626 ++src;
4627 --length;
4628 }
4629 const int srcAlign = ((quintptr)src) & 0x3;
4630 int length32 = length >> 1;
4631 if (length32 && srcAlign == 0) {
4632 while (length32--) {
4633 const quint32 *src32 = reinterpret_cast<const quint32*>(src);
4634 quint32 *dest32 = reinterpret_cast<quint32*>(dest);
4635 *dest32 = interpolate_pixel_rgb16x2_255(*src32, alpha,
4636 *dest32, ialpha);
4637 dest += 2;
4638 src += 2;
4639 }
4640 length &= 0x1;
4641 }
4642 while (length--) {
4643 *dest = interpolate_pixel_rgb16_255(*src, alpha, *dest, ialpha);
4644 ++dest;
4645 ++src;
4646 }
4647}
4648
4649static void blend_untransformed_rgb565(int count, const QT_FT_Span *spans, void *userData)
4650{
4651 QSpanData *data = reinterpret_cast<QSpanData*>(userData);
4652 QPainter::CompositionMode mode = data->rasterBuffer->compositionMode;
4653
4654 if (data->texture.format != QImage::Format_RGB16
4655 || (mode != QPainter::CompositionMode_SourceOver
4656 && mode != QPainter::CompositionMode_Source))
4657 {
4658 blend_untransformed_generic(count, spans, userData);
4659 return;
4660 }
4661
4662 const int image_width = data->texture.width;
4663 const int image_height = data->texture.height;
4664 int xoff = -qRound(-data->dx);
4665 int yoff = -qRound(-data->dy);
4666
4667 auto function = [=](int cStart, int cEnd)
4668 {
4669 for (int c = cStart; c < cEnd; ++c) {
4670 if (!spans[c].len)
4671 continue;
4672 const quint8 coverage = (data->texture.const_alpha * spans[c].coverage) >> 8;
4673 if (coverage == 0)
4674 continue;
4675
4676 int x = spans[c].x;
4677 int length = spans[c].len;
4678 int sx = xoff + x;
4679 int sy = yoff + spans[c].y;
4680 if (sy >= 0 && sy < image_height && sx < image_width) {
4681 if (sx < 0) {
4682 x -= sx;
4683 length += sx;
4684 sx = 0;
4685 }
4686 if (sx + length > image_width)
4687 length = image_width - sx;
4688 if (length > 0) {
4689 quint16 *dest = (quint16 *)data->rasterBuffer->scanLine(spans[c].y) + x;
4690 const quint16 *src = (const quint16 *)data->texture.scanLine(sy) + sx;
4691 if (coverage == 255) {
4692 memcpy(dest, src, length * sizeof(quint16));
4693 } else {
4694 const quint8 alpha = (coverage + 1) >> 3;
4695 const quint8 ialpha = 0x20 - alpha;
4696 if (alpha > 0)
4697 blend_sourceOver_rgb16_rgb16(dest, src, length, alpha, ialpha);
4698 }
4699 }
4700 }
4701 }
4702 };
4703 QT_THREAD_PARALLEL_FILLS(function, MinParallelBlendPixels);
4704}
4705
4706static void blend_tiled_generic(int count, const QT_FT_Span *spans, void *userData)
4707{
4708 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4709
4710 const Operator op = getOperator(data, spans, count);
4711
4712 const int image_width = data->texture.width;
4713 const int image_height = data->texture.height;
4714 const int const_alpha = data->texture.const_alpha;
4715 int xoff = -qRound(-data->dx) % image_width;
4716 int yoff = -qRound(-data->dy) % image_height;
4717
4718 if (xoff < 0)
4719 xoff += image_width;
4720 if (yoff < 0)
4721 yoff += image_height;
4722
4723 auto function = [=, &op](int cStart, int cEnd)
4724 {
4725 alignas(16) Q_DECL_UNINITIALIZED uint buffer[BufferSize];
4726 alignas(16) Q_DECL_UNINITIALIZED uint src_buffer[BufferSize];
4727 for (int c = cStart; c < cEnd; ++c) {
4728 int x = spans[c].x;
4729 int length = spans[c].len;
4730 int sx = (xoff + spans[c].x) % image_width;
4731 int sy = (spans[c].y + yoff) % image_height;
4732 if (sx < 0)
4733 sx += image_width;
4734 if (sy < 0)
4735 sy += image_height;
4736
4737 const int coverage = (spans[c].coverage * const_alpha) >> 8;
4738 while (length) {
4739 int l = qMin(image_width - sx, length);
4740 if (BufferSize < l)
4741 l = BufferSize;
4742 const uint *src = op.srcFetch(src_buffer, &op, data, sy, sx, l);
4743 uint *dest = op.destFetch(buffer, data->rasterBuffer, x, spans[c].y, l);
4744 op.func(dest, src, l, coverage);
4745 if (op.destStore)
4746 op.destStore(data->rasterBuffer, x, spans[c].y, dest, l);
4747 x += l;
4748 sx += l;
4749 length -= l;
4750 if (sx >= image_width)
4751 sx = 0;
4752 }
4753 }
4754 };
4755 QT_THREAD_PARALLEL_FILLS(function, MinParallelBlendPixels);
4756}
4757
4758#if QT_CONFIG(raster_64bit)
4759static void blend_tiled_generic_rgb64(int count, const QT_FT_Span *spans, void *userData)
4760{
4761 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4762
4763 const Operator op = getOperator(data, spans, count);
4764 if (!op.func64) {
4765 qCDebug(lcQtGuiDrawHelper, "blend_tiled_generic_rgb64: unsupported 64-bit blend attempted, falling back to 32-bit");
4766 return blend_tiled_generic(count, spans, userData);
4767 }
4768
4769 const int image_width = data->texture.width;
4770 const int image_height = data->texture.height;
4771 int xoff = -qRound(-data->dx) % image_width;
4772 int yoff = -qRound(-data->dy) % image_height;
4773
4774 if (xoff < 0)
4775 xoff += image_width;
4776 if (yoff < 0)
4777 yoff += image_height;
4778
4779 bool isBpp32 = qPixelLayouts[data->rasterBuffer->format].bpp == QPixelLayout::BPP32;
4780 bool isBpp64 = qPixelLayouts[data->rasterBuffer->format].bpp == QPixelLayout::BPP64;
4781 if (op.destFetch64 == destFetch64Undefined && image_width <= BufferSize && (isBpp32 || isBpp64)) {
4782 alignas(16) Q_DECL_UNINITIALIZED QRgba64 src_buffer[BufferSize];
4783 // If destination isn't blended into the result, we can do the tiling directly on destination pixels.
4784 while (count--) {
4785 int x = spans->x;
4786 int y = spans->y;
4787 int length = spans->len;
4788 int sx = (xoff + spans->x) % image_width;
4789 int sy = (spans->y + yoff) % image_height;
4790 if (sx < 0)
4791 sx += image_width;
4792 if (sy < 0)
4793 sy += image_height;
4794
4795 int sl = qMin(image_width, length);
4796 if (sx > 0 && sl > 0) {
4797 int l = qMin(image_width - sx, sl);
4798 const QRgba64 *src = op.srcFetch64(src_buffer, &op, data, sy, sx, l);
4799 op.destStore64(data->rasterBuffer, x, y, src, l);
4800 x += l;
4801 sx += l;
4802 sl -= l;
4803 if (sx >= image_width)
4804 sx = 0;
4805 }
4806 if (sl > 0) {
4807 Q_ASSERT(sx == 0);
4808 const QRgba64 *src = op.srcFetch64(src_buffer, &op, data, sy, sx, sl);
4809 op.destStore64(data->rasterBuffer, x, y, src, sl);
4810 x += sl;
4811 sx += sl;
4812 sl -= sl;
4813 if (sx >= image_width)
4814 sx = 0;
4815 }
4816 if (isBpp32) {
4817 uint *dest = reinterpret_cast<uint *>(data->rasterBuffer->scanLine(y)) + x - image_width;
4818 for (int i = image_width; i < length; ++i)
4819 dest[i] = dest[i - image_width];
4820 } else {
4821 quint64 *dest = reinterpret_cast<quint64 *>(data->rasterBuffer->scanLine(y)) + x - image_width;
4822 for (int i = image_width; i < length; ++i)
4823 dest[i] = dest[i - image_width];
4824 }
4825 ++spans;
4826 }
4827 return;
4828 }
4829
4830 auto function = [=, &op](int cStart, int cEnd)
4831 {
4832 alignas(16) Q_DECL_UNINITIALIZED QRgba64 buffer[BufferSize];
4833 alignas(16) Q_DECL_UNINITIALIZED QRgba64 src_buffer[BufferSize];
4834 for (int c = cStart; c < cEnd; ++c) {
4835 int x = spans[c].x;
4836 int length = spans[c].len;
4837 int sx = (xoff + spans[c].x) % image_width;
4838 int sy = (spans[c].y + yoff) % image_height;
4839 if (sx < 0)
4840 sx += image_width;
4841 if (sy < 0)
4842 sy += image_height;
4843
4844 const int coverage = (spans[c].coverage * data->texture.const_alpha) >> 8;
4845 while (length) {
4846 int l = qMin(image_width - sx, length);
4847 if (BufferSize < l)
4848 l = BufferSize;
4849 const QRgba64 *src = op.srcFetch64(src_buffer, &op, data, sy, sx, l);
4850 QRgba64 *dest = op.destFetch64(buffer, data->rasterBuffer, x, spans[c].y, l);
4851 op.func64(dest, src, l, coverage);
4852 if (op.destStore64)
4853 op.destStore64(data->rasterBuffer, x, spans[c].y, dest, l);
4854 x += l;
4855 sx += l;
4856 length -= l;
4857 if (sx >= image_width)
4858 sx = 0;
4859 }
4860 }
4861 };
4862 QT_THREAD_PARALLEL_FILLS(function, MinParallelBlendPixels);
4863}
4864#endif
4865
4866#if QT_CONFIG(raster_fp)
4867static void blend_tiled_generic_fp(int count, const QT_FT_Span *spans, void *userData)
4868{
4869 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4870
4871 const Operator op = getOperator(data, spans, count);
4872 if (!op.funcFP) {
4873 qCDebug(lcQtGuiDrawHelper, "blend_tiled_generic_fp: unsupported 4xFP blend attempted, falling back to 32-bit");
4874 return blend_tiled_generic(count, spans, userData);
4875 }
4876
4877 const int image_width = data->texture.width;
4878 const int image_height = data->texture.height;
4879 int xoff = -qRound(-data->dx) % image_width;
4880 int yoff = -qRound(-data->dy) % image_height;
4881
4882 if (xoff < 0)
4883 xoff += image_width;
4884 if (yoff < 0)
4885 yoff += image_height;
4886
4887 // Consider tiling optimizing like the other versions.
4888
4889 auto function = [=, &op](int cStart, int cEnd)
4890 {
4891 alignas(16) Q_DECL_UNINITIALIZED QRgbaFloat32 buffer[BufferSize];
4892 alignas(16) Q_DECL_UNINITIALIZED QRgbaFloat32 src_buffer[BufferSize];
4893 for (int c = cStart; c < cEnd; ++c) {
4894 int x = spans[c].x;
4895 int length = spans[c].len;
4896 int sx = (xoff + spans[c].x) % image_width;
4897 int sy = (spans[c].y + yoff) % image_height;
4898 if (sx < 0)
4899 sx += image_width;
4900 if (sy < 0)
4901 sy += image_height;
4902
4903 const int coverage = (spans[c].coverage * data->texture.const_alpha) >> 8;
4904 while (length) {
4905 int l = qMin(image_width - sx, length);
4906 if (BufferSize < l)
4907 l = BufferSize;
4908 const QRgbaFloat32 *src = op.srcFetchFP(src_buffer, &op, data, sy, sx, l);
4909 QRgbaFloat32 *dest = op.destFetchFP(buffer, data->rasterBuffer, x, spans[c].y, l);
4910 op.funcFP(dest, src, l, coverage);
4911 if (op.destStoreFP)
4912 op.destStoreFP(data->rasterBuffer, x, spans[c].y, dest, l);
4913 x += l;
4914 sx += l;
4915 length -= l;
4916 if (sx >= image_width)
4917 sx = 0;
4918 }
4919 }
4920 };
4921 QT_THREAD_PARALLEL_FILLS(function, MinParallelBlendPixels);
4922}
4923#endif
4924
4925static void blend_tiled_argb(int count, const QT_FT_Span *spans, void *userData)
4926{
4927 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4928 if (data->texture.format != QImage::Format_ARGB32_Premultiplied
4929 && data->texture.format != QImage::Format_RGB32) {
4930 blend_tiled_generic(count, spans, userData);
4931 return;
4932 }
4933
4934 const Operator op = getOperator(data, spans, count);
4935
4936 const int image_width = data->texture.width;
4937 const int image_height = data->texture.height;
4938 int xoff = -qRound(-data->dx) % image_width;
4939 int yoff = -qRound(-data->dy) % image_height;
4940
4941 if (xoff < 0)
4942 xoff += image_width;
4943 if (yoff < 0)
4944 yoff += image_height;
4945 const auto func = op.func;
4946 const int const_alpha = data->texture.const_alpha;
4947
4948 auto function = [=] (int cStart, int cEnd) {
4949 for (int c = cStart; c < cEnd; ++c) {
4950 int x = spans[c].x;
4951 int length = spans[c].len;
4952 int sx = (xoff + spans[c].x) % image_width;
4953 int sy = (spans[c].y + yoff) % image_height;
4954 if (sx < 0)
4955 sx += image_width;
4956 if (sy < 0)
4957 sy += image_height;
4958
4959 const int coverage = (spans[c].coverage * const_alpha) >> 8;
4960 while (length) {
4961 int l = qMin(image_width - sx, length);
4962 if (BufferSize < l)
4963 l = BufferSize;
4964 const uint *src = (const uint *)data->texture.scanLine(sy) + sx;
4965 uint *dest = ((uint *)data->rasterBuffer->scanLine(spans[c].y)) + x;
4966 func(dest, src, l, coverage);
4967 x += l;
4968 sx += l;
4969 length -= l;
4970 if (sx >= image_width)
4971 sx = 0;
4972 }
4973 }
4974 };
4975 QT_THREAD_PARALLEL_FILLS(function, MinParallelBlendPixels);
4976}
4977
4978static void blend_tiled_rgb565(int count, const QT_FT_Span *spans, void *userData)
4979{
4980 QSpanData *data = reinterpret_cast<QSpanData*>(userData);
4981 QPainter::CompositionMode mode = data->rasterBuffer->compositionMode;
4982
4983 if (data->texture.format != QImage::Format_RGB16
4984 || (mode != QPainter::CompositionMode_SourceOver
4985 && mode != QPainter::CompositionMode_Source))
4986 {
4987 blend_tiled_generic(count, spans, userData);
4988 return;
4989 }
4990
4991 const int image_width = data->texture.width;
4992 const int image_height = data->texture.height;
4993 int xoff = -qRound(-data->dx) % image_width;
4994 int yoff = -qRound(-data->dy) % image_height;
4995
4996 if (xoff < 0)
4997 xoff += image_width;
4998 if (yoff < 0)
4999 yoff += image_height;
5000
5001 const int const_alpha = data->texture.const_alpha;
5002 auto function = [=] (int cStart, int cEnd) {
5003 for (int c = cStart; c < cEnd; ++c) {
5004 const quint8 coverage = (const_alpha * spans[c].coverage) >> 8;
5005 if (coverage == 0)
5006 continue;
5007
5008 int x = spans[c].x;
5009 int length = spans[c].len;
5010 int sx = (xoff + spans[c].x) % image_width;
5011 int sy = (spans[c].y + yoff) % image_height;
5012 if (sx < 0)
5013 sx += image_width;
5014 if (sy < 0)
5015 sy += image_height;
5016
5017 if (coverage == 255) {
5018 // Copy the first texture block
5019 length = qMin(image_width,length);
5020 int tx = x;
5021 while (length) {
5022 int l = qMin(image_width - sx, length);
5023 if (BufferSize < l)
5024 l = BufferSize;
5025 quint16 *dest = ((quint16 *)data->rasterBuffer->scanLine(spans[c].y)) + tx;
5026 const quint16 *src = (const quint16 *)data->texture.scanLine(sy) + sx;
5027 memcpy(dest, src, l * sizeof(quint16));
5028 length -= l;
5029 tx += l;
5030 sx += l;
5031 if (sx >= image_width)
5032 sx = 0;
5033 }
5034
5035 // Now use the rasterBuffer as the source of the texture,
5036 // We can now progressively copy larger blocks
5037 // - Less cpu time in code figuring out what to copy
5038 // We are dealing with one block of data
5039 // - More likely to fit in the cache
5040 // - can use memcpy
5041 int copy_image_width = qMin(image_width, int(spans[c].len));
5042 length = spans[c].len - copy_image_width;
5043 quint16 *src = ((quint16 *)data->rasterBuffer->scanLine(spans[c].y)) + x;
5044 quint16 *dest = src + copy_image_width;
5045 while (copy_image_width < length) {
5046 memcpy(dest, src, copy_image_width * sizeof(quint16));
5047 dest += copy_image_width;
5048 length -= copy_image_width;
5049 copy_image_width *= 2;
5050 }
5051 if (length > 0)
5052 memcpy(dest, src, length * sizeof(quint16));
5053 } else {
5054 const quint8 alpha = (coverage + 1) >> 3;
5055 const quint8 ialpha = 0x20 - alpha;
5056 if (alpha > 0) {
5057 while (length) {
5058 int l = qMin(image_width - sx, length);
5059 if (BufferSize < l)
5060 l = BufferSize;
5061 quint16 *dest = ((quint16 *)data->rasterBuffer->scanLine(spans[c].y)) + x;
5062 const quint16 *src = (const quint16 *)data->texture.scanLine(sy) + sx;
5063 blend_sourceOver_rgb16_rgb16(dest, src, l, alpha, ialpha);
5064 x += l;
5065 sx += l;
5066 length -= l;
5067 if (sx >= image_width)
5068 sx = 0;
5069 }
5070 }
5071 }
5072 }
5073 };
5074 QT_THREAD_PARALLEL_FILLS(function, MinParallelBlendPixels);
5075}
5076
5077/* Image formats here are target formats */
5079 blend_untransformed_argb, // Untransformed
5080 blend_tiled_argb, // Tiled
5081 blend_src_generic, // Transformed
5082 blend_src_generic, // TransformedTiled
5083 blend_src_generic, // TransformedBilinear
5084 blend_src_generic // TransformedBilinearTiled
5085};
5086
5088 blend_untransformed_rgb565, // Untransformed
5089 blend_tiled_rgb565, // Tiled
5090 blend_src_generic, // Transformed
5091 blend_src_generic, // TransformedTiled
5092 blend_src_generic, // TransformedBilinear
5093 blend_src_generic // TransformedBilinearTiled
5094};
5095
5097 blend_untransformed_generic, // Untransformed
5098 blend_tiled_generic, // Tiled
5099 blend_src_generic, // Transformed
5100 blend_src_generic, // TransformedTiled
5101 blend_src_generic, // TransformedBilinear
5102 blend_src_generic // TransformedBilinearTiled
5103};
5104
5105#if QT_CONFIG(raster_64bit)
5106static const ProcessSpans processTextureSpansGeneric64[NBlendTypes] = {
5107 blend_untransformed_generic_rgb64, // Untransformed
5108 blend_tiled_generic_rgb64, // Tiled
5109 blend_src_generic_rgb64, // Transformed
5110 blend_src_generic_rgb64, // TransformedTiled
5111 blend_src_generic_rgb64, // TransformedBilinear
5112 blend_src_generic_rgb64 // TransformedBilinearTiled
5113};
5114#endif
5115
5116#if QT_CONFIG(raster_fp)
5117static const ProcessSpans processTextureSpansGenericFP[NBlendTypes] = {
5118 blend_untransformed_generic_fp, // Untransformed
5119 blend_tiled_generic_fp, // Tiled
5120 blend_src_generic_fp, // Transformed
5121 blend_src_generic_fp, // TransformedTiled
5122 blend_src_generic_fp, // TransformedBilinear
5123 blend_src_generic_fp // TransformedBilinearTiled
5124};
5125#endif
5126void qBlendTexture(int count, const QT_FT_Span *spans, void *userData)
5127{
5128 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
5129 TextureBlendType blendType = getBlendType(data);
5130 ProcessSpans proc;
5131 switch (data->rasterBuffer->format) {
5132 case QImage::Format_Invalid:
5133 Q_UNREACHABLE_RETURN();
5134 case QImage::Format_ARGB32_Premultiplied:
5135 proc = processTextureSpansARGB32PM[blendType];
5136 break;
5137 case QImage::Format_RGB16:
5138 proc = processTextureSpansRGB16[blendType];
5139 break;
5140#if defined(__SSE2__) || defined(__ARM_NEON__) || defined(QT_COMPILER_SUPPORTS_LSX) || (Q_PROCESSOR_WORDSIZE == 8)
5141 case QImage::Format_ARGB32:
5142 case QImage::Format_RGBA8888:
5143#endif
5144 case QImage::Format_BGR30:
5145 case QImage::Format_A2BGR30_Premultiplied:
5146 case QImage::Format_RGB30:
5147 case QImage::Format_A2RGB30_Premultiplied:
5148 case QImage::Format_RGBX64:
5149 case QImage::Format_RGBA64:
5150 case QImage::Format_RGBA64_Premultiplied:
5151 case QImage::Format_Grayscale16:
5152#if !QT_CONFIG(raster_fp)
5153 case QImage::Format_RGBX16FPx4:
5154 case QImage::Format_RGBA16FPx4:
5155 case QImage::Format_RGBA16FPx4_Premultiplied:
5156 case QImage::Format_RGBX32FPx4:
5157 case QImage::Format_RGBA32FPx4:
5158 case QImage::Format_RGBA32FPx4_Premultiplied:
5159#endif
5160#if QT_CONFIG(raster_64bit)
5161 proc = processTextureSpansGeneric64[blendType];
5162 break;
5163#endif // QT_CONFIG(raster_64bit)
5164#if QT_CONFIG(raster_fp)
5165 case QImage::Format_RGBX16FPx4:
5166 case QImage::Format_RGBA16FPx4:
5167 case QImage::Format_RGBA16FPx4_Premultiplied:
5168 case QImage::Format_RGBX32FPx4:
5169 case QImage::Format_RGBA32FPx4:
5170 case QImage::Format_RGBA32FPx4_Premultiplied:
5171 proc = processTextureSpansGenericFP[blendType];
5172 break;
5173#endif
5174 default:
5175 proc = processTextureSpansGeneric[blendType];
5176 break;
5177 }
5178 proc(count, spans, userData);
5179}
5180
5181static inline bool calculate_fixed_gradient_factors(int count, const QT_FT_Span *spans,
5182 const QSpanData *data,
5183 const LinearGradientValues &linear,
5184 int *pyinc, int *poff)
5185{
5186 /*
5187 The logic for vertical gradient calculations is a mathematically
5188 reduced copy of that in fetchLinearGradient() - which is basically:
5189
5190 qreal ry = data->m22 * (y + 0.5) + data->dy;
5191 qreal t = linear.dy*ry + linear.off;
5192 t *= (GRADIENT_STOPTABLE_SIZE - 1);
5193 quint32 color =
5194 qt_gradient_pixel_fixed(&data->gradient,
5195 int(t * FIXPT_SIZE));
5196
5197 This has then been converted to fixed point to improve performance.
5198 */
5199 const int gss = GRADIENT_STOPTABLE_SIZE - 1;
5200 qreal ryinc = linear.dy * data->m22 * gss * FIXPT_SIZE;
5201 qreal roff = (linear.dy * (data->m22 * qreal(0.5) + data->dy) + linear.off) * gss * FIXPT_SIZE;
5202 const qreal limit = qreal(std::numeric_limits<int>::max() - FIXPT_SIZE);
5203 if (count && (std::fabs(ryinc) < limit) && (std::fabs(roff) < limit)
5204 && (std::fabs(ryinc * spans->y + roff) < limit)
5205 && (std::fabs(ryinc * (spans + count - 1)->y + roff) < limit)) {
5206 *pyinc = int(ryinc);
5207 *poff = int(roff);
5208 return true;
5209 }
5210 return false;
5211}
5212
5213static bool blend_vertical_gradient_argb(int count, const QT_FT_Span *spans, void *userData)
5214{
5215 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
5216
5217 LinearGradientValues linear;
5218 getLinearGradientValues(&linear, data);
5219
5220 CompositionFunctionSolid funcSolid =
5221 functionForModeSolid[data->rasterBuffer->compositionMode];
5222
5223 int yinc(0), off(0);
5224 if (!calculate_fixed_gradient_factors(count, spans, data, linear, &yinc, &off))
5225 return false;
5226
5227 while (count--) {
5228 int y = spans->y;
5229 int x = spans->x;
5230
5231 quint32 *dst = (quint32 *)(data->rasterBuffer->scanLine(y)) + x;
5232 quint32 color =
5233 qt_gradient_pixel_fixed(&data->gradient, yinc * y + off);
5234
5235 funcSolid(dst, spans->len, color, spans->coverage);
5236 ++spans;
5237 }
5238 return true;
5239}
5240
5241template<ProcessSpans blend_color>
5242static bool blend_vertical_gradient(int count, const QT_FT_Span *spans, void *userData)
5243{
5244 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
5245
5246 LinearGradientValues linear;
5247 getLinearGradientValues(&linear, data);
5248
5249 int yinc(0), off(0);
5250 if (!calculate_fixed_gradient_factors(count, spans, data, linear, &yinc, &off))
5251 return false;
5252
5253 while (count--) {
5254 int y = spans->y;
5255
5256#if QT_CONFIG(raster_64bit)
5257 data->solidColor = qt_gradient_pixel64_fixed(&data->gradient, yinc * y + off);
5258#else
5259 data->solidColor = qt_gradient_pixel_fixed(&data->gradient, yinc * y + off);
5260#endif
5261 blend_color(1, spans, userData);
5262 ++spans;
5263 }
5264 return true;
5265}
5266
5267void qBlendGradient(int count, const QT_FT_Span *spans, void *userData)
5268{
5269 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
5270 bool isVerticalGradient =
5271 data->txop <= QTransform::TxScale &&
5272 data->type == QSpanData::LinearGradient &&
5273 data->gradient.linear.end.x == data->gradient.linear.origin.x;
5274 switch (data->rasterBuffer->format) {
5275 case QImage::Format_Invalid:
5276 break;
5277 case QImage::Format_RGB32:
5278 case QImage::Format_ARGB32_Premultiplied:
5279 if (isVerticalGradient && blend_vertical_gradient_argb(count, spans, userData))
5280 return;
5281 return blend_src_generic(count, spans, userData);
5282#if defined(__SSE2__) || defined(__ARM_NEON__) || defined(QT_COMPILER_SUPPORTS_LSX) || (Q_PROCESSOR_WORDSIZE == 8)
5283 case QImage::Format_ARGB32:
5284 case QImage::Format_RGBA8888:
5285#endif
5286 case QImage::Format_BGR30:
5287 case QImage::Format_A2BGR30_Premultiplied:
5288 case QImage::Format_RGB30:
5289 case QImage::Format_A2RGB30_Premultiplied:
5290 case QImage::Format_RGBX64:
5291 case QImage::Format_RGBA64:
5292 case QImage::Format_RGBA64_Premultiplied:
5293#if !QT_CONFIG(raster_fp)
5294 case QImage::Format_RGBX16FPx4:
5295 case QImage::Format_RGBA16FPx4:
5296 case QImage::Format_RGBA16FPx4_Premultiplied:
5297 case QImage::Format_RGBX32FPx4:
5298 case QImage::Format_RGBA32FPx4:
5299 case QImage::Format_RGBA32FPx4_Premultiplied:
5300#endif
5301#if QT_CONFIG(raster_64bit)
5302 if (isVerticalGradient && blend_vertical_gradient<blend_color_generic_rgb64>(count, spans, userData))
5303 return;
5304 return blend_src_generic_rgb64(count, spans, userData);
5305#endif // QT_CONFIG(raster_64bit)
5306#if QT_CONFIG(raster_fp)
5307 case QImage::Format_RGBX16FPx4:
5308 case QImage::Format_RGBA16FPx4:
5309 case QImage::Format_RGBA16FPx4_Premultiplied:
5310 case QImage::Format_RGBX32FPx4:
5311 case QImage::Format_RGBA32FPx4:
5312 case QImage::Format_RGBA32FPx4_Premultiplied:
5313 if (isVerticalGradient && blend_vertical_gradient<blend_color_generic_fp>(count, spans, userData))
5314 return;
5315 return blend_src_generic_fp(count, spans, userData);
5316#endif
5317 default:
5318 if (isVerticalGradient && blend_vertical_gradient<blend_color_generic>(count, spans, userData))
5319 return;
5320 return blend_src_generic(count, spans, userData);
5321 }
5322 Q_UNREACHABLE();
5323}
5324
5325template <class DST> static
5326inline void qt_bitmapblit_template(QRasterBuffer *rasterBuffer,
5327 int x, int y, DST color,
5328 const uchar *map,
5329 int mapWidth, int mapHeight, int mapStride)
5330{
5331 DST *dest = reinterpret_cast<DST *>(rasterBuffer->scanLine(y)) + x;
5332 const int destStride = rasterBuffer->stride<DST>();
5333
5334 if (mapWidth > 8) {
5335 while (--mapHeight >= 0) {
5336 int x0 = 0;
5337 int n = 0;
5338 for (int x = 0; x < mapWidth; x += 8) {
5339 uchar s = map[x >> 3];
5340 for (int i = 0; i < 8; ++i) {
5341 if (s & 0x80) {
5342 ++n;
5343 } else {
5344 if (n) {
5345 qt_memfill(dest + x0, color, n);
5346 x0 += n + 1;
5347 n = 0;
5348 } else {
5349 ++x0;
5350 }
5351 if (!s) {
5352 x0 += 8 - 1 - i;
5353 break;
5354 }
5355 }
5356 s <<= 1;
5357 }
5358 }
5359 if (n)
5360 qt_memfill(dest + x0, color, n);
5361 dest += destStride;
5362 map += mapStride;
5363 }
5364 } else {
5365 while (--mapHeight >= 0) {
5366 int x0 = 0;
5367 int n = 0;
5368 for (uchar s = *map; s; s <<= 1) {
5369 if (s & 0x80) {
5370 ++n;
5371 } else if (n) {
5372 qt_memfill(dest + x0, color, n);
5373 x0 += n + 1;
5374 n = 0;
5375 } else {
5376 ++x0;
5377 }
5378 }
5379 if (n)
5380 qt_memfill(dest + x0, color, n);
5381 dest += destStride;
5382 map += mapStride;
5383 }
5384 }
5385}
5386
5387inline static void qt_bitmapblit_argb32(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, color.toArgb32(),
5393 map, mapWidth, mapHeight, mapStride);
5394}
5395
5396inline static void qt_bitmapblit_rgba8888(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<quint32>(rasterBuffer, x, y, ARGB2RGBA(color.toArgb32()),
5402 map, mapWidth, mapHeight, mapStride);
5403}
5404
5405template<QtPixelOrder PixelOrder>
5406inline static void qt_bitmapblit_rgb30(QRasterBuffer *rasterBuffer,
5407 int x, int y, const QRgba64 &color,
5408 const uchar *map,
5409 int mapWidth, int mapHeight, int mapStride)
5410{
5411 qt_bitmapblit_template<quint32>(rasterBuffer, x, y, qConvertRgb64ToRgb30<PixelOrder>(color),
5412 map, mapWidth, mapHeight, mapStride);
5413}
5414
5415inline static void qt_bitmapblit_quint16(QRasterBuffer *rasterBuffer,
5416 int x, int y, const QRgba64 &color,
5417 const uchar *map,
5418 int mapWidth, int mapHeight, int mapStride)
5419{
5420 qt_bitmapblit_template<quint16>(rasterBuffer, x, y, color.toRgb16(),
5421 map, mapWidth, mapHeight, mapStride);
5422}
5423
5424static inline void grayBlendPixel(quint32 *dst, int coverage, QRgba64 srcLinear, const QColorTrcLut *colorProfile)
5425{
5426 // Do a gammacorrected gray alphablend...
5427 const QRgba64 dstLinear = colorProfile ? colorProfile->toLinear64(*dst) : QRgba64::fromArgb32(*dst);
5428
5429 QRgba64 blend = interpolate255(srcLinear, coverage, dstLinear, 255 - coverage);
5430
5431 *dst = colorProfile ? colorProfile->fromLinear64(blend) : toArgb32(blend);
5432}
5433
5434static inline void alphamapblend_argb32(quint32 *dst, int coverage, QRgba64 srcLinear, quint32 src, const QColorTrcLut *colorProfile)
5435{
5436 if (coverage == 0) {
5437 // nothing
5438 } else if (coverage == 255 || !colorProfile) {
5439 blend_pixel(*dst, src, coverage);
5440 } else if (*dst < 0xff000000) {
5441 // Give up and do a naive gray alphablend. Needed to deal with ARGB32 and invalid ARGB32_premultiplied, see QTBUG-60571
5442 blend_pixel(*dst, src, coverage);
5443 } else if (src >= 0xff000000) {
5444 grayBlendPixel(dst, coverage, srcLinear, colorProfile);
5445 } else {
5446 // First do naive blend with text-color
5447 QRgb s = *dst;
5448 blend_pixel(s, src);
5449 // Then gamma-corrected blend with glyph shape
5450 QRgba64 s64 = colorProfile ? colorProfile->toLinear64(s) : QRgba64::fromArgb32(s);
5451 grayBlendPixel(dst, coverage, s64, colorProfile);
5452 }
5453}
5454
5455#if QT_CONFIG(raster_64bit)
5456
5457static inline void grayBlendPixel(QRgba64 &dst, int coverage, QRgba64 srcLinear, const QColorTrcLut *colorProfile)
5458{
5459 // Do a gammacorrected gray alphablend...
5460 QRgba64 dstColor = dst;
5461 if (colorProfile) {
5462 if (dstColor.isOpaque())
5463 dstColor = colorProfile->toLinear(dstColor);
5464 else if (!dstColor.isTransparent())
5465 dstColor = colorProfile->toLinear(dstColor.unpremultiplied()).premultiplied();
5466 }
5467
5468 blend_pixel(dstColor, srcLinear, coverage);
5469
5470 if (colorProfile) {
5471 if (dstColor.isOpaque())
5472 dstColor = colorProfile->fromLinear(dstColor);
5473 else if (!dstColor.isTransparent())
5474 dstColor = colorProfile->fromLinear(dstColor.unpremultiplied()).premultiplied();
5475 }
5476 dst = dstColor;
5477}
5478
5479static inline void alphamapblend_generic(int coverage, QRgba64 *dest, int x, const QRgba64 &srcLinear, const QRgba64 &src, const QColorTrcLut *colorProfile)
5480{
5481 if (coverage == 0) {
5482 // nothing
5483 } else if (coverage == 255) {
5484 blend_pixel(dest[x], src);
5485 } else if (src.isOpaque()) {
5486 grayBlendPixel(dest[x], coverage, srcLinear, colorProfile);
5487 } else {
5488 // First do naive blend with text-color
5489 QRgba64 s = dest[x];
5490 blend_pixel(s, src);
5491 // Then gamma-corrected blend with glyph shape
5492 if (colorProfile)
5493 s = colorProfile->toLinear(s);
5494 grayBlendPixel(dest[x], coverage, s, colorProfile);
5495 }
5496}
5497
5498static void qt_alphamapblit_generic_oneline(const uchar *map, int len,
5499 const QRgba64 srcColor, QRgba64 *dest,
5500 const QRgba64 color,
5501 const QColorTrcLut *colorProfile)
5502{
5503 for (int j = 0; j < len; ++j)
5504 alphamapblend_generic(map[j], dest, j, srcColor, color, colorProfile);
5505}
5506
5507static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer,
5508 int x, int y, const QRgba64 &color,
5509 const uchar *map,
5510 int mapWidth, int mapHeight, int mapStride,
5511 const QClipData *clip, bool useGammaCorrection)
5512{
5513 if (color.isTransparent())
5514 return;
5515
5516 const QColorTrcLut *colorProfile = nullptr;
5517
5518 if (useGammaCorrection)
5519 colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA8Text();
5520
5521 QRgba64 srcColor = color;
5522 if (colorProfile && color.isOpaque())
5523 srcColor = colorProfile->toLinear(srcColor);
5524
5525 alignas(8) Q_DECL_UNINITIALIZED QRgba64 buffer[BufferSize];
5526 const DestFetchProc64 destFetch64 = destFetchProc64[rasterBuffer->format];
5527 const DestStoreProc64 destStore64 = destStoreProc64[rasterBuffer->format];
5528
5529 if (!clip) {
5530 for (int ly = 0; ly < mapHeight; ++ly) {
5531 int i = x;
5532 int length = mapWidth;
5533 while (length > 0) {
5534 int l = qMin(BufferSize, length);
5535
5536 QRgba64 *dest = destFetch64(buffer, rasterBuffer, i, y + ly, l);
5537 qt_alphamapblit_generic_oneline(map + i - x, l,
5538 srcColor, dest, color,
5539 colorProfile);
5540 if (destStore64)
5541 destStore64(rasterBuffer, i, y + ly, dest, l);
5542 length -= l;
5543 i += l;
5544 }
5545 map += mapStride;
5546 }
5547 } else {
5548 int bottom = qMin(y + mapHeight, rasterBuffer->height());
5549
5550 int top = qMax(y, 0);
5551 map += (top - y) * mapStride;
5552
5553 const_cast<QClipData *>(clip)->initialize();
5554 for (int yp = top; yp<bottom; ++yp) {
5555 const QClipData::ClipLine &line = clip->m_clipLines[yp];
5556
5557 for (int i=0; i<line.count; ++i) {
5558 const QT_FT_Span &clip = line.spans[i];
5559
5560 int start = qMax<int>(x, clip.x);
5561 int end = qMin<int>(x + mapWidth, clip.x + clip.len);
5562 if (end <= start)
5563 continue;
5564 Q_ASSERT(end - start <= BufferSize);
5565 QRgba64 *dest = destFetch64(buffer, rasterBuffer, start, clip.y, end - start);
5566 qt_alphamapblit_generic_oneline(map + start - x, end - start,
5567 srcColor, dest, color,
5568 colorProfile);
5569 if (destStore64)
5570 destStore64(rasterBuffer, start, clip.y, dest, end - start);
5571 } // for (i -> line.count)
5572 map += mapStride;
5573 } // for (yp -> bottom)
5574 }
5575}
5576#else
5577static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer,
5578 int x, int y, const QRgba64 &color,
5579 const uchar *map,
5580 int mapWidth, int mapHeight, int mapStride,
5581 const QClipData *clip, bool useGammaCorrection)
5582{
5583 if (color.isTransparent())
5584 return;
5585
5586 const quint32 c = color.toArgb32();
5587
5588 const QColorTrcLut *colorProfile = nullptr;
5589
5590 if (useGammaCorrection)
5591 colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA8Text();
5592
5593 QRgba64 srcColor = color;
5594 if (colorProfile && color.isOpaque())
5595 srcColor = colorProfile->toLinear(srcColor);
5596
5597 quint32 buffer[BufferSize];
5598 const DestFetchProc destFetch = destFetchProc[rasterBuffer->format];
5599 const DestStoreProc destStore = destStoreProc[rasterBuffer->format];
5600
5601 if (!clip) {
5602 for (int ly = 0; ly < mapHeight; ++ly) {
5603 int i = x;
5604 int length = mapWidth;
5605 while (length > 0) {
5606 int l = qMin(BufferSize, length);
5607 quint32 *dest = destFetch(buffer, rasterBuffer, i, y + ly, l);
5608 for (int j=0; j < l; ++j) {
5609 const int coverage = map[j + (i - x)];
5610 alphamapblend_argb32(dest + j, coverage, srcColor, c, colorProfile);
5611 }
5612 if (destStore)
5613 destStore(rasterBuffer, i, y + ly, dest, l);
5614 length -= l;
5615 i += l;
5616 }
5617 map += mapStride;
5618 }
5619 } else {
5620 int bottom = qMin(y + mapHeight, rasterBuffer->height());
5621
5622 int top = qMax(y, 0);
5623 map += (top - y) * mapStride;
5624
5625 const_cast<QClipData *>(clip)->initialize();
5626 for (int yp = top; yp<bottom; ++yp) {
5627 const QClipData::ClipLine &line = clip->m_clipLines[yp];
5628
5629 for (int i=0; i<line.count; ++i) {
5630 const QT_FT_Span &clip = line.spans[i];
5631
5632 int start = qMax<int>(x, clip.x);
5633 int end = qMin<int>(x + mapWidth, clip.x + clip.len);
5634 if (end <= start)
5635 continue;
5636 Q_ASSERT(end - start <= BufferSize);
5637 quint32 *dest = destFetch(buffer, rasterBuffer, start, clip.y, end - start);
5638
5639 for (int xp=start; xp<end; ++xp) {
5640 const int coverage = map[xp - x];
5641 alphamapblend_argb32(dest + xp - x, coverage, srcColor, color, colorProfile);
5642 }
5643 if (destStore)
5644 destStore(rasterBuffer, start, clip.y, dest, end - start);
5645 } // for (i -> line.count)
5646 map += mapStride;
5647 } // for (yp -> bottom)
5648 }
5649}
5650#endif
5651
5652static inline void alphamapblend_quint16(int coverage, quint16 *dest, int x, const quint16 srcColor)
5653{
5654 if (coverage == 0) {
5655 // nothing
5656 } else if (coverage == 255) {
5657 dest[x] = srcColor;
5658 } else {
5659 dest[x] = BYTE_MUL_RGB16(srcColor, coverage)
5660 + BYTE_MUL_RGB16(dest[x], 255 - coverage);
5661 }
5662}
5663
5665 int x, int y, const QRgba64 &color,
5666 const uchar *map,
5667 int mapWidth, int mapHeight, int mapStride,
5668 const QClipData *clip, bool useGammaCorrection)
5669{
5670 if (useGammaCorrection || !color.isOpaque()) {
5671 qt_alphamapblit_generic(rasterBuffer, x, y, color, map, mapWidth, mapHeight, mapStride, clip, useGammaCorrection);
5672 return;
5673 }
5674
5675 const quint16 c = color.toRgb16();
5676
5677 if (!clip) {
5678 quint16 *dest = reinterpret_cast<quint16*>(rasterBuffer->scanLine(y)) + x;
5679 const int destStride = rasterBuffer->stride<quint16>();
5680 while (--mapHeight >= 0) {
5681 for (int i = 0; i < mapWidth; ++i)
5682 alphamapblend_quint16(map[i], dest, i, c);
5683 dest += destStride;
5684 map += mapStride;
5685 }
5686 } else {
5687 int top = qMax(y, 0);
5688 int bottom = qMin(y + mapHeight, rasterBuffer->height());
5689 map += (top - y) * mapStride;
5690
5691 const_cast<QClipData *>(clip)->initialize();
5692 for (int yp = top; yp<bottom; ++yp) {
5693 const QClipData::ClipLine &line = clip->m_clipLines[yp];
5694
5695 quint16 *dest = reinterpret_cast<quint16*>(rasterBuffer->scanLine(yp));
5696
5697 for (int i=0; i<line.count; ++i) {
5698 const QT_FT_Span &clip = line.spans[i];
5699
5700 int start = qMax<int>(x, clip.x);
5701 int end = qMin<int>(x + mapWidth, clip.x + clip.len);
5702
5703 for (int xp=start; xp<end; ++xp)
5704 alphamapblend_quint16(map[xp - x], dest, xp, c);
5705 } // for (i -> line.count)
5706 map += mapStride;
5707 } // for (yp -> bottom)
5708 }
5709}
5710
5711static void qt_alphamapblit_argb32_oneline(const uchar *map,
5712 int mapWidth, const QRgba64 &srcColor,
5713 quint32 *dest, const quint32 c,
5714 const QColorTrcLut *colorProfile)
5715{
5716 for (int i = 0; i < mapWidth; ++i)
5717 alphamapblend_argb32(dest + i, map[i], srcColor, c, colorProfile);
5718}
5719
5720static void qt_alphamapblit_argb32(QRasterBuffer *rasterBuffer,
5721 int x, int y, const QRgba64 &color,
5722 const uchar *map,
5723 int mapWidth, int mapHeight, int mapStride,
5724 const QClipData *clip, bool useGammaCorrection)
5725{
5726 const quint32 c = color.toArgb32();
5727 const int destStride = rasterBuffer->stride<quint32>();
5728
5729 if (color.isTransparent())
5730 return;
5731
5732 const QColorTrcLut *colorProfile = nullptr;
5733
5734 if (useGammaCorrection)
5735 colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA8Text();
5736
5737 QRgba64 srcColor = color;
5738 if (colorProfile && color.isOpaque())
5739 srcColor = colorProfile->toLinear(srcColor);
5740
5741 if (!clip) {
5742 quint32 *dest = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y)) + x;
5743 while (--mapHeight >= 0) {
5744 qt_alphamapblit_argb32_oneline(map, mapWidth, srcColor, dest, c, colorProfile);
5745 dest += destStride;
5746 map += mapStride;
5747 }
5748 } else {
5749 int bottom = qMin(y + mapHeight, rasterBuffer->height());
5750
5751 int top = qMax(y, 0);
5752 map += (top - y) * mapStride;
5753
5754 const_cast<QClipData *>(clip)->initialize();
5755 for (int yp = top; yp<bottom; ++yp) {
5756 const QClipData::ClipLine &line = clip->m_clipLines[yp];
5757
5758 quint32 *dest = reinterpret_cast<quint32 *>(rasterBuffer->scanLine(yp));
5759
5760 for (int i=0; i<line.count; ++i) {
5761 const QT_FT_Span &clip = line.spans[i];
5762 int start = qMax<int>(x, clip.x);
5763 int end = qMin<int>(x + mapWidth, clip.x + clip.len);
5764 qt_alphamapblit_argb32_oneline(map + start - x, end - start, srcColor, dest + start, c, colorProfile);
5765 } // for (yp -> bottom)
5766 map += mapStride;
5767 }
5768 }
5769}
5770
5771#if QT_CONFIG(raster_64bit)
5772static void qt_alphamapblit_nonpremul_argb32(QRasterBuffer *rasterBuffer,
5773 int x, int y, const QRgba64 &color,
5774 const uchar *map,
5775 int mapWidth, int mapHeight, int mapStride,
5776 const QClipData *clip, bool useGammaCorrection)
5777{
5778 if (clip)
5779 return qt_alphamapblit_generic(rasterBuffer, x, y, color, map, mapWidth, mapHeight,
5780 mapStride, clip, useGammaCorrection);
5781
5782 if (color.isTransparent())
5783 return;
5784
5785 const QColorTrcLut *colorProfile = nullptr;
5786
5787 if (useGammaCorrection)
5788 colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA8Text();
5789
5790 const quint32 c = color.toArgb32();
5791 QRgba64 srcColor = color;
5792 if (colorProfile && color.isOpaque())
5793 srcColor = colorProfile->toLinear(srcColor);
5794
5795 alignas(8) Q_DECL_UNINITIALIZED QRgba64 buffer[BufferSize];
5796 const DestFetchProc64 destFetch64 = destFetchProc64[rasterBuffer->format];
5797 const DestStoreProc64 destStore64 = destStoreProc64[rasterBuffer->format];
5798
5799 for (int ly = 0; ly < mapHeight; ++ly) {
5800 bool dstFullyOpaque = true;
5801 int i = x;
5802 int length = mapWidth;
5803 while (length > 0) {
5804 int l = qMin(BufferSize, length);
5805 quint32 *dest = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y + ly)) + i;
5806 for (int j = 0; j < l && dstFullyOpaque; ++j)
5807 dstFullyOpaque = (dest[j] & 0xff000000) == 0xff000000;
5808 if (dstFullyOpaque) {
5809 // Use RGB/ARGB32PM optimized version
5810 qt_alphamapblit_argb32_oneline(map + i - x, l, srcColor, dest, c, colorProfile);
5811 } else {
5812 // Use generic version
5813 QRgba64 *dest64 = destFetch64(buffer, rasterBuffer, i, y + ly, l);
5814 qt_alphamapblit_generic_oneline(map + i - x, l,
5815 srcColor, dest64, color,
5816 colorProfile);
5817 if (destStore64)
5818 destStore64(rasterBuffer, i, y + ly, dest64, l);
5819 }
5820 length -= l;
5821 i += l;
5822 }
5823 map += mapStride;
5824 }
5825}
5826#endif
5827
5828static inline int qRgbAvg(QRgb rgb)
5829{
5830 return (qRed(rgb) * 5 + qGreen(rgb) * 6 + qBlue(rgb) * 5) / 16;
5831}
5832
5833static inline void rgbBlendPixel(quint32 *dst, int coverage, QRgba64 slinear, const QColorTrcLut *colorProfile)
5834{
5835 // Do a gammacorrected RGB alphablend...
5836 const QRgba64 dlinear = colorProfile ? colorProfile->toLinear64(*dst) : QRgba64::fromArgb32(*dst);
5837
5838 QRgba64 blend = rgbBlend(dlinear, slinear, coverage);
5839
5840 *dst = colorProfile ? colorProfile->fromLinear64(blend) : toArgb32(blend);
5841}
5842
5843static inline QRgb rgbBlend(QRgb d, QRgb s, uint rgbAlpha)
5844{
5845#if defined(__SSE2__)
5846 __m128i vd = _mm_cvtsi32_si128(d);
5847 __m128i vs = _mm_cvtsi32_si128(s);
5848 __m128i va = _mm_cvtsi32_si128(rgbAlpha);
5849 const __m128i vz = _mm_setzero_si128();
5850 vd = _mm_unpacklo_epi8(vd, vz);
5851 vs = _mm_unpacklo_epi8(vs, vz);
5852 va = _mm_unpacklo_epi8(va, vz);
5853 __m128i vb = _mm_xor_si128(_mm_set1_epi16(255), va);
5854 vs = _mm_mullo_epi16(vs, va);
5855 vd = _mm_mullo_epi16(vd, vb);
5856 vd = _mm_add_epi16(vd, vs);
5857 vd = _mm_add_epi16(vd, _mm_srli_epi16(vd, 8));
5858 vd = _mm_add_epi16(vd, _mm_set1_epi16(0x80));
5859 vd = _mm_srli_epi16(vd, 8);
5860 vd = _mm_packus_epi16(vd, vd);
5861 return _mm_cvtsi128_si32(vd);
5862#else
5863 const int dr = qRed(d);
5864 const int dg = qGreen(d);
5865 const int db = qBlue(d);
5866
5867 const int sr = qRed(s);
5868 const int sg = qGreen(s);
5869 const int sb = qBlue(s);
5870
5871 const int mr = qRed(rgbAlpha);
5872 const int mg = qGreen(rgbAlpha);
5873 const int mb = qBlue(rgbAlpha);
5874
5875 const int nr = qt_div_255(sr * mr + dr * (255 - mr));
5876 const int ng = qt_div_255(sg * mg + dg * (255 - mg));
5877 const int nb = qt_div_255(sb * mb + db * (255 - mb));
5878
5879 return 0xff000000 | (nr << 16) | (ng << 8) | nb;
5880#endif
5881}
5882
5883static inline void alphargbblend_argb32(quint32 *dst, uint coverage, const QRgba64 &srcLinear, quint32 src, const QColorTrcLut *colorProfile)
5884{
5885 if (coverage == 0xff000000) {
5886 // nothing
5887 } else if (coverage == 0xffffffff && qAlpha(src) == 255) {
5888 blend_pixel(*dst, src);
5889 } else if (*dst < 0xff000000) {
5890 // Give up and do a naive gray alphablend. Needed to deal with ARGB32 and invalid ARGB32_premultiplied, see QTBUG-60571
5891 blend_pixel(*dst, src, qRgbAvg(coverage));
5892 } else if (!colorProfile) {
5893 // First do naive blend with text-color
5894 QRgb s = *dst;
5895 blend_pixel(s, src);
5896 // Then a naive blend with glyph shape
5897 *dst = rgbBlend(*dst, s, coverage);
5898 } else if (srcLinear.isOpaque()) {
5899 rgbBlendPixel(dst, coverage, srcLinear, colorProfile);
5900 } else {
5901 // First do naive blend with text-color
5902 QRgb s = *dst;
5903 blend_pixel(s, src);
5904 // Then gamma-corrected blend with glyph shape
5905 QRgba64 s64 = colorProfile ? colorProfile->toLinear64(s) : QRgba64::fromArgb32(s);
5906 rgbBlendPixel(dst, coverage, s64, colorProfile);
5907 }
5908}
5909
5910#if QT_CONFIG(raster_64bit)
5911static inline void rgbBlendPixel(QRgba64 &dst, int coverage, QRgba64 slinear, const QColorTrcLut *colorProfile)
5912{
5913 // Do a gammacorrected RGB alphablend...
5914 const QRgba64 dlinear = colorProfile ? colorProfile->toLinear(dst) : dst;
5915
5916 QRgba64 blend = rgbBlend(dlinear, slinear, coverage);
5917
5918 dst = colorProfile ? colorProfile->fromLinear(blend) : blend;
5919}
5920
5921static inline void alphargbblend_generic(uint coverage, QRgba64 *dest, int x, const QRgba64 &srcLinear, const QRgba64 &src, const QColorTrcLut *colorProfile)
5922{
5923 if (coverage == 0xff000000) {
5924 // nothing
5925 } else if (coverage == 0xffffffff) {
5926 blend_pixel(dest[x], src);
5927 } else if (!dest[x].isOpaque()) {
5928 // Do a gray alphablend.
5929 alphamapblend_generic(qRgbAvg(coverage), dest, x, srcLinear, src, colorProfile);
5930 } else if (src.isOpaque()) {
5931 rgbBlendPixel(dest[x], coverage, srcLinear, colorProfile);
5932 } else {
5933 // First do naive blend with text-color
5934 QRgba64 s = dest[x];
5935 blend_pixel(s, src);
5936 // Then gamma-corrected blend with glyph shape
5937 if (colorProfile)
5938 s = colorProfile->toLinear(s);
5939 rgbBlendPixel(dest[x], coverage, s, colorProfile);
5940 }
5941}
5942
5943static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer,
5944 int x, int y, const QRgba64 &color,
5945 const uint *src, int mapWidth, int mapHeight, int srcStride,
5946 const QClipData *clip, bool useGammaCorrection)
5947{
5948 if (color.isTransparent())
5949 return;
5950
5951 const QColorTrcLut *colorProfile = nullptr;
5952
5953 if (useGammaCorrection)
5954 colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA32Text();
5955
5956 QRgba64 srcColor = color;
5957 if (colorProfile && color.isOpaque())
5958 srcColor = colorProfile->toLinear(srcColor);
5959
5960 alignas(8) Q_DECL_UNINITIALIZED QRgba64 buffer[BufferSize];
5961 const DestFetchProc64 destFetch64 = destFetchProc64[rasterBuffer->format];
5962 const DestStoreProc64 destStore64 = destStoreProc64[rasterBuffer->format];
5963
5964 if (!clip) {
5965 for (int ly = 0; ly < mapHeight; ++ly) {
5966 int i = x;
5967 int length = mapWidth;
5968 while (length > 0) {
5969 int l = qMin(BufferSize, length);
5970 QRgba64 *dest = destFetch64(buffer, rasterBuffer, i, y + ly, l);
5971 for (int j=0; j < l; ++j) {
5972 const uint coverage = src[j + (i - x)];
5973 alphargbblend_generic(coverage, dest, j, srcColor, color, colorProfile);
5974 }
5975 if (destStore64)
5976 destStore64(rasterBuffer, i, y + ly, dest, l);
5977 length -= l;
5978 i += l;
5979 }
5980 src += srcStride;
5981 }
5982 } else {
5983 int bottom = qMin(y + mapHeight, rasterBuffer->height());
5984
5985 int top = qMax(y, 0);
5986 src += (top - y) * srcStride;
5987
5988 const_cast<QClipData *>(clip)->initialize();
5989 for (int yp = top; yp<bottom; ++yp) {
5990 const QClipData::ClipLine &line = clip->m_clipLines[yp];
5991
5992 for (int i=0; i<line.count; ++i) {
5993 const QT_FT_Span &clip = line.spans[i];
5994
5995 int start = qMax<int>(x, clip.x);
5996 int end = qMin<int>(x + mapWidth, clip.x + clip.len);
5997 if (end <= start)
5998 continue;
5999 Q_ASSERT(end - start <= BufferSize);
6000 QRgba64 *dest = destFetch64(buffer, rasterBuffer, start, clip.y, end - start);
6001
6002 for (int xp=start; xp<end; ++xp) {
6003 const uint coverage = src[xp - x];
6004 alphargbblend_generic(coverage, dest, xp - start, srcColor, color, colorProfile);
6005 }
6006 if (destStore64)
6007 destStore64(rasterBuffer, start, clip.y, dest, end - start);
6008 } // for (i -> line.count)
6009 src += srcStride;
6010 } // for (yp -> bottom)
6011 }
6012}
6013#else
6014static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer,
6015 int x, int y, const QRgba64 &color,
6016 const uint *src, int mapWidth, int mapHeight, int srcStride,
6017 const QClipData *clip, bool useGammaCorrection)
6018{
6019 if (color.isTransparent())
6020 return;
6021
6022 const quint32 c = color.toArgb32();
6023
6024 const QColorTrcLut *colorProfile = nullptr;
6025
6026 if (useGammaCorrection)
6027 colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA32Text();
6028
6029 QRgba64 srcColor = color;
6030 if (colorProfile && color.isOpaque())
6031 srcColor = colorProfile->toLinear(srcColor);
6032
6033 Q_DECL_UNINITIALIZED quint32 buffer[BufferSize];
6034 const DestFetchProc destFetch = destFetchProc[rasterBuffer->format];
6035 const DestStoreProc destStore = destStoreProc[rasterBuffer->format];
6036
6037 if (!clip) {
6038 for (int ly = 0; ly < mapHeight; ++ly) {
6039 int i = x;
6040 int length = mapWidth;
6041 while (length > 0) {
6042 int l = qMin(BufferSize, length);
6043 quint32 *dest = destFetch(buffer, rasterBuffer, i, y + ly, l);
6044 for (int j=0; j < l; ++j) {
6045 const uint coverage = src[j + (i - x)];
6046 alphargbblend_argb32(dest + j, coverage, srcColor, c, colorProfile);
6047 }
6048 if (destStore)
6049 destStore(rasterBuffer, i, y + ly, dest, l);
6050 length -= l;
6051 i += l;
6052 }
6053 src += srcStride;
6054 }
6055 } else {
6056 int bottom = qMin(y + mapHeight, rasterBuffer->height());
6057
6058 int top = qMax(y, 0);
6059 src += (top - y) * srcStride;
6060
6061 const_cast<QClipData *>(clip)->initialize();
6062 for (int yp = top; yp<bottom; ++yp) {
6063 const QClipData::ClipLine &line = clip->m_clipLines[yp];
6064
6065 for (int i=0; i<line.count; ++i) {
6066 const QT_FT_Span &clip = line.spans[i];
6067
6068 int start = qMax<int>(x, clip.x);
6069 int end = qMin<int>(x + mapWidth, clip.x + clip.len);
6070 if (end <= start)
6071 continue;
6072 Q_ASSERT(end - start <= BufferSize);
6073 quint32 *dest = destFetch(buffer, rasterBuffer, start, clip.y, end - start);
6074
6075 for (int xp=start; xp<end; ++xp) {
6076 const uint coverage = src[xp - x];
6077 alphargbblend_argb32(dest + xp - start, coverage, srcColor, c, colorProfile);
6078 }
6079 if (destStore)
6080 destStore(rasterBuffer, start, clip.y, dest, end - start);
6081 } // for (i -> line.count)
6082 src += srcStride;
6083 } // for (yp -> bottom)
6084 }
6085}
6086#endif
6087
6088static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer,
6089 int x, int y, const QRgba64 &color,
6090 const uint *src, int mapWidth, int mapHeight, int srcStride,
6091 const QClipData *clip, bool useGammaCorrection)
6092{
6093 if (color.isTransparent())
6094 return;
6095
6096 const quint32 c = color.toArgb32();
6097
6098 const QColorTrcLut *colorProfile = nullptr;
6099
6100 if (useGammaCorrection)
6101 colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA32Text();
6102
6103 QRgba64 srcColor = color;
6104 if (colorProfile && color.isOpaque())
6105 srcColor = colorProfile->toLinear(srcColor);
6106
6107 if (!clip) {
6108 quint32 *dst = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y)) + x;
6109 const int destStride = rasterBuffer->stride<quint32>();
6110 while (--mapHeight >= 0) {
6111 for (int i = 0; i < mapWidth; ++i) {
6112 const uint coverage = src[i];
6113 alphargbblend_argb32(dst + i, coverage, srcColor, c, colorProfile);
6114 }
6115
6116 dst += destStride;
6117 src += srcStride;
6118 }
6119 } else {
6120 int bottom = qMin(y + mapHeight, rasterBuffer->height());
6121
6122 int top = qMax(y, 0);
6123 src += (top - y) * srcStride;
6124
6125 const_cast<QClipData *>(clip)->initialize();
6126 for (int yp = top; yp<bottom; ++yp) {
6127 const QClipData::ClipLine &line = clip->m_clipLines[yp];
6128
6129 quint32 *dst = reinterpret_cast<quint32 *>(rasterBuffer->scanLine(yp));
6130
6131 for (int i=0; i<line.count; ++i) {
6132 const QT_FT_Span &clip = line.spans[i];
6133
6134 int start = qMax<int>(x, clip.x);
6135 int end = qMin<int>(x + mapWidth, clip.x + clip.len);
6136
6137 for (int xp=start; xp<end; ++xp) {
6138 const uint coverage = src[xp - x];
6139 alphargbblend_argb32(dst + xp, coverage, srcColor, c, colorProfile);
6140 }
6141 } // for (i -> line.count)
6142 src += srcStride;
6143 } // for (yp -> bottom)
6144
6145 }
6146}
6147
6148static void qt_rectfill_argb32(QRasterBuffer *rasterBuffer,
6149 int x, int y, int width, int height,
6150 const QRgba64 &color)
6151{
6152 qt_rectfill<quint32>(reinterpret_cast<quint32 *>(rasterBuffer->buffer()),
6153 color.toArgb32(), x, y, width, height, rasterBuffer->bytesPerLine());
6154}
6155
6156static void qt_rectfill_quint16(QRasterBuffer *rasterBuffer,
6157 int x, int y, int width, int height,
6158 const QRgba64 &color)
6159{
6160 const QPixelLayout &layout = qPixelLayouts[rasterBuffer->format];
6161 quint32 c32 = color.toArgb32();
6162 quint16 c16;
6163 layout.storeFromARGB32PM(reinterpret_cast<uchar *>(&c16), &c32, 0, 1, nullptr, nullptr);
6164 qt_rectfill<quint16>(reinterpret_cast<quint16 *>(rasterBuffer->buffer()),
6165 c16, x, y, width, height, rasterBuffer->bytesPerLine());
6166}
6167
6168static void qt_rectfill_quint24(QRasterBuffer *rasterBuffer,
6169 int x, int y, int width, int height,
6170 const QRgba64 &color)
6171{
6172 const QPixelLayout &layout = qPixelLayouts[rasterBuffer->format];
6173 quint32 c32 = color.toArgb32();
6174 quint24 c24;
6175 layout.storeFromARGB32PM(reinterpret_cast<uchar *>(&c24), &c32, 0, 1, nullptr, nullptr);
6176 qt_rectfill<quint24>(reinterpret_cast<quint24 *>(rasterBuffer->buffer()),
6177 c24, x, y, width, height, rasterBuffer->bytesPerLine());
6178}
6179
6181 int x, int y, int width, int height,
6182 const QRgba64 &color)
6183{
6184 qt_rectfill<quint32>(reinterpret_cast<quint32 *>(rasterBuffer->buffer()),
6185 color.unpremultiplied().toArgb32(), x, y, width, height, rasterBuffer->bytesPerLine());
6186}
6187
6188static void qt_rectfill_rgbx(QRasterBuffer *rasterBuffer,
6189 int x, int y, int width, int height,
6190 const QRgba64 &color)
6191{
6192 qt_rectfill<quint32>(reinterpret_cast<quint32 *>(rasterBuffer->buffer()),
6193 ARGB2RGBA(color.toArgb32() | 0xff000000), x, y, width, height, rasterBuffer->bytesPerLine());
6194}
6195
6196static void qt_rectfill_rgba(QRasterBuffer *rasterBuffer,
6197 int x, int y, int width, int height,
6198 const QRgba64 &color)
6199{
6200 qt_rectfill<quint32>(reinterpret_cast<quint32 *>(rasterBuffer->buffer()),
6201 ARGB2RGBA(color.toArgb32()), x, y, width, height, rasterBuffer->bytesPerLine());
6202}
6203
6205 int x, int y, int width, int height,
6206 const QRgba64 &color)
6207{
6208 qt_rectfill<quint32>(reinterpret_cast<quint32 *>(rasterBuffer->buffer()),
6209 ARGB2RGBA(color.unpremultiplied().toArgb32()), x, y, width, height, rasterBuffer->bytesPerLine());
6210}
6211
6212template<QtPixelOrder PixelOrder>
6213static void qt_rectfill_rgb30(QRasterBuffer *rasterBuffer,
6214 int x, int y, int width, int height,
6215 const QRgba64 &color)
6216{
6217 qt_rectfill<quint32>(reinterpret_cast<quint32 *>(rasterBuffer->buffer()),
6218 qConvertRgb64ToRgb30<PixelOrder>(color), x, y, width, height, rasterBuffer->bytesPerLine());
6219}
6220
6221static void qt_rectfill_alpha(QRasterBuffer *rasterBuffer,
6222 int x, int y, int width, int height,
6223 const QRgba64 &color)
6224{
6225 qt_rectfill<quint8>(reinterpret_cast<quint8 *>(rasterBuffer->buffer()),
6226 color.alpha() >> 8, x, y, width, height, rasterBuffer->bytesPerLine());
6227}
6228
6229static void qt_rectfill_gray(QRasterBuffer *rasterBuffer,
6230 int x, int y, int width, int height,
6231 const QRgba64 &color)
6232{
6233 qt_rectfill<quint8>(reinterpret_cast<quint8 *>(rasterBuffer->buffer()),
6234 qGray(color.toArgb32()), x, y, width, height, rasterBuffer->bytesPerLine());
6235}
6236
6237static void qt_rectfill_quint64(QRasterBuffer *rasterBuffer,
6238 int x, int y, int width, int height,
6239 const QRgba64 &color)
6240{
6241 const auto store = qStoreFromRGBA64PM[rasterBuffer->format];
6242 quint64 c64;
6243 store(reinterpret_cast<uchar *>(&c64), &color, 0, 1, nullptr, nullptr);
6244 qt_rectfill<quint64>(reinterpret_cast<quint64 *>(rasterBuffer->buffer()),
6245 c64, x, y, width, height, rasterBuffer->bytesPerLine());
6246}
6247
6248static void qt_rectfill_fp32x4(QRasterBuffer *rasterBuffer,
6249 int x, int y, int width, int height,
6250 const QRgba64 &color)
6251{
6252 const auto store = qStoreFromRGBA64PM[rasterBuffer->format];
6253 QRgbaFloat32 c;
6254 store(reinterpret_cast<uchar *>(&c), &color, 0, 1, nullptr, nullptr);
6255 qt_rectfill<QRgbaFloat32>(reinterpret_cast<QRgbaFloat32 *>(rasterBuffer->buffer()),
6256 c, x, y, width, height, rasterBuffer->bytesPerLine());
6257}
6258
6259// Map table for destination image format. Contains function pointers
6260// for blends of various types unto the destination
6261
6263{
6264 // Format_Invalid,
6265 { nullptr, nullptr, nullptr, nullptr, nullptr },
6266 // Format_Mono,
6267 {
6268 blend_color_generic,
6269 nullptr, nullptr, nullptr, nullptr
6270 },
6271 // Format_MonoLSB,
6272 {
6273 blend_color_generic,
6274 nullptr, nullptr, nullptr, nullptr
6275 },
6276 // Format_Indexed8,
6277 {
6278 blend_color_generic,
6279 nullptr, nullptr, nullptr, nullptr
6280 },
6281 // Format_RGB32,
6282 {
6283 blend_color_argb,
6284 qt_bitmapblit_argb32,
6285 qt_alphamapblit_argb32,
6286 qt_alphargbblit_argb32,
6287 qt_rectfill_argb32
6288 },
6289 // Format_ARGB32,
6290 {
6291 blend_color_generic,
6292 qt_bitmapblit_argb32,
6293#if QT_CONFIG(raster_64bit)
6294 qt_alphamapblit_nonpremul_argb32,
6295#else
6296 qt_alphamapblit_generic,
6297#endif
6298 qt_alphargbblit_generic,
6299 qt_rectfill_nonpremul_argb32
6300 },
6301 // Format_ARGB32_Premultiplied
6302 {
6303 blend_color_argb,
6304 qt_bitmapblit_argb32,
6305 qt_alphamapblit_argb32,
6306 qt_alphargbblit_argb32,
6307 qt_rectfill_argb32
6308 },
6309 // Format_RGB16
6310 {
6311 blend_color_generic,
6312 qt_bitmapblit_quint16,
6313 qt_alphamapblit_quint16,
6314 qt_alphargbblit_generic,
6315 qt_rectfill_quint16
6316 },
6317 // Format_ARGB8565_Premultiplied
6318 {
6319 blend_color_generic,
6320 nullptr,
6321 qt_alphamapblit_generic,
6322 qt_alphargbblit_generic,
6323 qt_rectfill_quint24
6324 },
6325 // Format_RGB666
6326 {
6327 blend_color_generic,
6328 nullptr,
6329 qt_alphamapblit_generic,
6330 qt_alphargbblit_generic,
6331 qt_rectfill_quint24
6332 },
6333 // Format_ARGB6666_Premultiplied
6334 {
6335 blend_color_generic,
6336 nullptr,
6337 qt_alphamapblit_generic,
6338 qt_alphargbblit_generic,
6339 qt_rectfill_quint24
6340 },
6341 // Format_RGB555
6342 {
6343 blend_color_generic,
6344 nullptr,
6345 qt_alphamapblit_generic,
6346 qt_alphargbblit_generic,
6347 qt_rectfill_quint16
6348 },
6349 // Format_ARGB8555_Premultiplied
6350 {
6351 blend_color_generic,
6352 nullptr,
6353 qt_alphamapblit_generic,
6354 qt_alphargbblit_generic,
6355 qt_rectfill_quint24
6356 },
6357 // Format_RGB888
6358 {
6359 blend_color_generic,
6360 nullptr,
6361 qt_alphamapblit_generic,
6362 qt_alphargbblit_generic,
6363 qt_rectfill_quint24
6364 },
6365 // Format_RGB444
6366 {
6367 blend_color_generic,
6368 nullptr,
6369 qt_alphamapblit_generic,
6370 qt_alphargbblit_generic,
6371 qt_rectfill_quint16
6372 },
6373 // Format_ARGB4444_Premultiplied
6374 {
6375 blend_color_generic,
6376 nullptr,
6377 qt_alphamapblit_generic,
6378 qt_alphargbblit_generic,
6379 qt_rectfill_quint16
6380 },
6381 // Format_RGBX8888
6382 {
6383 blend_color_generic,
6384 qt_bitmapblit_rgba8888,
6385 qt_alphamapblit_generic,
6386 qt_alphargbblit_generic,
6387 qt_rectfill_rgbx
6388 },
6389 // Format_RGBA8888
6390 {
6391 blend_color_generic,
6392 qt_bitmapblit_rgba8888,
6393 qt_alphamapblit_generic,
6394 qt_alphargbblit_generic,
6395 qt_rectfill_nonpremul_rgba
6396 },
6397 // Format_RGB8888_Premultiplied
6398 {
6399 blend_color_generic,
6400 qt_bitmapblit_rgba8888,
6401 qt_alphamapblit_generic,
6402 qt_alphargbblit_generic,
6403 qt_rectfill_rgba
6404 },
6405 // Format_BGR30
6406 {
6407 blend_color_generic_rgb64,
6408 qt_bitmapblit_rgb30<PixelOrderBGR>,
6409 qt_alphamapblit_generic,
6410 qt_alphargbblit_generic,
6411 qt_rectfill_rgb30<PixelOrderBGR>
6412 },
6413 // Format_A2BGR30_Premultiplied
6414 {
6415 blend_color_generic_rgb64,
6416 qt_bitmapblit_rgb30<PixelOrderBGR>,
6417 qt_alphamapblit_generic,
6418 qt_alphargbblit_generic,
6419 qt_rectfill_rgb30<PixelOrderBGR>
6420 },
6421 // Format_RGB30
6422 {
6423 blend_color_generic_rgb64,
6424 qt_bitmapblit_rgb30<PixelOrderRGB>,
6425 qt_alphamapblit_generic,
6426 qt_alphargbblit_generic,
6427 qt_rectfill_rgb30<PixelOrderRGB>
6428 },
6429 // Format_A2RGB30_Premultiplied
6430 {
6431 blend_color_generic_rgb64,
6432 qt_bitmapblit_rgb30<PixelOrderRGB>,
6433 qt_alphamapblit_generic,
6434 qt_alphargbblit_generic,
6435 qt_rectfill_rgb30<PixelOrderRGB>
6436 },
6437 // Format_Alpha8
6438 {
6439 blend_color_generic,
6440 nullptr,
6441 qt_alphamapblit_generic,
6442 qt_alphargbblit_generic,
6443 qt_rectfill_alpha
6444 },
6445 // Format_Grayscale8
6446 {
6447 blend_color_generic,
6448 nullptr,
6449 qt_alphamapblit_generic,
6450 qt_alphargbblit_generic,
6451 qt_rectfill_gray
6452 },
6453 // Format_RGBX64
6454 {
6455 blend_color_generic_rgb64,
6456 nullptr,
6457 qt_alphamapblit_generic,
6458 qt_alphargbblit_generic,
6459 qt_rectfill_quint64
6460 },
6461 // Format_RGBA64
6462 {
6463 blend_color_generic_rgb64,
6464 nullptr,
6465 qt_alphamapblit_generic,
6466 qt_alphargbblit_generic,
6467 qt_rectfill_quint64
6468 },
6469 // Format_RGBA64_Premultiplied
6470 {
6471 blend_color_generic_rgb64,
6472 nullptr,
6473 qt_alphamapblit_generic,
6474 qt_alphargbblit_generic,
6475 qt_rectfill_quint64
6476 },
6477 // Format_Grayscale16
6478 {
6479 blend_color_generic_rgb64,
6480 nullptr,
6481 qt_alphamapblit_generic,
6482 qt_alphargbblit_generic,
6483 qt_rectfill_quint16
6484 },
6485 // Format_BGR888
6486 {
6487 blend_color_generic,
6488 nullptr,
6489 qt_alphamapblit_generic,
6490 qt_alphargbblit_generic,
6491 qt_rectfill_quint24
6492 },
6493 // Format_RGBX16FPx4
6494 {
6495 blend_color_generic_fp,
6496 nullptr,
6497 qt_alphamapblit_generic,
6498 qt_alphargbblit_generic,
6499 qt_rectfill_quint64
6500 },
6501 // Format_RGBA16FPx4
6502 {
6503 blend_color_generic_fp,
6504 nullptr,
6505 qt_alphamapblit_generic,
6506 qt_alphargbblit_generic,
6507 qt_rectfill_quint64
6508 },
6509 // Format_RGBA16FPx4_Premultiplied
6510 {
6511 blend_color_generic_fp,
6512 nullptr,
6513 qt_alphamapblit_generic,
6514 qt_alphargbblit_generic,
6515 qt_rectfill_quint64
6516 },
6517 // Format_RGBX32FPx4
6518 {
6519 blend_color_generic_fp,
6520 nullptr,
6521 qt_alphamapblit_generic,
6522 qt_alphargbblit_generic,
6523 qt_rectfill_fp32x4
6524 },
6525 // Format_RGBA32FPx4
6526 {
6527 blend_color_generic_fp,
6528 nullptr,
6529 qt_alphamapblit_generic,
6530 qt_alphargbblit_generic,
6531 qt_rectfill_fp32x4
6532 },
6533 // Format_RGBA32FPx4_Premultiplied
6534 {
6535 blend_color_generic_fp,
6536 nullptr,
6537 qt_alphamapblit_generic,
6538 qt_alphargbblit_generic,
6539 qt_rectfill_fp32x4
6540 },
6541};
6542
6543static_assert(std::size(qDrawHelper) == QImage::NImageFormats);
6544
6545#if !defined(Q_PROCESSOR_X86) && !defined(QT_COMPILER_SUPPORTS_LSX)
6546void qt_memfill64(quint64 *dest, quint64 color, qsizetype count)
6547{
6548 qt_memfill_template<quint64>(dest, color, count);
6549}
6550#endif
6551
6552#if defined(QT_COMPILER_SUPPORTS_SSSE3) && defined(Q_CC_GNU) && !defined(Q_CC_CLANG)
6553__attribute__((optimize("no-tree-vectorize")))
6554#endif
6555void qt_memfill24(quint24 *dest, quint24 color, qsizetype count)
6556{
6557# ifdef QT_COMPILER_SUPPORTS_SSSE3
6558 extern void qt_memfill24_ssse3(quint24 *, quint24, qsizetype);
6559 if (qCpuHasFeature(SSSE3))
6560 return qt_memfill24_ssse3(dest, color, count);
6561# elif defined QT_COMPILER_SUPPORTS_LSX
6562 extern void qt_memfill24_lsx(quint24 *, quint24, qsizetype);
6563 if (qCpuHasFeature(LSX))
6564 return qt_memfill24_lsx(dest, color, count);
6565# endif
6566
6567 const quint32 v = color;
6568 quint24 *end = dest + count;
6569
6570 // prolog: align dest to 32bit
6571 while ((quintptr(dest) & 0x3) && dest < end) {
6572 *dest++ = v;
6573 }
6574 if (dest >= end)
6575 return;
6576
6577 const uint val1 = qFromBigEndian((v << 8) | (v >> 16));
6578 const uint val2 = qFromBigEndian((v << 16) | (v >> 8));
6579 const uint val3 = qFromBigEndian((v << 24) | (v >> 0));
6580
6581 for ( ; dest <= (end - 4); dest += 4) {
6582 quint32 *dst = reinterpret_cast<quint32 *>(dest);
6583 dst[0] = val1;
6584 dst[1] = val2;
6585 dst[2] = val3;
6586 }
6587
6588 // less than 4px left
6589 switch (end - dest) {
6590 case 3:
6591 *dest++ = v;
6592 Q_FALLTHROUGH();
6593 case 2:
6594 *dest++ = v;
6595 Q_FALLTHROUGH();
6596 case 1:
6597 *dest++ = v;
6598 }
6599}
6600
6601void qt_memfill16(quint16 *dest, quint16 value, qsizetype count)
6602{
6603 const int align = quintptr(dest) & 0x3;
6604 if (align) {
6605 *dest++ = value;
6606 --count;
6607 }
6608
6609 if (count & 0x1)
6610 dest[count - 1] = value;
6611
6612 const quint32 value32 = (value << 16) | value;
6613 qt_memfill32(reinterpret_cast<quint32*>(dest), value32, count / 2);
6614}
6615
6616#if defined(Q_PROCESSOR_X86) || defined(QT_COMPILER_SUPPORTS_LSX)
6617void (*qt_memfill32)(quint32 *dest, quint32 value, qsizetype count) = nullptr;
6618void (*qt_memfill64)(quint64 *dest, quint64 value, qsizetype count) = nullptr;
6619#elif !defined(__ARM_NEON__) && !defined(__MIPS_DSP__)
6620void qt_memfill32(quint32 *dest, quint32 color, qsizetype count)
6621{
6622 qt_memfill_template<quint32>(dest, color, count);
6623}
6624#endif
6625
6626#ifdef QT_COMPILER_SUPPORTS_SSE4_1
6627template<QtPixelOrder> void QT_FASTCALL storeA2RGB30PMFromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6628#elif defined(QT_COMPILER_SUPPORTS_LSX)
6629template<QtPixelOrder> void QT_FASTCALL storeA2RGB30PMFromARGB32PM_lsx(uchar *dest, const uint *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6630#endif
6631
6632extern void qInitBlendFunctions();
6633
6635{
6636 // Set up basic blend function tables.
6638
6639#if defined(Q_PROCESSOR_X86) && !defined(__SSE2__)
6640 qt_memfill32 = qt_memfill_template<quint32>;
6641 qt_memfill64 = qt_memfill_template<quint64>;
6642#elif defined(__SSE2__)
6643# ifndef __haswell__
6644 qt_memfill32 = qt_memfill32_sse2;
6645 qt_memfill64 = qt_memfill64_sse2;
6646# endif
6647 qDrawHelper[QImage::Format_RGB32].bitmapBlit = qt_bitmapblit32_sse2;
6648 qDrawHelper[QImage::Format_ARGB32].bitmapBlit = qt_bitmapblit32_sse2;
6649 qDrawHelper[QImage::Format_ARGB32_Premultiplied].bitmapBlit = qt_bitmapblit32_sse2;
6650 qDrawHelper[QImage::Format_RGB16].bitmapBlit = qt_bitmapblit16_sse2;
6651 qDrawHelper[QImage::Format_RGBX8888].bitmapBlit = qt_bitmapblit8888_sse2;
6652 qDrawHelper[QImage::Format_RGBA8888].bitmapBlit = qt_bitmapblit8888_sse2;
6653 qDrawHelper[QImage::Format_RGBA8888_Premultiplied].bitmapBlit = qt_bitmapblit8888_sse2;
6654
6655 extern void qt_scale_image_argb32_on_argb32_sse2(uchar *destPixels, int dbpl,
6656 const uchar *srcPixels, int sbpl, int srch,
6657 const QRectF &targetRect,
6658 const QRectF &sourceRect,
6659 const QRect &clip,
6660 int const_alpha);
6661 qScaleFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_scale_image_argb32_on_argb32_sse2;
6662 qScaleFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_scale_image_argb32_on_argb32_sse2;
6663 qScaleFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_scale_image_argb32_on_argb32_sse2;
6664 qScaleFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_scale_image_argb32_on_argb32_sse2;
6665
6666 extern void qt_blend_rgb32_on_rgb32_sse2(uchar *destPixels, int dbpl,
6667 const uchar *srcPixels, int sbpl,
6668 int w, int h,
6669 int const_alpha);
6670 extern void qt_blend_argb32_on_argb32_sse2(uchar *destPixels, int dbpl,
6671 const uchar *srcPixels, int sbpl,
6672 int w, int h,
6673 int const_alpha);
6674
6675 qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse2;
6676 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse2;
6677 qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse2;
6678 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse2;
6679 qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_sse2;
6680 qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_sse2;
6681 qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_sse2;
6682 qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_sse2;
6683
6684 extern const uint * QT_FASTCALL qt_fetch_radial_gradient_sse2(uint *buffer, const Operator *op, const QSpanData *data,
6685 int y, int x, int length);
6686
6687 qt_fetch_radial_gradient = qt_fetch_radial_gradient_sse2;
6688
6689 extern void QT_FASTCALL comp_func_SourceOver_sse2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
6690 extern void QT_FASTCALL comp_func_solid_SourceOver_sse2(uint *destPixels, int length, uint color, uint const_alpha);
6691 extern void QT_FASTCALL comp_func_Source_sse2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
6692 extern void QT_FASTCALL comp_func_solid_Source_sse2(uint *destPixels, int length, uint color, uint const_alpha);
6693 extern void QT_FASTCALL comp_func_Plus_sse2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
6694 qt_functionForMode_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_sse2;
6695 qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_sse2;
6696 qt_functionForMode_C[QPainter::CompositionMode_Source] = comp_func_Source_sse2;
6697 qt_functionForModeSolid_C[QPainter::CompositionMode_Source] = comp_func_solid_Source_sse2;
6698 qt_functionForMode_C[QPainter::CompositionMode_Plus] = comp_func_Plus_sse2;
6699
6700#ifdef QT_COMPILER_SUPPORTS_SSSE3
6701 if (qCpuHasFeature(SSSE3)) {
6702 extern void qt_blend_argb32_on_argb32_ssse3(uchar *destPixels, int dbpl,
6703 const uchar *srcPixels, int sbpl,
6704 int w, int h,
6705 int const_alpha);
6706
6707 extern const uint * QT_FASTCALL qt_fetchUntransformed_888_ssse3(uint *buffer, const Operator *, const QSpanData *data,
6708 int y, int x, int length);
6709 qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_ssse3;
6710 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_ssse3;
6711 qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_ssse3;
6712 qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_ssse3;
6713 sourceFetchUntransformed[QImage::Format_RGB888] = qt_fetchUntransformed_888_ssse3;
6714 extern void QT_FASTCALL rbSwap_888_ssse3(uchar *dst, const uchar *src, int count);
6715 qPixelLayouts[QImage::Format_RGB888].rbSwap = rbSwap_888_ssse3;
6716 qPixelLayouts[QImage::Format_BGR888].rbSwap = rbSwap_888_ssse3;
6717 }
6718#endif // SSSE3
6719
6720#if defined(QT_COMPILER_SUPPORTS_SSE4_1)
6721 if (qCpuHasFeature(SSE4_1)) {
6722 extern void QT_FASTCALL convertARGB32ToARGB32PM_sse4(uint *buffer, int count, const QList<QRgb> *);
6723 extern void QT_FASTCALL convertRGBA8888ToARGB32PM_sse4(uint *buffer, int count, const QList<QRgb> *);
6724 extern const uint *QT_FASTCALL fetchARGB32ToARGB32PM_sse4(uint *buffer, const uchar *src, int index, int count,
6725 const QList<QRgb> *, QDitherInfo *);
6726 extern const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_sse4(uint *buffer, const uchar *src, int index, int count,
6727 const QList<QRgb> *, QDitherInfo *);
6728 extern const QRgba64 * QT_FASTCALL convertARGB32ToRGBA64PM_sse4(QRgba64 *buffer, const uint *src, int count,
6729 const QList<QRgb> *, QDitherInfo *);
6730 extern const QRgba64 * QT_FASTCALL convertRGBA8888ToRGBA64PM_sse4(QRgba64 *buffer, const uint *src, int count,
6731 const QList<QRgb> *, QDitherInfo *);
6732 extern const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM_sse4(QRgba64 *buffer, const uchar *src, int index, int count,
6733 const QList<QRgb> *, QDitherInfo *);
6734 extern const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM_sse4(QRgba64 *buffer, const uchar *src, int index, int count,
6735 const QList<QRgb> *, QDitherInfo *);
6736 extern void QT_FASTCALL storeARGB32FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count,
6737 const QList<QRgb> *, QDitherInfo *);
6738 extern void QT_FASTCALL storeRGBA8888FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count,
6739 const QList<QRgb> *, QDitherInfo *);
6740 extern void QT_FASTCALL storeRGBXFromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count,
6741 const QList<QRgb> *, QDitherInfo *);
6742 extern void QT_FASTCALL storeARGB32FromRGBA64PM_sse4(uchar *dest, const QRgba64 *src, int index, int count,
6743 const QList<QRgb> *, QDitherInfo *);
6744 extern void QT_FASTCALL storeRGBA8888FromRGBA64PM_sse4(uchar *dest, const QRgba64 *src, int index, int count,
6745 const QList<QRgb> *, QDitherInfo *);
6746 extern void QT_FASTCALL storeRGBA64FromRGBA64PM_sse4(uchar *, const QRgba64 *, int, int, const QList<QRgb> *, QDitherInfo *);
6747 extern void QT_FASTCALL storeRGBx64FromRGBA64PM_sse4(uchar *, const QRgba64 *, int, int, const QList<QRgb> *, QDitherInfo *);
6748 extern void QT_FASTCALL destStore64ARGB32_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length);
6749 extern void QT_FASTCALL destStore64RGBA8888_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length);
6750# ifndef __haswell__
6751 qPixelLayouts[QImage::Format_ARGB32].fetchToARGB32PM = fetchARGB32ToARGB32PM_sse4;
6752 qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_sse4;
6753 qPixelLayouts[QImage::Format_RGBA8888].fetchToARGB32PM = fetchRGBA8888ToARGB32PM_sse4;
6754 qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_sse4;
6755 qPixelLayouts[QImage::Format_ARGB32].fetchToRGBA64PM = fetchARGB32ToRGBA64PM_sse4;
6756 qPixelLayouts[QImage::Format_ARGB32].convertToRGBA64PM = convertARGB32ToRGBA64PM_sse4;
6757 qPixelLayouts[QImage::Format_RGBA8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_sse4;
6758 qPixelLayouts[QImage::Format_RGBA8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_sse4;
6759 qPixelLayouts[QImage::Format_RGBX8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_sse4;
6760 qPixelLayouts[QImage::Format_RGBX8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_sse4;
6761# endif
6762 qPixelLayouts[QImage::Format_ARGB32].storeFromARGB32PM = storeARGB32FromARGB32PM_sse4;
6763 qPixelLayouts[QImage::Format_RGBA8888].storeFromARGB32PM = storeRGBA8888FromARGB32PM_sse4;
6764 qPixelLayouts[QImage::Format_RGBX8888].storeFromARGB32PM = storeRGBXFromARGB32PM_sse4;
6765 qPixelLayouts[QImage::Format_A2BGR30_Premultiplied].storeFromARGB32PM = storeA2RGB30PMFromARGB32PM_sse4<PixelOrderBGR>;
6766 qPixelLayouts[QImage::Format_A2RGB30_Premultiplied].storeFromARGB32PM = storeA2RGB30PMFromARGB32PM_sse4<PixelOrderRGB>;
6767 qStoreFromRGBA64PM[QImage::Format_ARGB32] = storeARGB32FromRGBA64PM_sse4;
6768 qStoreFromRGBA64PM[QImage::Format_RGBA8888] = storeRGBA8888FromRGBA64PM_sse4;
6769 qStoreFromRGBA64PM[QImage::Format_RGBX64] = storeRGBx64FromRGBA64PM_sse4;
6770 qStoreFromRGBA64PM[QImage::Format_RGBA64] = storeRGBA64FromRGBA64PM_sse4;
6771#if QT_CONFIG(raster_64bit)
6772 destStoreProc64[QImage::Format_ARGB32] = destStore64ARGB32_sse4;
6773 destStoreProc64[QImage::Format_RGBA8888] = destStore64RGBA8888_sse4;
6774#endif
6775#if QT_CONFIG(raster_fp)
6776 extern const QRgbaFloat32 *QT_FASTCALL fetchRGBA32FToRGBA32F_sse4(QRgbaFloat32 *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6777 extern void QT_FASTCALL storeRGBX32FFromRGBA32F_sse4(uchar *dest, const QRgbaFloat32 *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6778 extern void QT_FASTCALL storeRGBA32FFromRGBA32F_sse4(uchar *dest, const QRgbaFloat32 *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6779 qFetchToRGBA32F[QImage::Format_RGBA32FPx4] = fetchRGBA32FToRGBA32F_sse4;
6780 qStoreFromRGBA32F[QImage::Format_RGBX32FPx4] = storeRGBX32FFromRGBA32F_sse4;
6781 qStoreFromRGBA32F[QImage::Format_RGBA32FPx4] = storeRGBA32FFromRGBA32F_sse4;
6782#endif // QT_CONFIG(raster_fp)
6783 }
6784#endif
6785
6786#if defined(QT_COMPILER_SUPPORTS_AVX2)
6787 if (qCpuHasFeature(ArchHaswell)) {
6788 qt_memfill32 = qt_memfill32_avx2;
6789 qt_memfill64 = qt_memfill64_avx2;
6790 extern void qt_blend_rgb32_on_rgb32_avx2(uchar *destPixels, int dbpl,
6791 const uchar *srcPixels, int sbpl,
6792 int w, int h, int const_alpha);
6793 extern void qt_blend_argb32_on_argb32_avx2(uchar *destPixels, int dbpl,
6794 const uchar *srcPixels, int sbpl,
6795 int w, int h, int const_alpha);
6796 qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_avx2;
6797 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_avx2;
6798 qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_avx2;
6799 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_avx2;
6800 qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_avx2;
6801 qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_avx2;
6802 qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_avx2;
6803 qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_avx2;
6804
6805 extern void QT_FASTCALL comp_func_Source_avx2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
6806 extern void QT_FASTCALL comp_func_SourceOver_avx2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
6807 extern void QT_FASTCALL comp_func_solid_SourceOver_avx2(uint *destPixels, int length, uint color, uint const_alpha);
6808 qt_functionForMode_C[QPainter::CompositionMode_Source] = comp_func_Source_avx2;
6809 qt_functionForMode_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_avx2;
6810 qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_avx2;
6811#if QT_CONFIG(raster_64bit)
6812 extern void QT_FASTCALL comp_func_Source_rgb64_avx2(QRgba64 *destPixels, const QRgba64 *srcPixels, int length, uint const_alpha);
6813 extern void QT_FASTCALL comp_func_SourceOver_rgb64_avx2(QRgba64 *destPixels, const QRgba64 *srcPixels, int length, uint const_alpha);
6814 extern void QT_FASTCALL comp_func_solid_SourceOver_rgb64_avx2(QRgba64 *destPixels, int length, QRgba64 color, uint const_alpha);
6815 qt_functionForMode64_C[QPainter::CompositionMode_Source] = comp_func_Source_rgb64_avx2;
6816 qt_functionForMode64_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_rgb64_avx2;
6817 qt_functionForModeSolid64_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_rgb64_avx2;
6818#endif
6819#if QT_CONFIG(raster_fp)
6820 extern void QT_FASTCALL comp_func_Source_rgbafp_avx2(QRgbaFloat32 *destPixels, const QRgbaFloat32 *srcPixels, int length, uint const_alpha);
6821 extern void QT_FASTCALL comp_func_SourceOver_rgbafp_avx2(QRgbaFloat32 *destPixels, const QRgbaFloat32 *srcPixels, int length, uint const_alpha);
6822 extern void QT_FASTCALL comp_func_solid_Source_rgbafp_avx2(QRgbaFloat32 *destPixels, int length, QRgbaFloat32 color, uint const_alpha);
6823 extern void QT_FASTCALL comp_func_solid_SourceOver_rgbafp_avx2(QRgbaFloat32 *destPixels, int length, QRgbaFloat32 color, uint const_alpha);
6824 qt_functionForModeFP_C[QPainter::CompositionMode_Source] = comp_func_Source_rgbafp_avx2;
6825 qt_functionForModeFP_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_rgbafp_avx2;
6826 qt_functionForModeSolidFP_C[QPainter::CompositionMode_Source] = comp_func_solid_Source_rgbafp_avx2;
6827 qt_functionForModeSolidFP_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_rgbafp_avx2;
6828#endif
6829
6830 extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_scale_helper_avx2(uint *b, uint *end, const QTextureData &image,
6831 int &fx, int &fy, int fdx, int /*fdy*/);
6832 extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_downscale_helper_avx2(uint *b, uint *end, const QTextureData &image,
6833 int &fx, int &fy, int fdx, int /*fdy*/);
6834 extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_fast_rotate_helper_avx2(uint *b, uint *end, const QTextureData &image,
6835 int &fx, int &fy, int fdx, int fdy);
6836
6837 bilinearFastTransformHelperARGB32PM[0][SimpleScaleTransform] = fetchTransformedBilinearARGB32PM_simple_scale_helper_avx2;
6838 bilinearFastTransformHelperARGB32PM[0][DownscaleTransform] = fetchTransformedBilinearARGB32PM_downscale_helper_avx2;
6839 bilinearFastTransformHelperARGB32PM[0][FastRotateTransform] = fetchTransformedBilinearARGB32PM_fast_rotate_helper_avx2;
6840
6841 extern void QT_FASTCALL convertARGB32ToARGB32PM_avx2(uint *buffer, int count, const QList<QRgb> *);
6842 extern void QT_FASTCALL convertRGBA8888ToARGB32PM_avx2(uint *buffer, int count, const QList<QRgb> *);
6843 extern const uint *QT_FASTCALL fetchARGB32ToARGB32PM_avx2(uint *buffer, const uchar *src, int index, int count,
6844 const QList<QRgb> *, QDitherInfo *);
6845 extern const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_avx2(uint *buffer, const uchar *src, int index, int count,
6846 const QList<QRgb> *, QDitherInfo *);
6847 qPixelLayouts[QImage::Format_ARGB32].fetchToARGB32PM = fetchARGB32ToARGB32PM_avx2;
6848 qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_avx2;
6849 qPixelLayouts[QImage::Format_RGBA8888].fetchToARGB32PM = fetchRGBA8888ToARGB32PM_avx2;
6850 qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_avx2;
6851
6852 extern const QRgba64 *QT_FASTCALL convertARGB32ToRGBA64PM_avx2(QRgba64 *, const uint *, int, const QList<QRgb> *, QDitherInfo *);
6853 extern const QRgba64 *QT_FASTCALL convertRGBA8888ToRGBA64PM_avx2(QRgba64 *, const uint *, int count, const QList<QRgb> *, QDitherInfo *);
6854 extern const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM_avx2(QRgba64 *, const uchar *, int, int, const QList<QRgb> *, QDitherInfo *);
6855 extern const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM_avx2(QRgba64 *, const uchar *, int, int, const QList<QRgb> *, QDitherInfo *);
6856 extern const QRgba64 *QT_FASTCALL fetchRGBA64ToRGBA64PM_avx2(QRgba64 *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6857 qPixelLayouts[QImage::Format_ARGB32].convertToRGBA64PM = convertARGB32ToRGBA64PM_avx2;
6858 qPixelLayouts[QImage::Format_RGBX8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_avx2;
6859 qPixelLayouts[QImage::Format_ARGB32].fetchToRGBA64PM = fetchARGB32ToRGBA64PM_avx2;
6860 qPixelLayouts[QImage::Format_RGBX8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_avx2;
6861 qPixelLayouts[QImage::Format_RGBA64].fetchToRGBA64PM = fetchRGBA64ToRGBA64PM_avx2;
6862
6863 extern const uint *QT_FASTCALL fetchRGB16FToRGB32_avx2(uint *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6864 extern const uint *QT_FASTCALL fetchRGBA16FToARGB32PM_avx2(uint *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6865 extern const QRgba64 *QT_FASTCALL fetchRGBA16FPMToRGBA64PM_avx2(QRgba64 *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6866 extern const QRgba64 *QT_FASTCALL fetchRGBA16FToRGBA64PM_avx2(QRgba64 *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6867 extern void QT_FASTCALL storeRGB16FFromRGB32_avx2(uchar *dest, const uint *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6868 extern void QT_FASTCALL storeRGBA16FFromARGB32PM_avx2(uchar *dest, const uint *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6869 qPixelLayouts[QImage::Format_RGBX16FPx4].fetchToARGB32PM = fetchRGB16FToRGB32_avx2;
6870 qPixelLayouts[QImage::Format_RGBX16FPx4].fetchToRGBA64PM = fetchRGBA16FPMToRGBA64PM_avx2;
6871 qPixelLayouts[QImage::Format_RGBX16FPx4].storeFromARGB32PM = storeRGB16FFromRGB32_avx2;
6872 qPixelLayouts[QImage::Format_RGBX16FPx4].storeFromRGB32 = storeRGB16FFromRGB32_avx2;
6873 qPixelLayouts[QImage::Format_RGBA16FPx4].fetchToARGB32PM = fetchRGBA16FToARGB32PM_avx2;
6874 qPixelLayouts[QImage::Format_RGBA16FPx4].fetchToRGBA64PM = fetchRGBA16FToRGBA64PM_avx2;
6875 qPixelLayouts[QImage::Format_RGBA16FPx4].storeFromARGB32PM = storeRGBA16FFromARGB32PM_avx2;
6876 qPixelLayouts[QImage::Format_RGBA16FPx4].storeFromRGB32 = storeRGB16FFromRGB32_avx2;
6877 qPixelLayouts[QImage::Format_RGBA16FPx4_Premultiplied].fetchToARGB32PM = fetchRGB16FToRGB32_avx2;
6878 qPixelLayouts[QImage::Format_RGBA16FPx4_Premultiplied].fetchToRGBA64PM = fetchRGBA16FPMToRGBA64PM_avx2;
6879 qPixelLayouts[QImage::Format_RGBA16FPx4_Premultiplied].storeFromARGB32PM = storeRGB16FFromRGB32_avx2;
6880 qPixelLayouts[QImage::Format_RGBA16FPx4_Premultiplied].storeFromRGB32 = storeRGB16FFromRGB32_avx2;
6881#if QT_CONFIG(raster_fp)
6882 extern const QRgbaFloat32 *QT_FASTCALL fetchRGBA16FToRGBA32F_avx2(QRgbaFloat32 *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6883 extern void QT_FASTCALL storeRGBX16FFromRGBA32F_avx2(uchar *dest, const QRgbaFloat32 *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6884 extern void QT_FASTCALL storeRGBA16FFromRGBA32F_avx2(uchar *dest, const QRgbaFloat32 *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6885 qFetchToRGBA32F[QImage::Format_RGBA16FPx4] = fetchRGBA16FToRGBA32F_avx2;
6886 qStoreFromRGBA32F[QImage::Format_RGBX16FPx4] = storeRGBX16FFromRGBA32F_avx2;
6887 qStoreFromRGBA32F[QImage::Format_RGBA16FPx4] = storeRGBA16FFromRGBA32F_avx2;
6888#endif // QT_CONFIG(raster_fp)
6889 }
6890
6891#endif
6892
6893#endif // SSE2
6894
6895#if defined(QT_COMPILER_SUPPORTS_LSX)
6896 if (qCpuHasFeature(LSX)) {
6897 qt_memfill32 = qt_memfill32_lsx;
6898 qt_memfill64 = qt_memfill64_lsx;
6899
6900 qDrawHelper[QImage::Format_RGB32].bitmapBlit = qt_bitmapblit32_lsx;
6901 qDrawHelper[QImage::Format_ARGB32].bitmapBlit = qt_bitmapblit32_lsx;
6902 qDrawHelper[QImage::Format_ARGB32_Premultiplied].bitmapBlit = qt_bitmapblit32_lsx;
6903 qDrawHelper[QImage::Format_RGB16].bitmapBlit = qt_bitmapblit16_lsx;
6904 qDrawHelper[QImage::Format_RGBX8888].bitmapBlit = qt_bitmapblit8888_lsx;
6905 qDrawHelper[QImage::Format_RGBA8888].bitmapBlit = qt_bitmapblit8888_lsx;
6906 qDrawHelper[QImage::Format_RGBA8888_Premultiplied].bitmapBlit = qt_bitmapblit8888_lsx;
6907
6908 extern void qt_scale_image_argb32_on_argb32_lsx(uchar *destPixels, int dbpl,
6909 const uchar *srcPixels, int sbpl, int srch,
6910 const QRectF &targetRect,
6911 const QRectF &sourceRect,
6912 const QRect &clip,
6913 int const_alpha);
6914
6915 qScaleFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_scale_image_argb32_on_argb32_lsx;
6916 qScaleFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_scale_image_argb32_on_argb32_lsx;
6917 qScaleFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_scale_image_argb32_on_argb32_lsx;
6918 qScaleFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_scale_image_argb32_on_argb32_lsx;
6919
6920 extern void qt_blend_rgb32_on_rgb32_lsx(uchar *destPixels, int dbpl,
6921 const uchar *srcPixels, int sbpl,
6922 int w, int h,
6923 int const_alpha);
6924
6925 extern void qt_blend_argb32_on_argb32_lsx(uchar *destPixels, int dbpl,
6926 const uchar *srcPixels, int sbpl,
6927 int w, int h,
6928 int const_alpha);
6929
6930 qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_lsx;
6931 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_lsx;
6932 qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_lsx;
6933 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_lsx;
6934 qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_lsx;
6935 qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_lsx;
6936 qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_lsx;
6937 qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_lsx;
6938
6939 extern const uint * QT_FASTCALL qt_fetch_radial_gradient_lsx(uint *buffer, const Operator *op, const QSpanData *data,
6940 int y, int x, int length);
6941
6942 qt_fetch_radial_gradient = qt_fetch_radial_gradient_lsx;
6943
6944 extern void QT_FASTCALL comp_func_SourceOver_lsx(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
6945 extern void QT_FASTCALL comp_func_solid_SourceOver_lsx(uint *destPixels, int length, uint color, uint const_alpha);
6946 extern void QT_FASTCALL comp_func_Source_lsx(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
6947 extern void QT_FASTCALL comp_func_solid_Source_lsx(uint *destPixels, int length, uint color, uint const_alpha);
6948 extern void QT_FASTCALL comp_func_Plus_lsx(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
6949 qt_functionForMode_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_lsx;
6950 qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_lsx;
6951 qt_functionForMode_C[QPainter::CompositionMode_Source] = comp_func_Source_lsx;
6952 qt_functionForModeSolid_C[QPainter::CompositionMode_Source] = comp_func_solid_Source_lsx;
6953 qt_functionForMode_C[QPainter::CompositionMode_Plus] = comp_func_Plus_lsx;
6954
6955 extern const uint * QT_FASTCALL qt_fetchUntransformed_888_lsx(uint *buffer, const Operator *, const QSpanData *data,
6956 int y, int x, int length);
6957 sourceFetchUntransformed[QImage::Format_RGB888] = qt_fetchUntransformed_888_lsx;
6958 extern void QT_FASTCALL rbSwap_888_lsx(uchar *dst, const uchar *src, int count);
6959 qPixelLayouts[QImage::Format_RGB888].rbSwap = rbSwap_888_lsx;
6960 qPixelLayouts[QImage::Format_BGR888].rbSwap = rbSwap_888_lsx;
6961
6962 extern void QT_FASTCALL convertARGB32ToARGB32PM_lsx(uint *buffer, int count, const QList<QRgb> *);
6963 extern void QT_FASTCALL convertRGBA8888ToARGB32PM_lsx(uint *buffer, int count, const QList<QRgb> *);
6964 extern const uint *QT_FASTCALL fetchARGB32ToARGB32PM_lsx(uint *buffer, const uchar *src, int index, int count,
6965 const QList<QRgb> *, QDitherInfo *);
6966 extern const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_lsx(uint *buffer, const uchar *src, int index, int count,
6967 const QList<QRgb> *, QDitherInfo *);
6968 extern const QRgba64 * QT_FASTCALL convertARGB32ToRGBA64PM_lsx(QRgba64 *buffer, const uint *src, int count,
6969 const QList<QRgb> *, QDitherInfo *);
6970 extern const QRgba64 * QT_FASTCALL convertRGBA8888ToRGBA64PM_lsx(QRgba64 *buffer, const uint *src, int count,
6971 const QList<QRgb> *, QDitherInfo *);
6972 extern const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM_lsx(QRgba64 *buffer, const uchar *src, int index, int count,
6973 const QList<QRgb> *, QDitherInfo *);
6974 extern const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM_lsx(QRgba64 *buffer, const uchar *src, int index, int count,
6975 const QList<QRgb> *, QDitherInfo *);
6976 extern void QT_FASTCALL storeARGB32FromARGB32PM_lsx(uchar *dest, const uint *src, int index, int count,
6977 const QList<QRgb> *, QDitherInfo *);
6978 extern void QT_FASTCALL storeRGBA8888FromARGB32PM_lsx(uchar *dest, const uint *src, int index, int count,
6979 const QList<QRgb> *, QDitherInfo *);
6980 extern void QT_FASTCALL storeRGBXFromARGB32PM_lsx(uchar *dest, const uint *src, int index, int count,
6981 const QList<QRgb> *, QDitherInfo *);
6982 extern void QT_FASTCALL storeARGB32FromRGBA64PM_lsx(uchar *dest, const QRgba64 *src, int index, int count,
6983 const QList<QRgb> *, QDitherInfo *);
6984 extern void QT_FASTCALL storeRGBA8888FromRGBA64PM_lsx(uchar *dest, const QRgba64 *src, int index, int count,
6985 const QList<QRgb> *, QDitherInfo *);
6986 extern void QT_FASTCALL destStore64ARGB32_lsx(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length);
6987 extern void QT_FASTCALL destStore64RGBA8888_lsx(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length);
6988 extern void QT_FASTCALL storeRGBA64FromRGBA64PM_lsx(uchar *, const QRgba64 *, int, int, const QList<QRgb> *, QDitherInfo *);
6989 extern void QT_FASTCALL storeRGBx64FromRGBA64PM_lsx(uchar *, const QRgba64 *, int, int, const QList<QRgb> *, QDitherInfo *);
6990 qPixelLayouts[QImage::Format_ARGB32].fetchToARGB32PM = fetchARGB32ToARGB32PM_lsx;
6991 qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_lsx;
6992 qPixelLayouts[QImage::Format_RGBA8888].fetchToARGB32PM = fetchRGBA8888ToARGB32PM_lsx;
6993 qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_lsx;
6994 qPixelLayouts[QImage::Format_ARGB32].fetchToRGBA64PM = fetchARGB32ToRGBA64PM_lsx;
6995 qPixelLayouts[QImage::Format_ARGB32].convertToRGBA64PM = convertARGB32ToRGBA64PM_lsx;
6996 qPixelLayouts[QImage::Format_RGBA8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_lsx;
6997 qPixelLayouts[QImage::Format_RGBA8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_lsx;
6998 qPixelLayouts[QImage::Format_RGBX8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_lsx;
6999 qPixelLayouts[QImage::Format_RGBX8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_lsx;
7000 qPixelLayouts[QImage::Format_ARGB32].storeFromARGB32PM = storeARGB32FromARGB32PM_lsx;
7001 qPixelLayouts[QImage::Format_RGBA8888].storeFromARGB32PM = storeRGBA8888FromARGB32PM_lsx;
7002 qPixelLayouts[QImage::Format_RGBX8888].storeFromARGB32PM = storeRGBXFromARGB32PM_lsx;
7003 qPixelLayouts[QImage::Format_A2BGR30_Premultiplied].storeFromARGB32PM = storeA2RGB30PMFromARGB32PM_lsx<PixelOrderBGR>;
7004 qPixelLayouts[QImage::Format_A2RGB30_Premultiplied].storeFromARGB32PM = storeA2RGB30PMFromARGB32PM_lsx<PixelOrderRGB>;
7005 qStoreFromRGBA64PM[QImage::Format_ARGB32] = storeARGB32FromRGBA64PM_lsx;
7006 qStoreFromRGBA64PM[QImage::Format_RGBA8888] = storeRGBA8888FromRGBA64PM_lsx;
7007 qStoreFromRGBA64PM[QImage::Format_RGBX64] = storeRGBx64FromRGBA64PM_lsx;
7008 qStoreFromRGBA64PM[QImage::Format_RGBA64] = storeRGBA64FromRGBA64PM_lsx;
7009#if QT_CONFIG(raster_64bit)
7010 destStoreProc64[QImage::Format_ARGB32] = destStore64ARGB32_lsx;
7011 destStoreProc64[QImage::Format_RGBA8888] = destStore64RGBA8888_lsx;
7012#endif
7013#if QT_CONFIG(raster_fp)
7014 extern const QRgbaFloat32 *QT_FASTCALL fetchRGBA32FToRGBA32F_lsx(QRgbaFloat32 *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
7015 extern void QT_FASTCALL storeRGBX32FFromRGBA32F_lsx(uchar *dest, const QRgbaFloat32 *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
7016 extern void QT_FASTCALL storeRGBA32FFromRGBA32F_lsx(uchar *dest, const QRgbaFloat32 *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
7017 qFetchToRGBA32F[QImage::Format_RGBA32FPx4] = fetchRGBA32FToRGBA32F_lsx;
7018 qStoreFromRGBA32F[QImage::Format_RGBX32FPx4] = storeRGBX32FFromRGBA32F_lsx;
7019 qStoreFromRGBA32F[QImage::Format_RGBA32FPx4] = storeRGBA32FFromRGBA32F_lsx;
7020#endif // QT_CONFIG(raster_fp)
7021 }
7022
7023#if defined(QT_COMPILER_SUPPORTS_LASX)
7024 if (qCpuHasFeature(LASX)) {
7025 qt_memfill32 = qt_memfill32_lasx;
7026 qt_memfill64 = qt_memfill64_lasx;
7027
7028 extern void qt_blend_rgb32_on_rgb32_lasx(uchar *destPixels, int dbpl,
7029 const uchar *srcPixels, int sbpl,
7030 int w, int h, int const_alpha);
7031 extern void qt_blend_argb32_on_argb32_lasx(uchar *destPixels, int dbpl,
7032 const uchar *srcPixels, int sbpl,
7033 int w, int h, int const_alpha);
7034 qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_lasx;
7035 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_lasx;
7036 qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_lasx;
7037 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_lasx;
7038 qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_lasx;
7039 qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_lasx;
7040 qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_lasx;
7041 qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_lasx;
7042
7043 extern void QT_FASTCALL comp_func_Source_lasx(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
7044 extern void QT_FASTCALL comp_func_SourceOver_lasx(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
7045 extern void QT_FASTCALL comp_func_solid_SourceOver_lasx(uint *destPixels, int length, uint color, uint const_alpha);
7046 qt_functionForMode_C[QPainter::CompositionMode_Source] = comp_func_Source_lasx;
7047 qt_functionForMode_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_lasx;
7048 qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_lasx;
7049#if QT_CONFIG(raster_64bit)
7050 extern void QT_FASTCALL comp_func_Source_rgb64_lasx(QRgba64 *destPixels, const QRgba64 *srcPixels, int length, uint const_alpha);
7051 extern void QT_FASTCALL comp_func_solid_SourceOver_rgb64_lasx(QRgba64 *destPixels, int length, QRgba64 color, uint const_alpha);
7052 qt_functionForMode64_C[QPainter::CompositionMode_Source] = comp_func_Source_rgb64_lasx;
7053 qt_functionForModeSolid64_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_rgb64_lasx;
7054#endif
7055
7056 extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_scale_helper_lasx(uint *b, uint *end, const QTextureData &image,
7057 int &fx, int &fy, int fdx, int /*fdy*/);
7058 extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_downscale_helper_lasx(uint *b, uint *end, const QTextureData &image,
7059 int &fx, int &fy, int fdx, int /*fdy*/);
7060 extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_fast_rotate_helper_lasx(uint *b, uint *end, const QTextureData &image,
7061 int &fx, int &fy, int fdx, int fdy);
7062
7063 bilinearFastTransformHelperARGB32PM[0][SimpleScaleTransform] = fetchTransformedBilinearARGB32PM_simple_scale_helper_lasx;
7064 bilinearFastTransformHelperARGB32PM[0][DownscaleTransform] = fetchTransformedBilinearARGB32PM_downscale_helper_lasx;
7065 bilinearFastTransformHelperARGB32PM[0][FastRotateTransform] = fetchTransformedBilinearARGB32PM_fast_rotate_helper_lasx;
7066
7067 extern void QT_FASTCALL convertARGB32ToARGB32PM_lasx(uint *buffer, int count, const QList<QRgb> *);
7068 extern void QT_FASTCALL convertRGBA8888ToARGB32PM_lasx(uint *buffer, int count, const QList<QRgb> *);
7069 extern const uint *QT_FASTCALL fetchARGB32ToARGB32PM_lasx(uint *buffer, const uchar *src, int index, int count,
7070 const QList<QRgb> *, QDitherInfo *);
7071 extern const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_lasx(uint *buffer, const uchar *src, int index, int count,
7072 const QList<QRgb> *, QDitherInfo *);
7073 qPixelLayouts[QImage::Format_ARGB32].fetchToARGB32PM = fetchARGB32ToARGB32PM_lasx;
7074 qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_lasx;
7075 qPixelLayouts[QImage::Format_RGBA8888].fetchToARGB32PM = fetchRGBA8888ToARGB32PM_lasx;
7076 qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_lasx;
7077
7078 extern const QRgba64 * QT_FASTCALL convertARGB32ToRGBA64PM_lasx(QRgba64 *, const uint *, int, const QList<QRgb> *, QDitherInfo *);
7079 extern const QRgba64 * QT_FASTCALL convertRGBA8888ToRGBA64PM_lasx(QRgba64 *, const uint *, int count, const QList<QRgb> *, QDitherInfo *);
7080 extern const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM_lasx(QRgba64 *, const uchar *, int, int, const QList<QRgb> *, QDitherInfo *);
7081 extern const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM_lasx(QRgba64 *, const uchar *, int, int, const QList<QRgb> *, QDitherInfo *);
7082 qPixelLayouts[QImage::Format_ARGB32].convertToRGBA64PM = convertARGB32ToRGBA64PM_lasx;
7083 qPixelLayouts[QImage::Format_RGBX8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_lasx;
7084 qPixelLayouts[QImage::Format_ARGB32].fetchToRGBA64PM = fetchARGB32ToRGBA64PM_lasx;
7085 qPixelLayouts[QImage::Format_RGBX8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_lasx;
7086 }
7087#endif //QT_COMPILER_SUPPORTS_LASX
7088
7089#endif //QT_COMPILER_SUPPORTS_LSX
7090
7091#if defined(__ARM_NEON__)
7092 qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_neon;
7093 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_neon;
7094 qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_neon;
7095 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_neon;
7096#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
7097 qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_neon;
7098 qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_neon;
7099 qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_neon;
7100 qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_neon;
7101#endif
7102
7103 qt_functionForMode_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_neon;
7104 qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_neon;
7105 qt_functionForMode_C[QPainter::CompositionMode_Plus] = comp_func_Plus_neon;
7106
7107 extern const uint * QT_FASTCALL qt_fetch_radial_gradient_neon(uint *buffer, const Operator *op, const QSpanData *data,
7108 int y, int x, int length);
7109
7110 qt_fetch_radial_gradient = qt_fetch_radial_gradient_neon;
7111
7112 sourceFetchUntransformed[QImage::Format_RGB888] = qt_fetchUntransformed_888_neon;
7113
7114#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
7115 extern void QT_FASTCALL convertARGB32ToARGB32PM_neon(uint *buffer, int count, const QList<QRgb> *);
7116 extern void QT_FASTCALL convertRGBA8888ToARGB32PM_neon(uint *buffer, int count, const QList<QRgb> *);
7117 extern const uint *QT_FASTCALL fetchARGB32ToARGB32PM_neon(uint *buffer, const uchar *src, int index, int count,
7118 const QList<QRgb> *, QDitherInfo *);
7119 extern const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_neon(uint *buffer, const uchar *src, int index, int count,
7120 const QList<QRgb> *, QDitherInfo *);
7121 extern const QRgba64 * QT_FASTCALL convertARGB32ToRGBA64PM_neon(QRgba64 *buffer, const uint *src, int count,
7122 const QList<QRgb> *, QDitherInfo *);
7123 extern const QRgba64 * QT_FASTCALL convertRGBA8888ToRGBA64PM_neon(QRgba64 *buffer, const uint *src, int count,
7124 const QList<QRgb> *, QDitherInfo *);
7125 extern const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM_neon(QRgba64 *buffer, const uchar *src, int index, int count,
7126 const QList<QRgb> *, QDitherInfo *);
7127 extern const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM_neon(QRgba64 *buffer, const uchar *src, int index, int count,
7128 const QList<QRgb> *, QDitherInfo *);
7129 extern void QT_FASTCALL storeARGB32FromARGB32PM_neon(uchar *dest, const uint *src, int index, int count,
7130 const QList<QRgb> *, QDitherInfo *);
7131 extern void QT_FASTCALL storeRGBA8888FromARGB32PM_neon(uchar *dest, const uint *src, int index, int count,
7132 const QList<QRgb> *, QDitherInfo *);
7133 extern void QT_FASTCALL storeRGBXFromARGB32PM_neon(uchar *dest, const uint *src, int index, int count,
7134 const QList<QRgb> *, QDitherInfo *);
7135 qPixelLayouts[QImage::Format_ARGB32].fetchToARGB32PM = fetchARGB32ToARGB32PM_neon;
7136 qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_neon;
7137 qPixelLayouts[QImage::Format_ARGB32].storeFromARGB32PM = storeARGB32FromARGB32PM_neon;
7138 qPixelLayouts[QImage::Format_ARGB32].fetchToRGBA64PM = fetchARGB32ToRGBA64PM_neon;
7139 qPixelLayouts[QImage::Format_ARGB32].convertToRGBA64PM = convertARGB32ToRGBA64PM_neon;
7140 qPixelLayouts[QImage::Format_RGBA8888].fetchToARGB32PM = fetchRGBA8888ToARGB32PM_neon;
7141 qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_neon;
7142 qPixelLayouts[QImage::Format_RGBA8888].storeFromARGB32PM = storeRGBA8888FromARGB32PM_neon;
7143 qPixelLayouts[QImage::Format_RGBA8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_neon;
7144 qPixelLayouts[QImage::Format_RGBA8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_neon;
7145 qPixelLayouts[QImage::Format_RGBX8888].storeFromARGB32PM = storeRGBXFromARGB32PM_neon;
7146 qPixelLayouts[QImage::Format_RGBX8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_neon;
7147 qPixelLayouts[QImage::Format_RGBX8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_neon;
7148#endif
7149
7150#if defined(ENABLE_PIXMAN_DRAWHELPERS)
7151 // The RGB16 helpers are using Arm32 assemblythat has not been ported to AArch64
7152 qBlendFunctions[QImage::Format_RGB16][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_rgb16_neon;
7153 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB16] = qt_blend_rgb16_on_argb32_neon;
7154 qBlendFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_blend_rgb16_on_rgb16_neon;
7155
7156 qScaleFunctions[QImage::Format_RGB16][QImage::Format_ARGB32_Premultiplied] = qt_scale_image_argb32_on_rgb16_neon;
7157 qScaleFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_scale_image_rgb16_on_rgb16_neon;
7158
7159 qTransformFunctions[QImage::Format_RGB16][QImage::Format_ARGB32_Premultiplied] = qt_transform_image_argb32_on_rgb16_neon;
7160 qTransformFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_transform_image_rgb16_on_rgb16_neon;
7161
7162 qDrawHelper[QImage::Format_RGB16].alphamapBlit = qt_alphamapblit_quint16_neon;
7163
7164 destFetchProc[QImage::Format_RGB16] = qt_destFetchRGB16_neon;
7165 destStoreProc[QImage::Format_RGB16] = qt_destStoreRGB16_neon;
7166
7167 qMemRotateFunctions[QPixelLayout::BPP16][0] = qt_memrotate90_16_neon;
7168 qMemRotateFunctions[QPixelLayout::BPP16][2] = qt_memrotate270_16_neon;
7169#endif
7170#endif // defined(__ARM_NEON__)
7171
7172#if defined(__MIPS_DSP__)
7173 // Composition functions are all DSP r1
7174 qt_functionForMode_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_asm_mips_dsp;
7175 qt_functionForMode_C[QPainter::CompositionMode_Source] = comp_func_Source_mips_dsp;
7176 qt_functionForMode_C[QPainter::CompositionMode_DestinationOver] = comp_func_DestinationOver_mips_dsp;
7177 qt_functionForMode_C[QPainter::CompositionMode_SourceIn] = comp_func_SourceIn_mips_dsp;
7178 qt_functionForMode_C[QPainter::CompositionMode_DestinationIn] = comp_func_DestinationIn_mips_dsp;
7179 qt_functionForMode_C[QPainter::CompositionMode_DestinationOut] = comp_func_DestinationOut_mips_dsp;
7180 qt_functionForMode_C[QPainter::CompositionMode_SourceAtop] = comp_func_SourceAtop_mips_dsp;
7181 qt_functionForMode_C[QPainter::CompositionMode_DestinationAtop] = comp_func_DestinationAtop_mips_dsp;
7182 qt_functionForMode_C[QPainter::CompositionMode_Xor] = comp_func_XOR_mips_dsp;
7183 qt_functionForMode_C[QPainter::CompositionMode_SourceOut] = comp_func_SourceOut_mips_dsp;
7184
7185 qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_mips_dsp;
7186 qt_functionForModeSolid_C[QPainter::CompositionMode_DestinationOver] = comp_func_solid_DestinationOver_mips_dsp;
7187 qt_functionForModeSolid_C[QPainter::CompositionMode_SourceIn] = comp_func_solid_SourceIn_mips_dsp;
7188 qt_functionForModeSolid_C[QPainter::CompositionMode_DestinationIn] = comp_func_solid_DestinationIn_mips_dsp;
7189 qt_functionForModeSolid_C[QPainter::CompositionMode_SourceAtop] = comp_func_solid_SourceAtop_mips_dsp;
7190 qt_functionForModeSolid_C[QPainter::CompositionMode_DestinationAtop] = comp_func_solid_DestinationAtop_mips_dsp;
7191 qt_functionForModeSolid_C[QPainter::CompositionMode_Xor] = comp_func_solid_XOR_mips_dsp;
7192 qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOut] = comp_func_solid_SourceOut_mips_dsp;
7193
7194 qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_mips_dsp;
7195 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_mips_dsp;
7196 qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_mips_dsp;
7197 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_mips_dsp;
7198
7199 destFetchProc[QImage::Format_ARGB32] = qt_destFetchARGB32_mips_dsp;
7200
7201 destStoreProc[QImage::Format_ARGB32] = qt_destStoreARGB32_mips_dsp;
7202
7203 sourceFetchUntransformed[QImage::Format_RGB888] = qt_fetchUntransformed_888_mips_dsp;
7204 sourceFetchUntransformed[QImage::Format_RGB444] = qt_fetchUntransformed_444_mips_dsp;
7205 sourceFetchUntransformed[QImage::Format_ARGB8565_Premultiplied] = qt_fetchUntransformed_argb8565_premultiplied_mips_dsp;
7206
7207#if defined(__MIPS_DSPR2__)
7208 qBlendFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_blend_rgb16_on_rgb16_mips_dspr2;
7209 sourceFetchUntransformed[QImage::Format_RGB16] = qt_fetchUntransformedRGB16_mips_dspr2;
7210#else
7211 qBlendFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_blend_rgb16_on_rgb16_mips_dsp;
7212#endif // defined(__MIPS_DSPR2__)
7213#endif // defined(__MIPS_DSP__)
7214}
7215
7216// Ensure initialization if this object file is linked.
7218
7219QT_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)
#define QT_THREAD_PARALLEL_FILLS(function, minPixels)
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)
uint(QT_FASTCALL * Fetch1PixelFunc)(const uchar *src, int index)
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