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