Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
5#include "qdrawhelper_p.h"
6
7#include <qstylehints.h>
8#include <qguiapplication.h>
9#include <qatomic.h>
10#include <private/qcolortransform_p.h>
11#include <private/qcolortrclut_p.h>
12#include <private/qdrawhelper_p.h>
13#include <private/qdrawhelper_x86_p.h>
14#include <private/qdrawingprimitive_sse2_p.h>
15#include <private/qdrawhelper_neon_p.h>
16#if defined(QT_COMPILER_SUPPORTS_MIPS_DSP) || defined(QT_COMPILER_SUPPORTS_MIPS_DSPR2)
17#include <private/qdrawhelper_mips_dsp_p.h>
18#endif
19#include <private/qguiapplication_p.h>
20#include <private/qpaintengine_raster_p.h>
21#include <private/qpainter_p.h>
22#include <private/qpixellayout_p.h>
23#include <private/qrgba64_p.h>
24#include <qendian.h>
25#include <qloggingcategory.h>
26#include <qmath.h>
27
28#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
29#define QT_USE_THREAD_PARALLEL_FILLS
30#endif
31
32#if defined(QT_USE_THREAD_PARALLEL_FILLS)
33#include <qsemaphore.h>
34#include <qthreadpool.h>
35#include <private/qthreadpool_p.h>
36#endif
37
39
40Q_LOGGING_CATEGORY(lcQtGuiDrawHelper, "qt.gui.drawhelper")
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 <>
59{
60 return (src[index >> 3] >> (index & 7)) & 1;
61}
62
63template <>
65{
66 return (src[index >> 3] >> (~index & 7)) & 1;
67}
68
69template <>
71{
72 return src[index];
73}
74
75template <>
77{
78 return reinterpret_cast<const quint16 *>(src)[index];
79}
80
81template <>
83{
84 return reinterpret_cast<const quint24 *>(src)[index];
85}
86
87template <>
89{
90 return reinterpret_cast<const uint *>(src)[index];
91}
92
93template <>
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 <>
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 <>
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
118
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)
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
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*/
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)
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();
646
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();
667
668 QRgba64 tmp_line[BufferSize];
669 for (int k = 0; k < length; ++k)
670 tmp_line[k] = QRgba64::fromArgb32(buffer[k]);
671 tfd->applyReturnGray(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();
750
751 quint16 gray_line[BufferSize];
752 tfd->applyReturnGray(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();
775 }
776}
777
778static DestStoreProc64 destStoreProc64[] =
779{
780 nullptr, // Format_Invalid
781 nullptr, // Format_Mono,
782 nullptr, // Format_MonoLSB
783 nullptr, // Format_Indexed8
784 destStore64, // Format_RGB32
785 destStore64, // Format_ARGB32,
786 destStore64, // Format_ARGB32_Premultiplied
787 destStore64, // Format_RGB16
788 destStore64, // Format_ARGB8565_Premultiplied
789 destStore64, // Format_RGB666
790 destStore64, // Format_ARGB6666_Premultiplied
791 destStore64, // Format_RGB555
792 destStore64, // Format_ARGB8555_Premultiplied
793 destStore64, // Format_RGB888
794 destStore64, // Format_RGB444
795 destStore64, // Format_ARGB4444_Premultiplied
796 destStore64, // Format_RGBX8888
797 destStore64, // Format_RGBA8888
798 destStore64, // Format_RGBA8888_Premultiplied
799 destStore64, // Format_BGR30
800 destStore64, // Format_A2BGR30_Premultiplied
801 destStore64, // Format_RGB30
802 destStore64, // Format_A2RGB30_Premultiplied
803 destStore64, // Format_Alpha8
804 destStore64Gray8, // Format_Grayscale8
805 nullptr, // Format_RGBX64
806 destStore64RGBA64, // Format_RGBA64
807 nullptr, // Format_RGBA64_Premultiplied
808 destStore64Gray16, // Format_Grayscale16
809 destStore64, // Format_BGR888
810 destStore64, // Format_RGBX16FPx4
811 destStore64, // Format_RGBA16FPx4
812 destStore64, // Format_RGBA16FPx4_Premultiplied
813 destStore64, // Format_RGBX32FPx4
814 destStore64, // Format_RGBA32FPx4
815 destStore64, // Format_RGBA32FPx4_Premultiplied
816 destStore64, // Format_CMYK8888
817};
818
819static_assert(std::size(destStoreProc64) == QImage::NImageFormats);
820#endif
821
822#if QT_CONFIG(raster_fp)
823static void QT_FASTCALL destStoreFP(QRasterBuffer *rasterBuffer, int x, int y, const QRgbaFloat32 *buffer, int length)
824{
825 auto store = qStoreFromRGBA32F[rasterBuffer->format];
826 uchar *dest = rasterBuffer->scanLine(y);
827 store(dest, buffer, x, length, nullptr, nullptr);
828}
829#endif
830
831/*
832 Source fetches
833
834 This is a bit more complicated, as we need several fetch routines for every surface type
835
836 We need 5 fetch methods per surface type:
837 untransformed
838 transformed (tiled and not tiled)
839 transformed bilinear (tiled and not tiled)
840
841 We don't need bounds checks for untransformed, but we need them for the other ones.
842
843 The generic implementation does pixel by pixel fetches
844*/
845
855
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
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
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>
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
1100template<TextureBlendType blendType, QPixelLayout::BPP bpp>
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 uint buffer32[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])
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 uint buffer32[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 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
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
1252template<TextureBlendType blendType>
1253void fetchTransformedBilinear_pixelBounds(int max, int l1, int l2, int &v1, int &v2);
1254
1255template<>
1257{
1258 v1 %= max;
1259 if (v1 < 0)
1260 v1 += max;
1261 v2 = v1 + 1;
1262 if (v2 == max)
1263 v2 = 0;
1264 Q_ASSERT(v1 >= 0 && v1 < max);
1265 Q_ASSERT(v2 >= 0 && v2 < max);
1266}
1267
1268template<>
1270{
1271 if (v1 < l1)
1272 v2 = v1 = l1;
1273 else if (v1 >= l2)
1274 v2 = v1 = l2;
1275 else
1276 v2 = v1 + 1;
1277 Q_ASSERT(v1 >= l1 && v1 <= l2);
1278 Q_ASSERT(v2 >= l1 && v2 <= l2);
1279}
1280
1289
1290// Completes the partial interpolation stored in IntermediateBuffer.
1291// by performing the x-axis interpolation and joining the RB and AG buffers.
1292static void QT_FASTCALL intermediate_adder(uint *b, uint *end, const IntermediateBuffer &intermediate, int offset, int &fx, int fdx)
1293{
1294#if defined(QT_COMPILER_SUPPORTS_AVX2)
1295 extern void QT_FASTCALL intermediate_adder_avx2(uint *b, uint *end, const IntermediateBuffer &intermediate, int offset, int &fx, int fdx);
1296 if (qCpuHasFeature(ArchHaswell))
1297 return intermediate_adder_avx2(b, end, intermediate, offset, fx, fdx);
1298#endif
1299
1300 // Switch to intermediate buffer coordinates
1301 fx -= offset * fixed_scale;
1302
1303 while (b < end) {
1304 const int x = (fx >> 16);
1305
1306 const uint distx = (fx & 0x0000ffff) >> 8;
1307 const uint idistx = 256 - distx;
1308 const uint rb = (intermediate.buffer_rb[x] * idistx + intermediate.buffer_rb[x + 1] * distx) & 0xff00ff00;
1309 const uint ag = (intermediate.buffer_ag[x] * idistx + intermediate.buffer_ag[x + 1] * distx) & 0xff00ff00;
1310 *b = (rb >> 8) | ag;
1311 b++;
1312 fx += fdx;
1313 }
1314 fx += offset * fixed_scale;
1315}
1316
1317typedef void (QT_FASTCALL *BilinearFastTransformHelper)(uint *b, uint *end, const QTextureData &image, int &fx, int &fy, int fdx, int fdy);
1318
1319template<TextureBlendType blendType>
1321 int &fx, int &fy, int fdx, int /*fdy*/)
1322{
1323 int y1 = (fy >> 16);
1324 int y2;
1325 fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
1326 const uint *s1 = (const uint *)image.scanLine(y1);
1327 const uint *s2 = (const uint *)image.scanLine(y2);
1328
1329 const int disty = (fy & 0x0000ffff) >> 8;
1330 const int idisty = 256 - disty;
1331 const int length = end - b;
1332
1333 // The intermediate buffer is generated in the positive direction
1334 const int adjust = (fdx < 0) ? fdx * length : 0;
1335 const int offset = (fx + adjust) >> 16;
1336 int x = offset;
1337
1338 IntermediateBuffer intermediate;
1339 // count is the size used in the intermediate.buffer.
1340 int count = (qint64(length) * qAbs(fdx) + fixed_scale - 1) / fixed_scale + 2;
1341 // length is supposed to be <= BufferSize either because data->m11 < 1 or
1342 // data->m11 < 2, and any larger buffers split
1343 Q_ASSERT(count <= BufferSize + 2);
1344 int f = 0;
1345 int lim = count;
1346 if (blendType == BlendTransformedBilinearTiled) {
1347 x %= image.width;
1348 if (x < 0) x += image.width;
1349 } else {
1350 lim = qMin(count, image.x2 - x);
1351 if (x < image.x1) {
1352 Q_ASSERT(x < image.x2);
1353 uint t = s1[image.x1];
1354 uint b = s2[image.x1];
1355 quint32 rb = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff;
1356 quint32 ag = ((((t>>8) & 0xff00ff) * idisty + ((b>>8) & 0xff00ff) * disty) >> 8) & 0xff00ff;
1357 do {
1358 intermediate.buffer_rb[f] = rb;
1359 intermediate.buffer_ag[f] = ag;
1360 f++;
1361 x++;
1362 } while (x < image.x1 && f < lim);
1363 }
1364 }
1365
1366 if (blendType != BlendTransformedBilinearTiled) {
1367#if defined(__SSE2__)
1368 const __m128i disty_ = _mm_set1_epi16(disty);
1369 const __m128i idisty_ = _mm_set1_epi16(idisty);
1370 const __m128i colorMask = _mm_set1_epi32(0x00ff00ff);
1371
1372 lim -= 3;
1373 for (; f < lim; x += 4, f += 4) {
1374 // Load 4 pixels from s1, and split the alpha-green and red-blue component
1375 __m128i top = _mm_loadu_si128((const __m128i*)((const uint *)(s1)+x));
1376 __m128i topAG = _mm_srli_epi16(top, 8);
1377 __m128i topRB = _mm_and_si128(top, colorMask);
1378 // Multiplies each color component by idisty
1379 topAG = _mm_mullo_epi16 (topAG, idisty_);
1380 topRB = _mm_mullo_epi16 (topRB, idisty_);
1381
1382 // Same for the s2 vector
1383 __m128i bottom = _mm_loadu_si128((const __m128i*)((const uint *)(s2)+x));
1384 __m128i bottomAG = _mm_srli_epi16(bottom, 8);
1385 __m128i bottomRB = _mm_and_si128(bottom, colorMask);
1386 bottomAG = _mm_mullo_epi16 (bottomAG, disty_);
1387 bottomRB = _mm_mullo_epi16 (bottomRB, disty_);
1388
1389 // Add the values, and shift to only keep 8 significant bits per colors
1390 __m128i rAG =_mm_add_epi16(topAG, bottomAG);
1391 rAG = _mm_srli_epi16(rAG, 8);
1392 _mm_storeu_si128((__m128i*)(&intermediate.buffer_ag[f]), rAG);
1393 __m128i rRB =_mm_add_epi16(topRB, bottomRB);
1394 rRB = _mm_srli_epi16(rRB, 8);
1395 _mm_storeu_si128((__m128i*)(&intermediate.buffer_rb[f]), rRB);
1396 }
1397#elif defined(__ARM_NEON__)
1398 const int16x8_t disty_ = vdupq_n_s16(disty);
1399 const int16x8_t idisty_ = vdupq_n_s16(idisty);
1400 const int16x8_t colorMask = vdupq_n_s16(0x00ff);
1401
1402 lim -= 3;
1403 for (; f < lim; x += 4, f += 4) {
1404 // Load 4 pixels from s1, and split the alpha-green and red-blue component
1405 int16x8_t top = vld1q_s16((int16_t*)((const uint *)(s1)+x));
1406 int16x8_t topAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(top), 8));
1407 int16x8_t topRB = vandq_s16(top, colorMask);
1408 // Multiplies each color component by idisty
1409 topAG = vmulq_s16(topAG, idisty_);
1410 topRB = vmulq_s16(topRB, idisty_);
1411
1412 // Same for the s2 vector
1413 int16x8_t bottom = vld1q_s16((int16_t*)((const uint *)(s2)+x));
1414 int16x8_t bottomAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(bottom), 8));
1415 int16x8_t bottomRB = vandq_s16(bottom, colorMask);
1416 bottomAG = vmulq_s16(bottomAG, disty_);
1417 bottomRB = vmulq_s16(bottomRB, disty_);
1418
1419 // Add the values, and shift to only keep 8 significant bits per colors
1420 int16x8_t rAG = vaddq_s16(topAG, bottomAG);
1421 rAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(rAG), 8));
1422 vst1q_s16((int16_t*)(&intermediate.buffer_ag[f]), rAG);
1423 int16x8_t rRB = vaddq_s16(topRB, bottomRB);
1424 rRB = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(rRB), 8));
1425 vst1q_s16((int16_t*)(&intermediate.buffer_rb[f]), rRB);
1426 }
1427#endif
1428 }
1429 for (; f < count; f++) { // Same as above but without simd
1430 if (blendType == BlendTransformedBilinearTiled) {
1431 if (x >= image.width) x -= image.width;
1432 } else {
1433 x = qMin(x, image.x2 - 1);
1434 }
1435
1436 uint t = s1[x];
1437 uint b = s2[x];
1438
1439 intermediate.buffer_rb[f] = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff;
1440 intermediate.buffer_ag[f] = ((((t>>8) & 0xff00ff) * idisty + ((b>>8) & 0xff00ff) * disty) >> 8) & 0xff00ff;
1441 x++;
1442 }
1443
1444 // Now interpolate the values from the intermediate.buffer to get the final result.
1445 intermediate_adder(b, end, intermediate, offset, fx, fdx);
1446}
1447
1448template<TextureBlendType blendType>
1450 int &fx, int &fy, int fdx, int /*fdy*/)
1451{
1452 int y1 = (fy >> 16);
1453 int y2;
1454 fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
1455 const uint *s1 = (const uint *)image.scanLine(y1);
1456 const uint *s2 = (const uint *)image.scanLine(y2);
1457 const int disty = (fy & 0x0000ffff) >> 8;
1458
1459 if (blendType != BlendTransformedBilinearTiled) {
1460 const qint64 min_fx = qint64(image.x1) * fixed_scale;
1461 const qint64 max_fx = qint64(image.x2 - 1) * fixed_scale;
1462 while (b < end) {
1463 int x1 = (fx >> 16);
1464 int x2;
1465 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
1466 if (x1 != x2)
1467 break;
1468 uint top = s1[x1];
1469 uint bot = s2[x1];
1470 *b = INTERPOLATE_PIXEL_256(top, 256 - disty, bot, disty);
1471 fx += fdx;
1472 ++b;
1473 }
1474 uint *boundedEnd = end;
1475 if (fdx > 0)
1476 boundedEnd = qMin(boundedEnd, b + (max_fx - fx) / fdx);
1477 else if (fdx < 0)
1478 boundedEnd = qMin(boundedEnd, b + (min_fx - fx) / fdx);
1479
1480 // A fast middle part without boundary checks
1481 while (b < boundedEnd) {
1482 int x = (fx >> 16);
1483 int distx = (fx & 0x0000ffff) >> 8;
1484 *b = interpolate_4_pixels(s1 + x, s2 + x, distx, disty);
1485 fx += fdx;
1486 ++b;
1487 }
1488 }
1489
1490 while (b < end) {
1491 int x1 = (fx >> 16);
1492 int x2;
1493 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1 , x1, x2);
1494 uint tl = s1[x1];
1495 uint tr = s1[x2];
1496 uint bl = s2[x1];
1497 uint br = s2[x2];
1498 int distx = (fx & 0x0000ffff) >> 8;
1499 *b = interpolate_4_pixels(tl, tr, bl, br, distx, disty);
1500
1501 fx += fdx;
1502 ++b;
1503 }
1504}
1505
1506template<TextureBlendType blendType>
1508 int &fx, int &fy, int fdx, int /*fdy*/)
1509{
1510 int y1 = (fy >> 16);
1511 int y2;
1512 fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
1513 const uint *s1 = (const uint *)image.scanLine(y1);
1514 const uint *s2 = (const uint *)image.scanLine(y2);
1515 const int disty8 = (fy & 0x0000ffff) >> 8;
1516 const int disty4 = (disty8 + 0x08) >> 4;
1517
1518 if (blendType != BlendTransformedBilinearTiled) {
1519 const qint64 min_fx = qint64(image.x1) * fixed_scale;
1520 const qint64 max_fx = qint64(image.x2 - 1) * fixed_scale;
1521 while (b < end) {
1522 int x1 = (fx >> 16);
1523 int x2;
1524 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
1525 if (x1 != x2)
1526 break;
1527 uint top = s1[x1];
1528 uint bot = s2[x1];
1529 *b = INTERPOLATE_PIXEL_256(top, 256 - disty8, bot, disty8);
1530 fx += fdx;
1531 ++b;
1532 }
1533 uint *boundedEnd = end;
1534 if (fdx > 0)
1535 boundedEnd = qMin(boundedEnd, b + (max_fx - fx) / fdx);
1536 else if (fdx < 0)
1537 boundedEnd = qMin(boundedEnd, b + (min_fx - fx) / fdx);
1538 // A fast middle part without boundary checks
1539#if defined(__SSE2__)
1540 const __m128i colorMask = _mm_set1_epi32(0x00ff00ff);
1541 const __m128i v_256 = _mm_set1_epi16(256);
1542 const __m128i v_disty = _mm_set1_epi16(disty4);
1543 const __m128i v_fdx = _mm_set1_epi32(fdx*4);
1544 const __m128i v_fx_r = _mm_set1_epi32(0x8);
1545 __m128i v_fx = _mm_setr_epi32(fx, fx + fdx, fx + fdx + fdx, fx + fdx + fdx + fdx);
1546
1547 while (b < boundedEnd - 3) {
1548 __m128i offset = _mm_srli_epi32(v_fx, 16);
1549 const int offset0 = _mm_cvtsi128_si32(offset); offset = _mm_srli_si128(offset, 4);
1550 const int offset1 = _mm_cvtsi128_si32(offset); offset = _mm_srli_si128(offset, 4);
1551 const int offset2 = _mm_cvtsi128_si32(offset); offset = _mm_srli_si128(offset, 4);
1552 const int offset3 = _mm_cvtsi128_si32(offset);
1553 const __m128i tl = _mm_setr_epi32(s1[offset0], s1[offset1], s1[offset2], s1[offset3]);
1554 const __m128i tr = _mm_setr_epi32(s1[offset0 + 1], s1[offset1 + 1], s1[offset2 + 1], s1[offset3 + 1]);
1555 const __m128i bl = _mm_setr_epi32(s2[offset0], s2[offset1], s2[offset2], s2[offset3]);
1556 const __m128i br = _mm_setr_epi32(s2[offset0 + 1], s2[offset1 + 1], s2[offset2 + 1], s2[offset3 + 1]);
1557
1558 __m128i v_distx = _mm_srli_epi16(v_fx, 8);
1559 v_distx = _mm_srli_epi16(_mm_add_epi32(v_distx, v_fx_r), 4);
1560 v_distx = _mm_shufflehi_epi16(v_distx, _MM_SHUFFLE(2,2,0,0));
1561 v_distx = _mm_shufflelo_epi16(v_distx, _MM_SHUFFLE(2,2,0,0));
1562
1563 interpolate_4_pixels_16_sse2(tl, tr, bl, br, v_distx, v_disty, colorMask, v_256, b);
1564 b += 4;
1565 v_fx = _mm_add_epi32(v_fx, v_fdx);
1566 }
1567 fx = _mm_cvtsi128_si32(v_fx);
1568#elif defined(__ARM_NEON__)
1569 const int16x8_t colorMask = vdupq_n_s16(0x00ff);
1570 const int16x8_t invColorMask = vmvnq_s16(colorMask);
1571 const int16x8_t v_256 = vdupq_n_s16(256);
1572 const int16x8_t v_disty = vdupq_n_s16(disty4);
1573 const int16x8_t v_disty_ = vshlq_n_s16(v_disty, 4);
1574 int32x4_t v_fdx = vdupq_n_s32(fdx*4);
1575
1576 int32x4_t v_fx = vmovq_n_s32(fx);
1577 v_fx = vsetq_lane_s32(fx + fdx, v_fx, 1);
1578 v_fx = vsetq_lane_s32(fx + fdx * 2, v_fx, 2);
1579 v_fx = vsetq_lane_s32(fx + fdx * 3, v_fx, 3);
1580
1581 const int32x4_t v_ffff_mask = vdupq_n_s32(0x0000ffff);
1582 const int32x4_t v_fx_r = vdupq_n_s32(0x0800);
1583
1584 while (b < boundedEnd - 3) {
1585 uint32x4x2_t v_top, v_bot;
1586
1587 int x1 = (fx >> 16);
1588 fx += fdx;
1589 v_top = vld2q_lane_u32(s1 + x1, v_top, 0);
1590 v_bot = vld2q_lane_u32(s2 + x1, v_bot, 0);
1591 x1 = (fx >> 16);
1592 fx += fdx;
1593 v_top = vld2q_lane_u32(s1 + x1, v_top, 1);
1594 v_bot = vld2q_lane_u32(s2 + x1, v_bot, 1);
1595 x1 = (fx >> 16);
1596 fx += fdx;
1597 v_top = vld2q_lane_u32(s1 + x1, v_top, 2);
1598 v_bot = vld2q_lane_u32(s2 + x1, v_bot, 2);
1599 x1 = (fx >> 16);
1600 fx += fdx;
1601 v_top = vld2q_lane_u32(s1 + x1, v_top, 3);
1602 v_bot = vld2q_lane_u32(s2 + x1, v_bot, 3);
1603
1604 int32x4_t v_distx = vshrq_n_s32(vaddq_s32(vandq_s32(v_fx, v_ffff_mask), v_fx_r), 12);
1605 v_distx = vorrq_s32(v_distx, vshlq_n_s32(v_distx, 16));
1606
1607 interpolate_4_pixels_16_neon(
1608 vreinterpretq_s16_u32(v_top.val[0]), vreinterpretq_s16_u32(v_top.val[1]),
1609 vreinterpretq_s16_u32(v_bot.val[0]), vreinterpretq_s16_u32(v_bot.val[1]),
1610 vreinterpretq_s16_s32(v_distx), v_disty, v_disty_,
1611 colorMask, invColorMask, v_256, b);
1612 b+=4;
1613 v_fx = vaddq_s32(v_fx, v_fdx);
1614 }
1615#endif
1616 while (b < boundedEnd) {
1617 int x = (fx >> 16);
1618 if (hasFastInterpolate4()) {
1619 int distx8 = (fx & 0x0000ffff) >> 8;
1620 *b = interpolate_4_pixels(s1 + x, s2 + x, distx8, disty8);
1621 } else {
1622 int distx4 = ((fx & 0x0000ffff) + 0x0800) >> 12;
1623 *b = interpolate_4_pixels_16(s1[x], s1[x + 1], s2[x], s2[x + 1], distx4, disty4);
1624 }
1625 fx += fdx;
1626 ++b;
1627 }
1628 }
1629
1630 while (b < end) {
1631 int x1 = (fx >> 16);
1632 int x2;
1633 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
1634 uint tl = s1[x1];
1635 uint tr = s1[x2];
1636 uint bl = s2[x1];
1637 uint br = s2[x2];
1638 if (hasFastInterpolate4()) {
1639 int distx8 = (fx & 0x0000ffff) >> 8;
1640 *b = interpolate_4_pixels(tl, tr, bl, br, distx8, disty8);
1641 } else {
1642 int distx4 = ((fx & 0x0000ffff) + 0x0800) >> 12;
1643 *b = interpolate_4_pixels_16(tl, tr, bl, br, distx4, disty4);
1644 }
1645 fx += fdx;
1646 ++b;
1647 }
1648}
1649
1650template<TextureBlendType blendType>
1652 int &fx, int &fy, int fdx, int fdy)
1653{
1654 // if we are zooming more than 8 times, we use 8bit precision for the position.
1655 while (b < end) {
1656 int x1 = (fx >> 16);
1657 int x2;
1658 int y1 = (fy >> 16);
1659 int y2;
1660
1661 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
1662 fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
1663
1664 const uint *s1 = (const uint *)image.scanLine(y1);
1665 const uint *s2 = (const uint *)image.scanLine(y2);
1666
1667 uint tl = s1[x1];
1668 uint tr = s1[x2];
1669 uint bl = s2[x1];
1670 uint br = s2[x2];
1671
1672 int distx = (fx & 0x0000ffff) >> 8;
1673 int disty = (fy & 0x0000ffff) >> 8;
1674
1675 *b = interpolate_4_pixels(tl, tr, bl, br, distx, disty);
1676
1677 fx += fdx;
1678 fy += fdy;
1679 ++b;
1680 }
1681}
1682
1683template<TextureBlendType blendType>
1685 int &fx, int &fy, int fdx, int fdy)
1686{
1687 //we are zooming less than 8x, use 4bit precision
1688 if (blendType != BlendTransformedBilinearTiled) {
1689 const qint64 min_fx = qint64(image.x1) * fixed_scale;
1690 const qint64 max_fx = qint64(image.x2 - 1) * fixed_scale;
1691 const qint64 min_fy = qint64(image.y1) * fixed_scale;
1692 const qint64 max_fy = qint64(image.y2 - 1) * fixed_scale;
1693 // first handle the possibly bounded part in the beginning
1694 while (b < end) {
1695 int x1 = (fx >> 16);
1696 int x2;
1697 int y1 = (fy >> 16);
1698 int y2;
1699 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
1700 fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
1701 if (x1 != x2 && y1 != y2)
1702 break;
1703 const uint *s1 = (const uint *)image.scanLine(y1);
1704 const uint *s2 = (const uint *)image.scanLine(y2);
1705 uint tl = s1[x1];
1706 uint tr = s1[x2];
1707 uint bl = s2[x1];
1708 uint br = s2[x2];
1709 if (hasFastInterpolate4()) {
1710 int distx = (fx & 0x0000ffff) >> 8;
1711 int disty = (fy & 0x0000ffff) >> 8;
1712 *b = interpolate_4_pixels(tl, tr, bl, br, distx, disty);
1713 } else {
1714 int distx = ((fx & 0x0000ffff) + 0x0800) >> 12;
1715 int disty = ((fy & 0x0000ffff) + 0x0800) >> 12;
1716 *b = interpolate_4_pixels_16(tl, tr, bl, br, distx, disty);
1717 }
1718 fx += fdx;
1719 fy += fdy;
1720 ++b;
1721 }
1722 uint *boundedEnd = end;
1723 if (fdx > 0)
1724 boundedEnd = qMin(boundedEnd, b + (max_fx - fx) / fdx);
1725 else if (fdx < 0)
1726 boundedEnd = qMin(boundedEnd, b + (min_fx - fx) / fdx);
1727 if (fdy > 0)
1728 boundedEnd = qMin(boundedEnd, b + (max_fy - fy) / fdy);
1729 else if (fdy < 0)
1730 boundedEnd = qMin(boundedEnd, b + (min_fy - fy) / fdy);
1731
1732 // until boundedEnd we can now have a fast middle part without boundary checks
1733#if defined(__SSE2__)
1734 const __m128i colorMask = _mm_set1_epi32(0x00ff00ff);
1735 const __m128i v_256 = _mm_set1_epi16(256);
1736 const __m128i v_fdx = _mm_set1_epi32(fdx*4);
1737 const __m128i v_fdy = _mm_set1_epi32(fdy*4);
1738 const __m128i v_fxy_r = _mm_set1_epi32(0x8);
1739 __m128i v_fx = _mm_setr_epi32(fx, fx + fdx, fx + fdx + fdx, fx + fdx + fdx + fdx);
1740 __m128i v_fy = _mm_setr_epi32(fy, fy + fdy, fy + fdy + fdy, fy + fdy + fdy + fdy);
1741
1742 const uchar *textureData = image.imageData;
1743 const qsizetype bytesPerLine = image.bytesPerLine;
1744 const __m128i vbpl = _mm_shufflelo_epi16(_mm_cvtsi32_si128(bytesPerLine/4), _MM_SHUFFLE(0, 0, 0, 0));
1745
1746 while (b < boundedEnd - 3) {
1747 const __m128i vy = _mm_packs_epi32(_mm_srli_epi32(v_fy, 16), _mm_setzero_si128());
1748 // 4x16bit * 4x16bit -> 4x32bit
1749 __m128i offset = _mm_unpacklo_epi16(_mm_mullo_epi16(vy, vbpl), _mm_mulhi_epi16(vy, vbpl));
1750 offset = _mm_add_epi32(offset, _mm_srli_epi32(v_fx, 16));
1751 const int offset0 = _mm_cvtsi128_si32(offset); offset = _mm_srli_si128(offset, 4);
1752 const int offset1 = _mm_cvtsi128_si32(offset); offset = _mm_srli_si128(offset, 4);
1753 const int offset2 = _mm_cvtsi128_si32(offset); offset = _mm_srli_si128(offset, 4);
1754 const int offset3 = _mm_cvtsi128_si32(offset);
1755 const uint *topData = (const uint *)(textureData);
1756 const __m128i tl = _mm_setr_epi32(topData[offset0], topData[offset1], topData[offset2], topData[offset3]);
1757 const __m128i tr = _mm_setr_epi32(topData[offset0 + 1], topData[offset1 + 1], topData[offset2 + 1], topData[offset3 + 1]);
1758 const uint *bottomData = (const uint *)(textureData + bytesPerLine);
1759 const __m128i bl = _mm_setr_epi32(bottomData[offset0], bottomData[offset1], bottomData[offset2], bottomData[offset3]);
1760 const __m128i br = _mm_setr_epi32(bottomData[offset0 + 1], bottomData[offset1 + 1], bottomData[offset2 + 1], bottomData[offset3 + 1]);
1761
1762 __m128i v_distx = _mm_srli_epi16(v_fx, 8);
1763 __m128i v_disty = _mm_srli_epi16(v_fy, 8);
1764 v_distx = _mm_srli_epi16(_mm_add_epi32(v_distx, v_fxy_r), 4);
1765 v_disty = _mm_srli_epi16(_mm_add_epi32(v_disty, v_fxy_r), 4);
1766 v_distx = _mm_shufflehi_epi16(v_distx, _MM_SHUFFLE(2,2,0,0));
1767 v_distx = _mm_shufflelo_epi16(v_distx, _MM_SHUFFLE(2,2,0,0));
1768 v_disty = _mm_shufflehi_epi16(v_disty, _MM_SHUFFLE(2,2,0,0));
1769 v_disty = _mm_shufflelo_epi16(v_disty, _MM_SHUFFLE(2,2,0,0));
1770
1771 interpolate_4_pixels_16_sse2(tl, tr, bl, br, v_distx, v_disty, colorMask, v_256, b);
1772 b += 4;
1773 v_fx = _mm_add_epi32(v_fx, v_fdx);
1774 v_fy = _mm_add_epi32(v_fy, v_fdy);
1775 }
1776 fx = _mm_cvtsi128_si32(v_fx);
1777 fy = _mm_cvtsi128_si32(v_fy);
1778#elif defined(__ARM_NEON__)
1779 const int16x8_t colorMask = vdupq_n_s16(0x00ff);
1780 const int16x8_t invColorMask = vmvnq_s16(colorMask);
1781 const int16x8_t v_256 = vdupq_n_s16(256);
1782 int32x4_t v_fdx = vdupq_n_s32(fdx * 4);
1783 int32x4_t v_fdy = vdupq_n_s32(fdy * 4);
1784
1785 const uchar *textureData = image.imageData;
1786 const qsizetype bytesPerLine = image.bytesPerLine;
1787
1788 int32x4_t v_fx = vmovq_n_s32(fx);
1789 int32x4_t v_fy = vmovq_n_s32(fy);
1790 v_fx = vsetq_lane_s32(fx + fdx, v_fx, 1);
1791 v_fy = vsetq_lane_s32(fy + fdy, v_fy, 1);
1792 v_fx = vsetq_lane_s32(fx + fdx * 2, v_fx, 2);
1793 v_fy = vsetq_lane_s32(fy + fdy * 2, v_fy, 2);
1794 v_fx = vsetq_lane_s32(fx + fdx * 3, v_fx, 3);
1795 v_fy = vsetq_lane_s32(fy + fdy * 3, v_fy, 3);
1796
1797 const int32x4_t v_ffff_mask = vdupq_n_s32(0x0000ffff);
1798 const int32x4_t v_round = vdupq_n_s32(0x0800);
1799
1800 while (b < boundedEnd - 3) {
1801 uint32x4x2_t v_top, v_bot;
1802
1803 int x1 = (fx >> 16);
1804 int y1 = (fy >> 16);
1805 fx += fdx; fy += fdy;
1806 const uchar *sl = textureData + bytesPerLine * y1;
1807 const uint *s1 = reinterpret_cast<const uint *>(sl);
1808 const uint *s2 = reinterpret_cast<const uint *>(sl + bytesPerLine);
1809 v_top = vld2q_lane_u32(s1 + x1, v_top, 0);
1810 v_bot = vld2q_lane_u32(s2 + x1, v_bot, 0);
1811 x1 = (fx >> 16);
1812 y1 = (fy >> 16);
1813 fx += fdx; fy += fdy;
1814 sl = textureData + bytesPerLine * y1;
1815 s1 = reinterpret_cast<const uint *>(sl);
1816 s2 = reinterpret_cast<const uint *>(sl + bytesPerLine);
1817 v_top = vld2q_lane_u32(s1 + x1, v_top, 1);
1818 v_bot = vld2q_lane_u32(s2 + x1, v_bot, 1);
1819 x1 = (fx >> 16);
1820 y1 = (fy >> 16);
1821 fx += fdx; fy += fdy;
1822 sl = textureData + bytesPerLine * y1;
1823 s1 = reinterpret_cast<const uint *>(sl);
1824 s2 = reinterpret_cast<const uint *>(sl + bytesPerLine);
1825 v_top = vld2q_lane_u32(s1 + x1, v_top, 2);
1826 v_bot = vld2q_lane_u32(s2 + x1, v_bot, 2);
1827 x1 = (fx >> 16);
1828 y1 = (fy >> 16);
1829 fx += fdx; fy += fdy;
1830 sl = textureData + bytesPerLine * y1;
1831 s1 = reinterpret_cast<const uint *>(sl);
1832 s2 = reinterpret_cast<const uint *>(sl + bytesPerLine);
1833 v_top = vld2q_lane_u32(s1 + x1, v_top, 3);
1834 v_bot = vld2q_lane_u32(s2 + x1, v_bot, 3);
1835
1836 int32x4_t v_distx = vshrq_n_s32(vaddq_s32(vandq_s32(v_fx, v_ffff_mask), v_round), 12);
1837 int32x4_t v_disty = vshrq_n_s32(vaddq_s32(vandq_s32(v_fy, v_ffff_mask), v_round), 12);
1838 v_distx = vorrq_s32(v_distx, vshlq_n_s32(v_distx, 16));
1839 v_disty = vorrq_s32(v_disty, vshlq_n_s32(v_disty, 16));
1840 int16x8_t v_disty_ = vshlq_n_s16(vreinterpretq_s16_s32(v_disty), 4);
1841
1842 interpolate_4_pixels_16_neon(
1843 vreinterpretq_s16_u32(v_top.val[0]), vreinterpretq_s16_u32(v_top.val[1]),
1844 vreinterpretq_s16_u32(v_bot.val[0]), vreinterpretq_s16_u32(v_bot.val[1]),
1845 vreinterpretq_s16_s32(v_distx), vreinterpretq_s16_s32(v_disty),
1846 v_disty_, colorMask, invColorMask, v_256, b);
1847 b += 4;
1848 v_fx = vaddq_s32(v_fx, v_fdx);
1849 v_fy = vaddq_s32(v_fy, v_fdy);
1850 }
1851#endif
1852 while (b < boundedEnd) {
1853 int x = (fx >> 16);
1854 int y = (fy >> 16);
1855
1856 const uint *s1 = (const uint *)image.scanLine(y);
1857 const uint *s2 = (const uint *)image.scanLine(y + 1);
1858
1859 if (hasFastInterpolate4()) {
1860 int distx = (fx & 0x0000ffff) >> 8;
1861 int disty = (fy & 0x0000ffff) >> 8;
1862 *b = interpolate_4_pixels(s1 + x, s2 + x, distx, disty);
1863 } else {
1864 int distx = ((fx & 0x0000ffff) + 0x0800) >> 12;
1865 int disty = ((fy & 0x0000ffff) + 0x0800) >> 12;
1866 *b = interpolate_4_pixels_16(s1[x], s1[x + 1], s2[x], s2[x + 1], distx, disty);
1867 }
1868
1869 fx += fdx;
1870 fy += fdy;
1871 ++b;
1872 }
1873 }
1874
1875 while (b < end) {
1876 int x1 = (fx >> 16);
1877 int x2;
1878 int y1 = (fy >> 16);
1879 int y2;
1880
1881 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
1882 fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
1883
1884 const uint *s1 = (const uint *)image.scanLine(y1);
1885 const uint *s2 = (const uint *)image.scanLine(y2);
1886
1887 uint tl = s1[x1];
1888 uint tr = s1[x2];
1889 uint bl = s2[x1];
1890 uint br = s2[x2];
1891
1892 if (hasFastInterpolate4()) {
1893 int distx = (fx & 0x0000ffff) >> 8;
1894 int disty = (fy & 0x0000ffff) >> 8;
1895 *b = interpolate_4_pixels(tl, tr, bl, br, distx, disty);
1896 } else {
1897 int distx = ((fx & 0x0000ffff) + 0x0800) >> 12;
1898 int disty = ((fy & 0x0000ffff) + 0x0800) >> 12;
1899 *b = interpolate_4_pixels_16(tl, tr, bl, br, distx, disty);
1900 }
1901
1902 fx += fdx;
1903 fy += fdy;
1904 ++b;
1905 }
1906}
1907
1908
1910 {
1911 fetchTransformedBilinearARGB32PM_simple_scale_helper<BlendTransformedBilinear>,
1912 fetchTransformedBilinearARGB32PM_upscale_helper<BlendTransformedBilinear>,
1913 fetchTransformedBilinearARGB32PM_downscale_helper<BlendTransformedBilinear>,
1914 fetchTransformedBilinearARGB32PM_rotate_helper<BlendTransformedBilinear>,
1915 fetchTransformedBilinearARGB32PM_fast_rotate_helper<BlendTransformedBilinear>
1916 },
1917 {
1918 fetchTransformedBilinearARGB32PM_simple_scale_helper<BlendTransformedBilinearTiled>,
1919 fetchTransformedBilinearARGB32PM_upscale_helper<BlendTransformedBilinearTiled>,
1920 fetchTransformedBilinearARGB32PM_downscale_helper<BlendTransformedBilinearTiled>,
1921 fetchTransformedBilinearARGB32PM_rotate_helper<BlendTransformedBilinearTiled>,
1922 fetchTransformedBilinearARGB32PM_fast_rotate_helper<BlendTransformedBilinearTiled>
1923 }
1924};
1925
1926template<TextureBlendType blendType> /* blendType = BlendTransformedBilinear or BlendTransformedBilinearTiled */
1928 const QSpanData *data, int y, int x,
1929 int length)
1930{
1931 const qreal cx = x + qreal(0.5);
1932 const qreal cy = y + qreal(0.5);
1933 constexpr int tiled = (blendType == BlendTransformedBilinearTiled) ? 1 : 0;
1934
1935 uint *end = buffer + length;
1936 uint *b = buffer;
1937 if (canUseFastMatrixPath(cx, cy, length, data)) {
1938 // The increment pr x in the scanline
1939 int fdx = (int)(data->m11 * fixed_scale);
1940 int fdy = (int)(data->m12 * fixed_scale);
1941
1942 int fx = int((data->m21 * cy
1943 + data->m11 * cx + data->dx) * fixed_scale);
1944 int fy = int((data->m22 * cy
1945 + data->m12 * cx + data->dy) * fixed_scale);
1946
1947 fx -= half_point;
1948 fy -= half_point;
1949
1950 if (fdy == 0) { // simple scale, no rotation or shear
1951 if (qAbs(fdx) <= fixed_scale) {
1952 // simple scale up on X
1953 bilinearFastTransformHelperARGB32PM[tiled][SimpleScaleTransform](b, end, data->texture, fx, fy, fdx, fdy);
1954 } else if (qAbs(fdx) <= 2 * fixed_scale) {
1955 // simple scale down on X, less than 2x
1956 const int mid = (length * 2 < BufferSize) ? length : ((length + 1) / 2);
1957 bilinearFastTransformHelperARGB32PM[tiled][SimpleScaleTransform](buffer, buffer + mid, data->texture, fx, fy, fdx, fdy);
1958 if (mid != length)
1959 bilinearFastTransformHelperARGB32PM[tiled][SimpleScaleTransform](buffer + mid, buffer + length, data->texture, fx, fy, fdx, fdy);
1960 } else if (qAbs(data->m22) < qreal(1./8.)) {
1961 // scale up more than 8x (on Y)
1962 bilinearFastTransformHelperARGB32PM[tiled][UpscaleTransform](b, end, data->texture, fx, fy, fdx, fdy);
1963 } else {
1964 // scale down on X
1965 bilinearFastTransformHelperARGB32PM[tiled][DownscaleTransform](b, end, data->texture, fx, fy, fdx, fdy);
1966 }
1967 } else { // rotation or shear
1968 if (qAbs(data->m11) < qreal(1./8.) || qAbs(data->m22) < qreal(1./8.) ) {
1969 // if we are zooming more than 8 times, we use 8bit precision for the position.
1970 bilinearFastTransformHelperARGB32PM[tiled][RotateTransform](b, end, data->texture, fx, fy, fdx, fdy);
1971 } else {
1972 // we are zooming less than 8x, use 4bit precision
1973 bilinearFastTransformHelperARGB32PM[tiled][FastRotateTransform](b, end, data->texture, fx, fy, fdx, fdy);
1974 }
1975 }
1976 } else {
1977 const QTextureData &image = data->texture;
1978
1979 const qreal fdx = data->m11;
1980 const qreal fdy = data->m12;
1981 const qreal fdw = data->m13;
1982
1983 qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
1984 qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
1985 qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
1986
1987 while (b < end) {
1988 const qreal iw = fw == 0 ? 1 : 1 / fw;
1989 const qreal px = fx * iw - qreal(0.5);
1990 const qreal py = fy * iw - qreal(0.5);
1991
1992 int x1 = int(px) - (px < 0);
1993 int x2;
1994 int y1 = int(py) - (py < 0);
1995 int y2;
1996
1997 int distx = int((px - x1) * 256);
1998 int disty = int((py - y1) * 256);
1999
2000 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
2001 fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
2002
2003 const uint *s1 = (const uint *)data->texture.scanLine(y1);
2004 const uint *s2 = (const uint *)data->texture.scanLine(y2);
2005
2006 uint tl = s1[x1];
2007 uint tr = s1[x2];
2008 uint bl = s2[x1];
2009 uint br = s2[x2];
2010
2011 *b = interpolate_4_pixels(tl, tr, bl, br, distx, disty);
2012
2013 fx += fdx;
2014 fy += fdy;
2015 fw += fdw;
2016 //force increment to avoid /0
2017 if (!fw) {
2018 fw += fdw;
2019 }
2020 ++b;
2021 }
2022 }
2023
2024 return buffer;
2025}
2026
2027template<TextureBlendType blendType>
2029 int &fx, int &fy, int fdx, int /*fdy*/)
2030{
2031 const QPixelLayout *layout = &qPixelLayouts[image.format];
2032 const QList<QRgb> *clut = image.colorTable;
2033 const FetchAndConvertPixelsFunc fetch = layout->fetchToARGB32PM;
2034
2035 int y1 = (fy >> 16);
2036 int y2;
2037 fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
2038 const uchar *s1 = image.scanLine(y1);
2039 const uchar *s2 = image.scanLine(y2);
2040
2041 const int disty = (fy & 0x0000ffff) >> 8;
2042 const int idisty = 256 - disty;
2043 const int length = end - b;
2044
2045 // The intermediate buffer is generated in the positive direction
2046 const int adjust = (fdx < 0) ? fdx * length : 0;
2047 const int offset = (fx + adjust) >> 16;
2048 int x = offset;
2049
2050 IntermediateBuffer intermediate;
2051 uint *buf1 = intermediate.buffer_rb;
2052 uint *buf2 = intermediate.buffer_ag;
2053 const uint *ptr1;
2054 const uint *ptr2;
2055
2056 int count = (qint64(length) * qAbs(fdx) + fixed_scale - 1) / fixed_scale + 2;
2057 Q_ASSERT(count <= BufferSize + 2);
2058
2059 if (blendType == BlendTransformedBilinearTiled) {
2060 x %= image.width;
2061 if (x < 0)
2062 x += image.width;
2063 int len1 = qMin(count, image.width - x);
2064 int len2 = qMin(x, count - len1);
2065
2066 ptr1 = fetch(buf1, s1, x, len1, clut, nullptr);
2067 ptr2 = fetch(buf2, s2, x, len1, clut, nullptr);
2068 for (int i = 0; i < len1; ++i) {
2069 uint t = ptr1[i];
2070 uint b = ptr2[i];
2071 buf1[i] = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff;
2072 buf2[i] = ((((t >> 8) & 0xff00ff) * idisty + ((b >> 8) & 0xff00ff) * disty) >> 8) & 0xff00ff;
2073 }
2074
2075 if (len2) {
2076 ptr1 = fetch(buf1 + len1, s1, 0, len2, clut, nullptr);
2077 ptr2 = fetch(buf2 + len1, s2, 0, len2, clut, nullptr);
2078 for (int i = 0; i < len2; ++i) {
2079 uint t = ptr1[i];
2080 uint b = ptr2[i];
2081 buf1[i + len1] = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff;
2082 buf2[i + len1] = ((((t >> 8) & 0xff00ff) * idisty + ((b >> 8) & 0xff00ff) * disty) >> 8) & 0xff00ff;
2083 }
2084 }
2085 // Generate the rest by repeatedly repeating the previous set of pixels
2086 for (int i = image.width; i < count; ++i) {
2087 buf1[i] = buf1[i - image.width];
2088 buf2[i] = buf2[i - image.width];
2089 }
2090 } else {
2091 int start = qMax(x, image.x1);
2092 int end = qMin(x + count, image.x2);
2093 int len = qMax(1, end - start);
2094 int leading = start - x;
2095
2096 ptr1 = fetch(buf1 + leading, s1, start, len, clut, nullptr);
2097 ptr2 = fetch(buf2 + leading, s2, start, len, clut, nullptr);
2098
2099 for (int i = 0; i < len; ++i) {
2100 uint t = ptr1[i];
2101 uint b = ptr2[i];
2102 buf1[i + leading] = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff;
2103 buf2[i + leading] = ((((t >> 8) & 0xff00ff) * idisty + ((b >> 8) & 0xff00ff) * disty) >> 8) & 0xff00ff;
2104 }
2105
2106 for (int i = 0; i < leading; ++i) {
2107 buf1[i] = buf1[leading];
2108 buf2[i] = buf2[leading];
2109 }
2110 for (int i = leading + len; i < count; ++i) {
2111 buf1[i] = buf1[i - 1];
2112 buf2[i] = buf2[i - 1];
2113 }
2114 }
2115
2116 // Now interpolate the values from the intermediate.buffer to get the final result.
2117 intermediate_adder(b, end, intermediate, offset, fx, fdx);
2118}
2119
2120
2121template<TextureBlendType blendType, QPixelLayout::BPP bpp, typename T>
2122static void QT_FASTCALL fetchTransformedBilinear_fetcher(T *buf1, T *buf2, const int len, const QTextureData &image,
2123 int fx, int fy, const int fdx, const int fdy)
2124{
2125 const QPixelLayout &layout = qPixelLayouts[image.format];
2126 constexpr bool useFetch = (bpp < QPixelLayout::BPP32);
2127 if (useFetch)
2128 Q_ASSERT(sizeof(T) == sizeof(uint));
2129 else
2130 Q_ASSERT(layout.bpp == bpp || (layout.bpp == QPixelLayout::BPP16FPx4 && bpp == QPixelLayout::BPP64));
2131 const Fetch1PixelFunc fetch1 = (bpp == QPixelLayout::BPPNone) ? fetch1PixelTable[layout.bpp] : fetch1Pixel<bpp>;
2132 if (fdy == 0) {
2133 int y1 = (fy >> 16);
2134 int y2;
2135 fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
2136 const uchar *s1 = image.scanLine(y1);
2137 const uchar *s2 = image.scanLine(y2);
2138
2139 int i = 0;
2140 if (blendType == BlendTransformedBilinear) {
2141 for (; i < len; ++i) {
2142 int x1 = (fx >> 16);
2143 int x2;
2144 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
2145 if (x1 != x2)
2146 break;
2147 if constexpr (useFetch) {
2148 buf1[i * 2 + 0] = buf1[i * 2 + 1] = fetch1(s1, x1);
2149 buf2[i * 2 + 0] = buf2[i * 2 + 1] = fetch1(s2, x1);
2150 } else {
2151 buf1[i * 2 + 0] = buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x1];
2152 buf2[i * 2 + 0] = buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x1];
2153 }
2154 fx += fdx;
2155 }
2156 int fastLen = len;
2157 if (fdx > 0)
2158 fastLen = qMin(fastLen, int((qint64(image.x2 - 1) * fixed_scale - fx) / fdx));
2159 else if (fdx < 0)
2160 fastLen = qMin(fastLen, int((qint64(image.x1) * fixed_scale - fx) / fdx));
2161
2162 for (; i < fastLen; ++i) {
2163 int x = (fx >> 16);
2164 if constexpr (useFetch) {
2165 buf1[i * 2 + 0] = fetch1(s1, x);
2166 buf1[i * 2 + 1] = fetch1(s1, x + 1);
2167 buf2[i * 2 + 0] = fetch1(s2, x);
2168 buf2[i * 2 + 1] = fetch1(s2, x + 1);
2169 } else {
2170 buf1[i * 2 + 0] = reinterpret_cast<const T *>(s1)[x];
2171 buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x + 1];
2172 buf2[i * 2 + 0] = reinterpret_cast<const T *>(s2)[x];
2173 buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x + 1];
2174 }
2175 fx += fdx;
2176 }
2177 }
2178
2179 for (; i < len; ++i) {
2180 int x1 = (fx >> 16);
2181 int x2;
2182 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
2183 if constexpr (useFetch) {
2184 buf1[i * 2 + 0] = fetch1(s1, x1);
2185 buf1[i * 2 + 1] = fetch1(s1, x2);
2186 buf2[i * 2 + 0] = fetch1(s2, x1);
2187 buf2[i * 2 + 1] = fetch1(s2, x2);
2188 } else {
2189 buf1[i * 2 + 0] = reinterpret_cast<const T *>(s1)[x1];
2190 buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x2];
2191 buf2[i * 2 + 0] = reinterpret_cast<const T *>(s2)[x1];
2192 buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x2];
2193 }
2194 fx += fdx;
2195 }
2196 } else {
2197 int i = 0;
2198 if (blendType == BlendTransformedBilinear) {
2199 for (; i < len; ++i) {
2200 int x1 = (fx >> 16);
2201 int x2;
2202 int y1 = (fy >> 16);
2203 int y2;
2204 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
2205 fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
2206 if (x1 != x2 && y1 != y2)
2207 break;
2208 const uchar *s1 = image.scanLine(y1);
2209 const uchar *s2 = image.scanLine(y2);
2210 if constexpr (useFetch) {
2211 buf1[i * 2 + 0] = fetch1(s1, x1);
2212 buf1[i * 2 + 1] = fetch1(s1, x2);
2213 buf2[i * 2 + 0] = fetch1(s2, x1);
2214 buf2[i * 2 + 1] = fetch1(s2, x2);
2215 } else {
2216 buf1[i * 2 + 0] = reinterpret_cast<const T *>(s1)[x1];
2217 buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x2];
2218 buf2[i * 2 + 0] = reinterpret_cast<const T *>(s2)[x1];
2219 buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x2];
2220 }
2221 fx += fdx;
2222 fy += fdy;
2223 }
2224 int fastLen = len;
2225 if (fdx > 0)
2226 fastLen = qMin(fastLen, int((qint64(image.x2 - 1) * fixed_scale - fx) / fdx));
2227 else if (fdx < 0)
2228 fastLen = qMin(fastLen, int((qint64(image.x1) * fixed_scale - fx) / fdx));
2229 if (fdy > 0)
2230 fastLen = qMin(fastLen, int((qint64(image.y2 - 1) * fixed_scale - fy) / fdy));
2231 else if (fdy < 0)
2232 fastLen = qMin(fastLen, int((qint64(image.y1) * fixed_scale - fy) / fdy));
2233
2234 for (; i < fastLen; ++i) {
2235 int x = (fx >> 16);
2236 int y = (fy >> 16);
2237 const uchar *s1 = image.scanLine(y);
2238 const uchar *s2 = s1 + image.bytesPerLine;
2239 if constexpr (useFetch) {
2240 buf1[i * 2 + 0] = fetch1(s1, x);
2241 buf1[i * 2 + 1] = fetch1(s1, x + 1);
2242 buf2[i * 2 + 0] = fetch1(s2, x);
2243 buf2[i * 2 + 1] = fetch1(s2, x + 1);
2244 } else {
2245 buf1[i * 2 + 0] = reinterpret_cast<const T *>(s1)[x];
2246 buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x + 1];
2247 buf2[i * 2 + 0] = reinterpret_cast<const T *>(s2)[x];
2248 buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x + 1];
2249 }
2250 fx += fdx;
2251 fy += fdy;
2252 }
2253 }
2254
2255 for (; i < len; ++i) {
2256 int x1 = (fx >> 16);
2257 int x2;
2258 int y1 = (fy >> 16);
2259 int y2;
2260 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
2261 fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
2262
2263 const uchar *s1 = image.scanLine(y1);
2264 const uchar *s2 = image.scanLine(y2);
2265 if constexpr (useFetch) {
2266 buf1[i * 2 + 0] = fetch1(s1, x1);
2267 buf1[i * 2 + 1] = fetch1(s1, x2);
2268 buf2[i * 2 + 0] = fetch1(s2, x1);
2269 buf2[i * 2 + 1] = fetch1(s2, x2);
2270 } else {
2271 buf1[i * 2 + 0] = reinterpret_cast<const T *>(s1)[x1];
2272 buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x2];
2273 buf2[i * 2 + 0] = reinterpret_cast<const T *>(s2)[x1];
2274 buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x2];
2275 }
2276 fx += fdx;
2277 fy += fdy;
2278 }
2279 }
2280}
2281
2282template<TextureBlendType blendType, QPixelLayout::BPP bpp, typename T>
2283static void QT_FASTCALL fetchTransformedBilinear_slow_fetcher(T *buf1, T *buf2, ushort *distxs, ushort *distys,
2284 const int len, const QTextureData &image,
2285 qreal &fx, qreal &fy, qreal &fw,
2286 const qreal fdx, const qreal fdy, const qreal fdw)
2287{
2288 const QPixelLayout &layout = qPixelLayouts[image.format];
2289 constexpr bool useFetch = (bpp < QPixelLayout::BPP32);
2290 if (useFetch)
2291 Q_ASSERT(sizeof(T) == sizeof(uint));
2292 else
2293 Q_ASSERT(layout.bpp == bpp);
2294
2295 const Fetch1PixelFunc fetch1 = (bpp == QPixelLayout::BPPNone) ? fetch1PixelTable[layout.bpp] : fetch1Pixel<bpp>;
2296
2297 for (int i = 0; i < len; ++i) {
2298 const qreal iw = fw == 0 ? 16384 : 1 / fw;
2299 const qreal px = fx * iw - qreal(0.5);
2300 const qreal py = fy * iw - qreal(0.5);
2301
2302 int x1 = qFloor(px);
2303 int x2;
2304 int y1 = qFloor(py);
2305 int y2;
2306
2307 distxs[i] = ushort((px - x1) * (1<<16));
2308 distys[i] = ushort((py - y1) * (1<<16));
2309
2310 fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2);
2311 fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2);
2312
2313 const uchar *s1 = image.scanLine(y1);
2314 const uchar *s2 = image.scanLine(y2);
2315 if constexpr (useFetch) {
2316 buf1[i * 2 + 0] = fetch1(s1, x1);
2317 buf1[i * 2 + 1] = fetch1(s1, x2);
2318 buf2[i * 2 + 0] = fetch1(s2, x1);
2319 buf2[i * 2 + 1] = fetch1(s2, x2);
2320 } else {
2321 buf1[i * 2 + 0] = reinterpret_cast<const T *>(s1)[x1];
2322 buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x2];
2323 buf2[i * 2 + 0] = reinterpret_cast<const T *>(s2)[x1];
2324 buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x2];
2325 }
2326
2327 fx += fdx;
2328 fy += fdy;
2329 fw += fdw;
2330 }
2331}
2332
2333// blendType = BlendTransformedBilinear or BlendTransformedBilinearTiled
2334template<TextureBlendType blendType, QPixelLayout::BPP bpp>
2336 const QSpanData *data, int y, int x, int length)
2337{
2338 const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
2339 const QList<QRgb> *clut = data->texture.colorTable;
2340 Q_ASSERT(bpp == QPixelLayout::BPPNone || layout->bpp == bpp);
2341
2342 const qreal cx = x + qreal(0.5);
2343 const qreal cy = y + qreal(0.5);
2344
2345 if (canUseFastMatrixPath(cx, cy, length, data)) {
2346 // The increment pr x in the scanline
2347 int fdx = (int)(data->m11 * fixed_scale);
2348 int fdy = (int)(data->m12 * fixed_scale);
2349
2350 int fx = int((data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale);
2351 int fy = int((data->m22 * cy + data->m12 * cx + data->dy) * fixed_scale);
2352
2353 fx -= half_point;
2354 fy -= half_point;
2355
2356 if (fdy == 0) { // simple scale, no rotation or shear
2357 if (qAbs(fdx) <= fixed_scale) { // scale up on X
2358 fetchTransformedBilinear_simple_scale_helper<blendType>(buffer, buffer + length, data->texture, fx, fy, fdx, fdy);
2359 } else if (qAbs(fdx) <= 2 * fixed_scale) { // scale down on X less than 2x
2360 const int mid = (length * 2 < BufferSize) ? length : ((length + 1) / 2);
2361 fetchTransformedBilinear_simple_scale_helper<blendType>(buffer, buffer + mid, data->texture, fx, fy, fdx, fdy);
2362 if (mid != length)
2363 fetchTransformedBilinear_simple_scale_helper<blendType>(buffer + mid, buffer + length, data->texture, fx, fy, fdx, fdy);
2364 } else {
2365 const auto fetcher = fetchTransformedBilinear_fetcher<blendType,bpp,uint>;
2366
2367 uint buf1[BufferSize];
2368 uint buf2[BufferSize];
2369 uint *b = buffer;
2370 while (length) {
2371 int len = qMin(length, BufferSize / 2);
2372 fetcher(buf1, buf2, len, data->texture, fx, fy, fdx, 0);
2373 layout->convertToARGB32PM(buf1, len * 2, clut);
2374 layout->convertToARGB32PM(buf2, len * 2, clut);
2375
2376 if (hasFastInterpolate4() || qAbs(data->m22) < qreal(1./8.)) { // scale up more than 8x (on Y)
2377 int disty = (fy & 0x0000ffff) >> 8;
2378 for (int i = 0; i < len; ++i) {
2379 int distx = (fx & 0x0000ffff) >> 8;
2380 b[i] = interpolate_4_pixels(buf1 + i * 2, buf2 + i * 2, distx, disty);
2381 fx += fdx;
2382 }
2383 } else {
2384 int disty = ((fy & 0x0000ffff) + 0x0800) >> 12;
2385 for (int i = 0; i < len; ++i) {
2386 uint tl = buf1[i * 2 + 0];
2387 uint tr = buf1[i * 2 + 1];
2388 uint bl = buf2[i * 2 + 0];
2389 uint br = buf2[i * 2 + 1];
2390 int distx = ((fx & 0x0000ffff) + 0x0800) >> 12;
2391 b[i] = interpolate_4_pixels_16(tl, tr, bl, br, distx, disty);
2392 fx += fdx;
2393 }
2394 }
2395 length -= len;
2396 b += len;
2397 }
2398 }
2399 } else { // rotation or shear
2400 const auto fetcher = fetchTransformedBilinear_fetcher<blendType,bpp,uint>;
2401
2402 uint buf1[BufferSize];
2403 uint buf2[BufferSize];
2404 uint *b = buffer;
2405 while (length) {
2406 int len = qMin(length, BufferSize / 2);
2407 fetcher(buf1, buf2, len, data->texture, fx, fy, fdx, fdy);
2408 layout->convertToARGB32PM(buf1, len * 2, clut);
2409 layout->convertToARGB32PM(buf2, len * 2, clut);
2410
2411 if (hasFastInterpolate4() || qAbs(data->m11) < qreal(1./8.) || qAbs(data->m22) < qreal(1./8.)) {
2412 // If we are zooming more than 8 times, we use 8bit precision for the position.
2413 for (int i = 0; i < len; ++i) {
2414 int distx = (fx & 0x0000ffff) >> 8;
2415 int disty = (fy & 0x0000ffff) >> 8;
2416
2417 b[i] = interpolate_4_pixels(buf1 + i * 2, buf2 + i * 2, distx, disty);
2418 fx += fdx;
2419 fy += fdy;
2420 }
2421 } else {
2422 // We are zooming less than 8x, use 4bit precision
2423 for (int i = 0; i < len; ++i) {
2424 uint tl = buf1[i * 2 + 0];
2425 uint tr = buf1[i * 2 + 1];
2426 uint bl = buf2[i * 2 + 0];
2427 uint br = buf2[i * 2 + 1];
2428
2429 int distx = ((fx & 0x0000ffff) + 0x0800) >> 12;
2430 int disty = ((fy & 0x0000ffff) + 0x0800) >> 12;
2431
2432 b[i] = interpolate_4_pixels_16(tl, tr, bl, br, distx, disty);
2433 fx += fdx;
2434 fy += fdy;
2435 }
2436 }
2437
2438 length -= len;
2439 b += len;
2440 }
2441 }
2442 } else {
2443 const auto fetcher = fetchTransformedBilinear_slow_fetcher<blendType,bpp,uint>;
2444
2445 const qreal fdx = data->m11;
2446 const qreal fdy = data->m12;
2447 const qreal fdw = data->m13;
2448
2449 qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
2450 qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
2451 qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
2452
2453 uint buf1[BufferSize];
2454 uint buf2[BufferSize];
2455 uint *b = buffer;
2456
2457 ushort distxs[BufferSize / 2];
2458 ushort distys[BufferSize / 2];
2459
2460 while (length) {
2461 const int len = qMin(length, BufferSize / 2);
2462 fetcher(buf1, buf2, distxs, distys, len, data->texture, fx, fy, fw, fdx, fdy, fdw);
2463
2464 layout->convertToARGB32PM(buf1, len * 2, clut);
2465 layout->convertToARGB32PM(buf2, len * 2, clut);
2466
2467 for (int i = 0; i < len; ++i) {
2468 const int distx = distxs[i] >> 8;
2469 const int disty = distys[i] >> 8;
2470
2471 b[i] = interpolate_4_pixels(buf1 + i * 2, buf2 + i * 2, distx, disty);
2472 }
2473 length -= len;
2474 b += len;
2475 }
2476 }
2477
2478 return buffer;
2479}
2480
2481#if QT_CONFIG(raster_64bit)
2482template<TextureBlendType blendType>
2483static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint32(QRgba64 *buffer, const QSpanData *data,
2484 int y, int x, int length)
2485{
2486 const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
2487 const auto *clut = data->texture.colorTable;
2488 const auto convert = layout->convertToRGBA64PM;
2489
2490 const qreal cx = x + qreal(0.5);
2491 const qreal cy = y + qreal(0.5);
2492
2493 uint sbuf1[BufferSize];
2494 uint sbuf2[BufferSize];
2495 alignas(8) QRgba64 buf1[BufferSize];
2496 alignas(8) QRgba64 buf2[BufferSize];
2497 QRgba64 *b = buffer;
2498
2499 if (canUseFastMatrixPath(cx, cy, length, data)) {
2500 // The increment pr x in the scanline
2501 const int fdx = (int)(data->m11 * fixed_scale);
2502 const int fdy = (int)(data->m12 * fixed_scale);
2503
2504 int fx = int((data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale);
2505 int fy = int((data->m22 * cy + data->m12 * cx + data->dy) * fixed_scale);
2506
2507 fx -= half_point;
2508 fy -= half_point;
2509
2510 const auto fetcher =
2511 (layout->bpp == QPixelLayout::BPP32)
2512 ? fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPP32, uint>
2513 : fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPPNone, uint>;
2514
2515 if (fdy == 0) { //simple scale, no rotation
2516 while (length) {
2517 const int len = qMin(length, BufferSize / 2);
2518 const int disty = (fy & 0x0000ffff);
2519#if defined(__SSE2__)
2520 const __m128i vdy = _mm_set1_epi16(disty);
2521 const __m128i vidy = _mm_set1_epi16(0x10000 - disty);
2522#endif
2523 fetcher(sbuf1, sbuf2, len, data->texture, fx, fy, fdx, fdy);
2524
2525 convert(buf1, sbuf1, len * 2, clut, nullptr);
2526 if (disty)
2527 convert(buf2, sbuf2, len * 2, clut, nullptr);
2528
2529 for (int i = 0; i < len; ++i) {
2530 const int distx = (fx & 0x0000ffff);
2531#if defined(__SSE2__)
2532 __m128i vt = _mm_loadu_si128((const __m128i*)(buf1 + i*2));
2533 if (disty) {
2534 __m128i vb = _mm_loadu_si128((const __m128i*)(buf2 + i*2));
2535 vt = _mm_mulhi_epu16(vt, vidy);
2536 vb = _mm_mulhi_epu16(vb, vdy);
2537 vt = _mm_add_epi16(vt, vb);
2538 }
2539 if (distx) {
2540 const __m128i vdistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(distx), _MM_SHUFFLE(0, 0, 0, 0));
2541 const __m128i vidistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(0x10000 - distx), _MM_SHUFFLE(0, 0, 0, 0));
2542 vt = _mm_mulhi_epu16(vt, _mm_unpacklo_epi64(vidistx, vdistx));
2543 vt = _mm_add_epi16(vt, _mm_srli_si128(vt, 8));
2544 }
2545 _mm_storel_epi64((__m128i*)(b+i), vt);
2546#else
2547 b[i] = interpolate_4_pixels_rgb64(buf1 + i*2, buf2 + i*2, distx, disty);
2548#endif
2549 fx += fdx;
2550 }
2551 length -= len;
2552 b += len;
2553 }
2554 } else { // rotation or shear
2555 while (length) {
2556 const int len = qMin(length, BufferSize / 2);
2557
2558 fetcher(sbuf1, sbuf2, len, data->texture, fx, fy, fdx, fdy);
2559
2560 convert(buf1, sbuf1, len * 2, clut, nullptr);
2561 convert(buf2, sbuf2, len * 2, clut, nullptr);
2562
2563 for (int i = 0; i < len; ++i) {
2564 const int distx = (fx & 0x0000ffff);
2565 const int disty = (fy & 0x0000ffff);
2566 b[i] = interpolate_4_pixels_rgb64(buf1 + i*2, buf2 + i*2, distx, disty);
2567 fx += fdx;
2568 fy += fdy;
2569 }
2570
2571 length -= len;
2572 b += len;
2573 }
2574 }
2575 } else { // !(data->fast_matrix)
2576 const auto fetcher =
2577 (layout->bpp == QPixelLayout::BPP32)
2578 ? fetchTransformedBilinear_slow_fetcher<blendType, QPixelLayout::BPP32, uint>
2580
2581 const qreal fdx = data->m11;
2582 const qreal fdy = data->m12;
2583 const qreal fdw = data->m13;
2584
2585 qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
2586 qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
2587 qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
2588
2589 ushort distxs[BufferSize / 2];
2590 ushort distys[BufferSize / 2];
2591
2592 while (length) {
2593 const int len = qMin(length, BufferSize / 2);
2594 fetcher(sbuf1, sbuf2, distxs, distys, len, data->texture, fx, fy, fw, fdx, fdy, fdw);
2595
2596 convert(buf1, sbuf1, len * 2, clut, nullptr);
2597 convert(buf2, sbuf2, len * 2, clut, nullptr);
2598
2599 for (int i = 0; i < len; ++i) {
2600 const int distx = distxs[i];
2601 const int disty = distys[i];
2602 b[i] = interpolate_4_pixels_rgb64(buf1 + i*2, buf2 + i*2, distx, disty);
2603 }
2604
2605 length -= len;
2606 b += len;
2607 }
2608 }
2609 return buffer;
2610}
2611
2612template<TextureBlendType blendType>
2613static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint64(QRgba64 *buffer, const QSpanData *data,
2614 int y, int x, int length)
2615{
2616 const auto convert = convert64ToRGBA64PM[data->texture.format];
2617
2618 const qreal cx = x + qreal(0.5);
2619 const qreal cy = y + qreal(0.5);
2620
2621 alignas(8) QRgba64 buf1[BufferSize];
2622 alignas(8) QRgba64 buf2[BufferSize];
2623 QRgba64 *end = buffer + length;
2624 QRgba64 *b = buffer;
2625
2626 if (canUseFastMatrixPath(cx, cy, length, data)) {
2627 // The increment pr x in the scanline
2628 const int fdx = (int)(data->m11 * fixed_scale);
2629 const int fdy = (int)(data->m12 * fixed_scale);
2630
2631 int fx = int((data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale);
2632 int fy = int((data->m22 * cy + data->m12 * cx + data->dy) * fixed_scale);
2633
2634 fx -= half_point;
2635 fy -= half_point;
2636 const auto fetcher = fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPP64, QRgba64>;
2637
2638 if (fdy == 0) { //simple scale, no rotation
2639 while (length) {
2640 int len = qMin(length, BufferSize / 2);
2641 int disty = (fy & 0x0000ffff);
2642#if defined(__SSE2__)
2643 const __m128i vdy = _mm_set1_epi16(disty);
2644 const __m128i vidy = _mm_set1_epi16(0x10000 - disty);
2645#endif
2646 fetcher(buf1, buf2, len, data->texture, fx, fy, fdx, fdy);
2647
2648 convert(buf1, len * 2);
2649 if (disty)
2650 convert(buf2, len * 2);
2651
2652 for (int i = 0; i < len; ++i) {
2653 int distx = (fx & 0x0000ffff);
2654#if defined(__SSE2__)
2655 __m128i vt = _mm_loadu_si128((const __m128i*)(buf1 + i*2));
2656 if (disty) {
2657 __m128i vb = _mm_loadu_si128((const __m128i*)(buf2 + i*2));
2658 vt = _mm_mulhi_epu16(vt, vidy);
2659 vb = _mm_mulhi_epu16(vb, vdy);
2660 vt = _mm_add_epi16(vt, vb);
2661 }
2662 if (distx) {
2663 const __m128i vdistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(distx), _MM_SHUFFLE(0, 0, 0, 0));
2664 const __m128i vidistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(0x10000 - distx), _MM_SHUFFLE(0, 0, 0, 0));
2665 vt = _mm_mulhi_epu16(vt, _mm_unpacklo_epi64(vidistx, vdistx));
2666 vt = _mm_add_epi16(vt, _mm_srli_si128(vt, 8));
2667 }
2668 _mm_storel_epi64((__m128i*)(b+i), vt);
2669#else
2670 b[i] = interpolate_4_pixels_rgb64(buf1 + i*2, buf2 + i*2, distx, disty);
2671#endif
2672 fx += fdx;
2673 }
2674 length -= len;
2675 b += len;
2676 }
2677 } else { // rotation or shear
2678 while (b < end) {
2679 int len = qMin(length, BufferSize / 2);
2680
2681 fetcher(buf1, buf2, len, data->texture, fx, fy, fdx, fdy);
2682
2683 convert(buf1, len * 2);
2684 convert(buf2, len * 2);
2685
2686 for (int i = 0; i < len; ++i) {
2687 int distx = (fx & 0x0000ffff);
2688 int disty = (fy & 0x0000ffff);
2689 b[i] = interpolate_4_pixels_rgb64(buf1 + i*2, buf2 + i*2, distx, disty);
2690 fx += fdx;
2691 fy += fdy;
2692 }
2693
2694 length -= len;
2695 b += len;
2696 }
2697 }
2698 } else { // !(data->fast_matrix)
2699 const auto fetcher = fetchTransformedBilinear_slow_fetcher<blendType, QPixelLayout::BPP64, QRgba64>;
2700
2701 const qreal fdx = data->m11;
2702 const qreal fdy = data->m12;
2703 const qreal fdw = data->m13;
2704
2705 qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
2706 qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
2707 qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
2708
2709 ushort distxs[BufferSize / 2];
2710 ushort distys[BufferSize / 2];
2711
2712 while (length) {
2713 const int len = qMin(length, BufferSize / 2);
2714 fetcher(buf1, buf2, distxs, distys, len, data->texture, fx, fy, fw, fdx, fdy, fdw);
2715
2716 convert(buf1, len * 2);
2717 convert(buf2, len * 2);
2718
2719 for (int i = 0; i < len; ++i) {
2720 const int distx = distxs[i];
2721 const int disty = distys[i];
2722 b[i] = interpolate_4_pixels_rgb64(buf1 + i*2, buf2 + i*2, distx, disty);
2723 }
2724
2725 length -= len;
2726 b += len;
2727 }
2728 }
2729 return buffer;
2730}
2731
2732template<TextureBlendType blendType>
2733static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_f32x4(QRgba64 *buffer, const QSpanData *data,
2734 int y, int x, int length)
2735{
2736 const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
2737 const auto *clut = data->texture.colorTable;
2738 const auto convert = layout->fetchToRGBA64PM;
2739
2740 const qreal cx = x + qreal(0.5);
2741 const qreal cy = y + qreal(0.5);
2742
2743 QRgbaFloat32 sbuf1[BufferSize];
2744 QRgbaFloat32 sbuf2[BufferSize];
2745 alignas(8) QRgba64 buf1[BufferSize];
2746 alignas(8) QRgba64 buf2[BufferSize];
2747 QRgba64 *b = buffer;
2748
2749 if (canUseFastMatrixPath(cx, cy, length, data)) {
2750 // The increment pr x in the scanline
2751 const int fdx = (int)(data->m11 * fixed_scale);
2752 const int fdy = (int)(data->m12 * fixed_scale);
2753
2754 int fx = int((data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale);
2755 int fy = int((data->m22 * cy + data->m12 * cx + data->dy) * fixed_scale);
2756
2757 fx -= half_point;
2758 fy -= half_point;
2759
2760 const auto fetcher = fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPP32FPx4, QRgbaFloat32>;
2761
2762 const bool skipsecond = (fdy == 0) && ((fy & 0x0000ffff) == 0);
2763 while (length) {
2764 const int len = qMin(length, BufferSize / 2);
2765
2766 fetcher(sbuf1, sbuf2, len, data->texture, fx, fy, fdx, fdy);
2767
2768 convert(buf1, (const uchar *)sbuf1, 0, len * 2, clut, nullptr);
2769 if (!skipsecond)
2770 convert(buf2, (const uchar *)sbuf2, 0, len * 2, clut, nullptr);
2771
2772 for (int i = 0; i < len; ++i) {
2773 const int distx = (fx & 0x0000ffff);
2774 const int disty = (fy & 0x0000ffff);
2775 b[i] = interpolate_4_pixels_rgb64(buf1 + i*2, buf2 + i*2, distx, disty);
2776 fx += fdx;
2777 fy += fdy;
2778 }
2779
2780 length -= len;
2781 b += len;
2782 }
2783 } else { // !(data->fast_matrix)
2784 const auto fetcher = fetchTransformedBilinear_slow_fetcher<blendType, QPixelLayout::BPP32FPx4, QRgbaFloat32>;
2785
2786 const qreal fdx = data->m11;
2787 const qreal fdy = data->m12;
2788 const qreal fdw = data->m13;
2789
2790 qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
2791 qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
2792 qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
2793
2794 ushort distxs[BufferSize / 2];
2795 ushort distys[BufferSize / 2];
2796
2797 while (length) {
2798 const int len = qMin(length, BufferSize / 2);
2799 fetcher(sbuf1, sbuf2, distxs, distys, len, data->texture, fx, fy, fw, fdx, fdy, fdw);
2800
2801 convert(buf1, (const uchar *)sbuf1, 0, len * 2, clut, nullptr);
2802 convert(buf2, (const uchar *)sbuf2, 0, len * 2, clut, nullptr);
2803
2804 for (int i = 0; i < len; ++i) {
2805 const int distx = distxs[i];
2806 const int disty = distys[i];
2807 b[i] = interpolate_4_pixels_rgb64(buf1 + i*2, buf2 + i*2, distx, disty);
2808 }
2809
2810 length -= len;
2811 b += len;
2812 }
2813 }
2814 return buffer;
2815}
2816
2817template<TextureBlendType blendType>
2818static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, const Operator *,
2819 const QSpanData *data, int y, int x, int length)
2820{
2821 switch (qPixelLayouts[data->texture.format].bpp) {
2824 return fetchTransformedBilinear64_uint64<blendType>(buffer, data, y, x, length);
2826 return fetchTransformedBilinear64_f32x4<blendType>(buffer, data, y, x, length);
2827 default:
2828 return fetchTransformedBilinear64_uint32<blendType>(buffer, data, y, x, length);
2829 }
2830}
2831#endif
2832
2833#if QT_CONFIG(raster_fp)
2834static void interpolate_simple_rgba32f(QRgbaFloat32 *b, const QRgbaFloat32 *buf1, const QRgbaFloat32 *buf2, int len,
2835 int &fx, int fdx,
2836 int &fy, int fdy)
2837{
2838 for (int i = 0; i < len; ++i) {
2839 const int distx = (fx & 0x0000ffff);
2840 const int disty = (fy & 0x0000ffff);
2841 b[i] = interpolate_4_pixels_rgba32f(buf1 + i*2, buf2 + i*2, distx, disty);
2842 fx += fdx;
2843 fy += fdy;
2844 }
2845}
2846
2847static void interpolate_perspective_rgba32f(QRgbaFloat32 *b, const QRgbaFloat32 *buf1, const QRgbaFloat32 *buf2, int len,
2848 unsigned short *distxs,
2849 unsigned short *distys)
2850{
2851 for (int i = 0; i < len; ++i) {
2852 const int dx = distxs[i];
2853 const int dy = distys[i];
2854 b[i] = interpolate_4_pixels_rgba32f(buf1 + i*2, buf2 + i*2, dx, dy);
2855 }
2856}
2857
2858template<TextureBlendType blendType>
2859static const QRgbaFloat32 *QT_FASTCALL fetchTransformedBilinearFP_uint32(QRgbaFloat32 *buffer, const QSpanData *data,
2860 int y, int x, int length)
2861{
2862 const QPixelLayout *layout = &qPixelLayouts[data->texture.format];
2863 const auto *clut = data->texture.colorTable;
2864 const auto convert = qConvertToRGBA32F[data->texture.format];
2865
2866 const qreal cx = x + qreal(0.5);
2867 const qreal cy = y + qreal(0.5);
2868
2869 uint sbuf1[BufferSize];
2870 uint sbuf2[BufferSize];
2874
2875 if (canUseFastMatrixPath(cx, cy, length, data)) {
2876 // The increment pr x in the scanline
2877 const int fdx = (int)(data->m11 * fixed_scale);
2878 const int fdy = (int)(data->m12 * fixed_scale);
2879
2880 int fx = int((data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale);
2881 int fy = int((data->m22 * cy + data->m12 * cx + data->dy) * fixed_scale);
2882
2883 fx -= half_point;
2884 fy -= half_point;
2885
2886 const auto fetcher =
2887 (layout->bpp == QPixelLayout::BPP32)
2888 ? fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPP32, uint>
2889 : fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPPNone, uint>;
2890
2891 const bool skipsecond = (fdy == 0) && ((fy & 0x0000ffff) == 0);
2892 while (length) {
2893 const int len = qMin(length, BufferSize / 2);
2894 fetcher(sbuf1, sbuf2, len, data->texture, fx, fy, fdx, fdy);
2895
2896 convert(buf1, sbuf1, len * 2, clut, nullptr);
2897 if (!skipsecond)
2898 convert(buf2, sbuf2, len * 2, clut, nullptr);
2899
2900 interpolate_simple_rgba32f(b, buf1, buf2, len, fx, fdx, fy, fdy);
2901
2902 length -= len;
2903 b += len;
2904 }
2905 } else { // !(data->fast_matrix)
2906 const auto fetcher =
2907 (layout->bpp == QPixelLayout::BPP32)
2908 ? fetchTransformedBilinear_slow_fetcher<blendType, QPixelLayout::BPP32, uint>
2910
2911 const qreal fdx = data->m11;
2912 const qreal fdy = data->m12;
2913 const qreal fdw = data->m13;
2914 qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
2915 qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
2916 qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
2917 ushort distxs[BufferSize / 2];
2918 ushort distys[BufferSize / 2];
2919
2920 while (length) {
2921 const int len = qMin(length, BufferSize / 2);
2922 fetcher(sbuf1, sbuf2, distxs, distys, len, data->texture, fx, fy, fw, fdx, fdy, fdw);
2923
2924 convert(buf1, sbuf1, len * 2, clut, nullptr);
2925 convert(buf2, sbuf2, len * 2, clut, nullptr);
2926
2927 interpolate_perspective_rgba32f(b, buf1, buf2, len, distxs, distys);
2928
2929 length -= len;
2930 b += len;
2931 }
2932 }
2933 return buffer;
2934}
2935
2936template<TextureBlendType blendType>
2937static const QRgbaFloat32 *QT_FASTCALL fetchTransformedBilinearFP_uint64(QRgbaFloat32 *buffer, const QSpanData *data,
2938 int y, int x, int length)
2939{
2940 const auto convert = convert64ToRGBA32F[data->texture.format];
2941
2942 const qreal cx = x + qreal(0.5);
2943 const qreal cy = y + qreal(0.5);
2944
2945 quint64 sbuf1[BufferSize];
2946 quint64 sbuf2[BufferSize];
2950
2951 if (canUseFastMatrixPath(cx, cy, length, data)) {
2952 // The increment pr x in the scanline
2953 const int fdx = (int)(data->m11 * fixed_scale);
2954 const int fdy = (int)(data->m12 * fixed_scale);
2955
2956 int fx = int((data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale);
2957 int fy = int((data->m22 * cy + data->m12 * cx + data->dy) * fixed_scale);
2958
2959 fx -= half_point;
2960 fy -= half_point;
2961 const auto fetcher = fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPP64, quint64>;
2962
2963 const bool skipsecond = (fdy == 0) && ((fy & 0x0000ffff) == 0);
2964 while (length) {
2965 const int len = qMin(length, BufferSize / 2);
2966 fetcher(sbuf1, sbuf2, len, data->texture, fx, fy, fdx, fdy);
2967
2968 convert(buf1, sbuf1, len * 2);
2969 if (!skipsecond)
2970 convert(buf2, sbuf2, len * 2);
2971
2972 interpolate_simple_rgba32f(b, buf1, buf2, len, fx, fdx, fy, fdy);
2973
2974 length -= len;
2975 b += len;
2976 }
2977 } else { // !(data->fast_matrix)
2978 const auto fetcher = fetchTransformedBilinear_slow_fetcher<blendType, QPixelLayout::BPP64, quint64>;
2979
2980 const qreal fdx = data->m11;
2981 const qreal fdy = data->m12;
2982 const qreal fdw = data->m13;
2983
2984 qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
2985 qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
2986 qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
2987
2988 ushort distxs[BufferSize / 2];
2989 ushort distys[BufferSize / 2];
2990
2991 while (length) {
2992 const int len = qMin(length, BufferSize / 2);
2993 fetcher(sbuf1, sbuf2, distxs, distys, len, data->texture, fx, fy, fw, fdx, fdy, fdw);
2994
2995 convert(buf1, sbuf1, len * 2);
2996 convert(buf2, sbuf2, len * 2);
2997
2998 interpolate_perspective_rgba32f(b, buf1, buf2, len, distxs, distys);
2999
3000 length -= len;
3001 b += len;
3002 }
3003 }
3004 return buffer;
3005}
3006
3007template<TextureBlendType blendType>
3008static const QRgbaFloat32 *QT_FASTCALL fetchTransformedBilinearFP(QRgbaFloat32 *buffer, const QSpanData *data,
3009 int y, int x, int length)
3010{
3011 const auto convert = data->rasterBuffer->format == QImage::Format_RGBA32FPx4 ? convertRGBA32FToRGBA32FPM
3012 : convertRGBA32FToRGBA32F;
3013
3014 const qreal cx = x + qreal(0.5);
3015 const qreal cy = y + qreal(0.5);
3016
3020
3021 if (canUseFastMatrixPath(cx, cy, length, data)) {
3022 // The increment pr x in the scanline
3023 const int fdx = (int)(data->m11 * fixed_scale);
3024 const int fdy = (int)(data->m12 * fixed_scale);
3025
3026 int fx = int((data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale);
3027 int fy = int((data->m22 * cy + data->m12 * cx + data->dy) * fixed_scale);
3028
3029 fx -= half_point;
3030 fy -= half_point;
3031 const auto fetcher = fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPP32FPx4, QRgbaFloat32>;
3032
3033 const bool skipsecond = (fdy == 0) && ((fy & 0x0000ffff) == 0);
3034 while (length) {
3035 const int len = qMin(length, BufferSize / 2);
3036 fetcher(buf1, buf2, len, data->texture, fx, fy, fdx, fdy);
3037
3038 convert(buf1, len * 2);
3039 if (!skipsecond)
3040 convert(buf2, len * 2);
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 = fetchTransformedBilinear_slow_fetcher<blendType, QPixelLayout::BPP32FPx4, QRgbaFloat32>;
3049
3050 const qreal fdx = data->m11;
3051 const qreal fdy = data->m12;
3052 const qreal fdw = data->m13;
3053
3054 qreal fx = data->m21 * cy + data->m11 * cx + data->dx;
3055 qreal fy = data->m22 * cy + data->m12 * cx + data->dy;
3056 qreal fw = data->m23 * cy + data->m13 * cx + data->m33;
3057
3058 ushort distxs[BufferSize / 2];
3059 ushort distys[BufferSize / 2];
3060
3061 while (length) {
3062 const int len = qMin(length, BufferSize / 2);
3063 fetcher(buf1, buf2, distxs, distys, len, data->texture, fx, fy, fw, fdx, fdy, fdw);
3064
3065 convert(buf1, len * 2);
3066 convert(buf2, len * 2);
3067
3068 interpolate_perspective_rgba32f(b, buf1, buf2, len, distxs, distys);
3069
3070 length -= len;
3071 b += len;
3072 }
3073 }
3074 return buffer;
3075}
3076
3077template<TextureBlendType blendType>
3078static const QRgbaFloat32 *QT_FASTCALL fetchTransformedBilinearFP(QRgbaFloat32 *buffer, const Operator *,
3079 const QSpanData *data, int y, int x, int length)
3080{
3081 switch (qPixelLayouts[data->texture.format].bpp) {
3084 return fetchTransformedBilinearFP_uint64<blendType>(buffer, data, y, x, length);
3086 return fetchTransformedBilinearFP<blendType>(buffer, data, y, x, length);
3087 default:
3088 return fetchTransformedBilinearFP_uint32<blendType>(buffer, data, y, x, length);
3089 }
3090}
3091#endif // QT_CONFIG(raster_fp)
3092
3093// FetchUntransformed can have more specialized methods added depending on SIMD features.
3095 nullptr, // Invalid
3096 fetchUntransformed, // Mono
3097 fetchUntransformed, // MonoLsb
3098 fetchUntransformed, // Indexed8
3100 fetchUntransformed, // ARGB32
3101 fetchUntransformedARGB32PM, // ARGB32_Premultiplied
3102 fetchUntransformedRGB16, // RGB16
3103 fetchUntransformed, // ARGB8565_Premultiplied
3104 fetchUntransformed, // RGB666
3105 fetchUntransformed, // ARGB6666_Premultiplied
3106 fetchUntransformed, // RGB555
3107 fetchUntransformed, // ARGB8555_Premultiplied
3108 fetchUntransformed, // RGB888
3109 fetchUntransformed, // RGB444
3110 fetchUntransformed, // ARGB4444_Premultiplied
3111 fetchUntransformed, // RGBX8888
3112 fetchUntransformed, // RGBA8888
3113 fetchUntransformed, // RGBA8888_Premultiplied
3114 fetchUntransformed, // Format_BGR30
3115 fetchUntransformed, // Format_A2BGR30_Premultiplied
3116 fetchUntransformed, // Format_RGB30
3117 fetchUntransformed, // Format_A2RGB30_Premultiplied
3118 fetchUntransformed, // Alpha8
3119 fetchUntransformed, // Grayscale8
3120 fetchUntransformed, // RGBX64
3121 fetchUntransformed, // RGBA64
3122 fetchUntransformed, // RGBA64_Premultiplied
3123 fetchUntransformed, // Grayscale16
3124 fetchUntransformed, // BGR888
3125 fetchUntransformed, // RGBX16FPx4
3126 fetchUntransformed, // RGBA16FPx4
3127 fetchUntransformed, // RGBA16FPx4_Premultiplied
3128 fetchUntransformed, // RGBX32Px4
3129 fetchUntransformed, // RGBA32FPx4
3130 fetchUntransformed, // RGBA32FPx4_Premultiplied
3131 fetchUntransformed, // CMYK8888
3132};
3133
3134static_assert(std::size(sourceFetchUntransformed) == QImage::NImageFormats);
3135
3137 fetchUntransformed, // Untransformed
3138 fetchUntransformed, // Tiled
3139 fetchTransformed<BlendTransformed, QPixelLayout::BPPNone>, // Transformed
3140 fetchTransformed<BlendTransformedTiled, QPixelLayout::BPPNone>, // TransformedTiled
3141 fetchTransformedBilinear<BlendTransformedBilinear, QPixelLayout::BPPNone>, // TransformedBilinear
3142 fetchTransformedBilinear<BlendTransformedBilinearTiled, QPixelLayout::BPPNone> // TransformedBilinearTiled
3143};
3144
3145static_assert(std::size(sourceFetchGeneric) == NBlendTypes);
3146
3148 fetchUntransformedARGB32PM, // Untransformed
3150 fetchTransformed<BlendTransformed, QPixelLayout::BPP32>, // Transformed
3151 fetchTransformed<BlendTransformedTiled, QPixelLayout::BPP32>, // TransformedTiled
3152 fetchTransformedBilinearARGB32PM<BlendTransformedBilinear>, // Bilinear
3153 fetchTransformedBilinearARGB32PM<BlendTransformedBilinearTiled> // BilinearTiled
3154};
3155
3156static_assert(std::size(sourceFetchARGB32PM) == NBlendTypes);
3157
3159 fetchUntransformed, // Untransformed
3160 fetchUntransformed, // Tiled
3161 fetchTransformed<BlendTransformed, QPixelLayout::BPP16>, // Transformed
3162 fetchTransformed<BlendTransformedTiled, QPixelLayout::BPP16>, // TransformedTiled
3163 fetchTransformedBilinear<BlendTransformedBilinear, QPixelLayout::BPP16>, // TransformedBilinear
3164 fetchTransformedBilinear<BlendTransformedBilinearTiled, QPixelLayout::BPP16> // TransformedBilinearTiled
3165};
3166
3167static_assert(std::size(sourceFetchAny16) == NBlendTypes);
3168
3170 fetchUntransformed, // Untransformed
3171 fetchUntransformed, // Tiled
3172 fetchTransformed<BlendTransformed, QPixelLayout::BPP32>, // Transformed
3173 fetchTransformed<BlendTransformedTiled, QPixelLayout::BPP32>, // TransformedTiled
3174 fetchTransformedBilinear<BlendTransformedBilinear, QPixelLayout::BPP32>, // TransformedBilinear
3175 fetchTransformedBilinear<BlendTransformedBilinearTiled, QPixelLayout::BPP32> // TransformedBilinearTiled
3176};
3177
3178static_assert(std::size(sourceFetchAny32) == NBlendTypes);
3179
3181{
3183 return sourceFetchARGB32PM[blendType];
3184 if (blendType == BlendUntransformed || blendType == BlendTiled)
3187 return sourceFetchAny16[blendType];
3189 return sourceFetchAny32[blendType];
3190 return sourceFetchGeneric[blendType];
3191}
3192
3193#if QT_CONFIG(raster_64bit)
3194static const SourceFetchProc64 sourceFetchGeneric64[] = {
3195 fetchUntransformed64, // Untransformed
3196 fetchUntransformed64, // Tiled
3197 fetchTransformed64<BlendTransformed>, // Transformed
3198 fetchTransformed64<BlendTransformedTiled>, // TransformedTiled
3199 fetchTransformedBilinear64<BlendTransformedBilinear>, // Bilinear
3200 fetchTransformedBilinear64<BlendTransformedBilinearTiled> // BilinearTiled
3201};
3202
3203static_assert(std::size(sourceFetchGeneric64) == NBlendTypes);
3204
3205static const SourceFetchProc64 sourceFetchRGBA64PM[] = {
3206 fetchUntransformedRGBA64PM, // Untransformed
3207 fetchUntransformedRGBA64PM, // Tiled
3208 fetchTransformed64<BlendTransformed>, // Transformed
3209 fetchTransformed64<BlendTransformedTiled>, // TransformedTiled
3210 fetchTransformedBilinear64<BlendTransformedBilinear>, // Bilinear
3211 fetchTransformedBilinear64<BlendTransformedBilinearTiled> // BilinearTiled
3212};
3213
3214static_assert(std::size(sourceFetchRGBA64PM) == NBlendTypes);
3215
3216static inline SourceFetchProc64 getSourceFetch64(TextureBlendType blendType, QImage::Format format)
3217{
3219 return sourceFetchRGBA64PM[blendType];
3220 return sourceFetchGeneric64[blendType];
3221}
3222#endif
3223
3224#if QT_CONFIG(raster_fp)
3225static const SourceFetchProcFP sourceFetchGenericFP[] = {
3226 fetchUntransformedFP, // Untransformed
3227 fetchUntransformedFP, // Tiled
3228 fetchTransformedFP<BlendTransformed>, // Transformed
3229 fetchTransformedFP<BlendTransformedTiled>, // TransformedTiled
3230 fetchTransformedBilinearFP<BlendTransformedBilinear>, // Bilinear
3231 fetchTransformedBilinearFP<BlendTransformedBilinearTiled> // BilinearTiled
3232};
3233
3234static_assert(std::size(sourceFetchGenericFP) == NBlendTypes);
3235
3236static inline SourceFetchProcFP getSourceFetchFP(TextureBlendType blendType, QImage::Format /*format*/)
3237{
3238 return sourceFetchGenericFP[blendType];
3239}
3240#endif
3241
3242#define FIXPT_BITS 8
3243#define FIXPT_SIZE (1<<FIXPT_BITS)
3244#define FIXPT_MAX (INT_MAX >> (FIXPT_BITS + 1))
3245
3246static uint qt_gradient_pixel_fixed(const QGradientData *data, int fixed_pos)
3247{
3248 int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
3249 return data->colorTable32[qt_gradient_clamp(data, ipos)];
3250}
3251
3252#if QT_CONFIG(raster_64bit)
3253static const QRgba64& qt_gradient_pixel64_fixed(const QGradientData *data, int fixed_pos)
3254{
3255 int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
3256 return data->colorTable64[qt_gradient_clamp(data, ipos)];
3257}
3258#endif
3259
3260#if QT_CONFIG(raster_fp)
3261static inline QRgbaFloat32 qt_gradient_pixelFP(const QGradientData *data, qreal pos)
3262{
3263 int ipos = int(pos * (GRADIENT_STOPTABLE_SIZE - 1) + qreal(0.5));
3264 QRgba64 rgb64 = data->colorTable64[qt_gradient_clamp(data, ipos)];
3265 return QRgbaFloat32::fromRgba64(rgb64.red(),rgb64.green(), rgb64.blue(), rgb64.alpha());
3266}
3267
3268static inline QRgbaFloat32 qt_gradient_pixelFP_fixed(const QGradientData *data, int fixed_pos)
3269{
3270 int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
3271 QRgba64 rgb64 = data->colorTable64[qt_gradient_clamp(data, ipos)];
3272 return QRgbaFloat32::fromRgba64(rgb64.red(), rgb64.green(), rgb64.blue(), rgb64.alpha());
3273}
3274#endif
3275
3277{
3278 v->dx = data->gradient.linear.end.x - data->gradient.linear.origin.x;
3279 v->dy = data->gradient.linear.end.y - data->gradient.linear.origin.y;
3280 v->l = v->dx * v->dx + v->dy * v->dy;
3281 v->off = 0;
3282 if (v->l != 0) {
3283 v->dx /= v->l;
3284 v->dy /= v->l;
3285 v->off = -v->dx * data->gradient.linear.origin.x - v->dy * data->gradient.linear.origin.y;
3286 }
3287}
3288
3290{
3291public:
3292 typedef uint Type;
3293 static Type null() { return 0; }
3294 static Type fetchSingle(const QGradientData& gradient, qreal v)
3295 {
3296 Q_ASSERT(std::isfinite(v));
3297 return qt_gradient_pixel(&gradient, v);
3298 }
3299 static Type fetchSingle(const QGradientData& gradient, int v)
3300 {
3301 return qt_gradient_pixel_fixed(&gradient, v);
3302 }
3303 static void memfill(Type *buffer, Type fill, int length)
3304 {
3306 }
3307};
3308
3309#if QT_CONFIG(raster_64bit)
3310class GradientBase64
3311{
3312public:
3313 typedef QRgba64 Type;
3314 static Type null() { return QRgba64::fromRgba64(0); }
3315 static Type fetchSingle(const QGradientData& gradient, qreal v)
3316 {
3317 Q_ASSERT(std::isfinite(v));
3318 return qt_gradient_pixel64(&gradient, v);
3319 }
3320 static Type fetchSingle(const QGradientData& gradient, int v)
3321 {
3322 return qt_gradient_pixel64_fixed(&gradient, v);
3323 }
3324 static void memfill(Type *buffer, Type fill, int length)
3325 {
3327 }
3328};
3329#endif
3330
3331#if QT_CONFIG(raster_fp)
3332class GradientBaseFP
3333{
3334public:
3335 typedef QRgbaFloat32 Type;
3336 static Type null() { return QRgbaFloat32::fromRgba64(0,0,0,0); }
3337 static Type fetchSingle(const QGradientData& gradient, qreal v)
3338 {
3339 Q_ASSERT(std::isfinite(v));
3340 return qt_gradient_pixelFP(&gradient, v);
3341 }
3342 static Type fetchSingle(const QGradientData& gradient, int v)
3343 {
3344 return qt_gradient_pixelFP_fixed(&gradient, v);
3345 }
3346 static void memfill(Type *buffer, Type fill, int length)
3347 {
3348 quint64 fillCopy;
3349 memcpy(&fillCopy, &fill, sizeof(quint64));
3350 qt_memfill64((quint64*)buffer, fillCopy, length);
3351 }
3352};
3353#endif
3354
3355template<class GradientBase, typename BlendType>
3356static inline const BlendType * QT_FASTCALL qt_fetch_linear_gradient_template(
3357 BlendType *buffer, const Operator *op, const QSpanData *data,
3358 int y, int x, int length)
3359{
3360 const BlendType *b = buffer;
3361 qreal t, inc;
3362
3363 bool affine = true;
3364 qreal rx=0, ry=0;
3365 if (op->linear.l == 0) {
3366 t = inc = 0;
3367 } else {
3368 rx = data->m21 * (y + qreal(0.5)) + data->m11 * (x + qreal(0.5)) + data->dx;
3369 ry = data->m22 * (y + qreal(0.5)) + data->m12 * (x + qreal(0.5)) + data->dy;
3370 t = op->linear.dx*rx + op->linear.dy*ry + op->linear.off;
3371 inc = op->linear.dx * data->m11 + op->linear.dy * data->m12;
3372 affine = !data->m13 && !data->m23;
3373
3374 if (affine) {
3375 t *= (GRADIENT_STOPTABLE_SIZE - 1);
3376 inc *= (GRADIENT_STOPTABLE_SIZE - 1);
3377 }
3378 }
3379
3380 const BlendType *end = buffer + length;
3381 if (affine) {
3382 if (inc > qreal(-1e-5) && inc < qreal(1e-5)) {
3383 if (std::abs(t) < FIXPT_MAX)
3384 GradientBase::memfill(buffer, GradientBase::fetchSingle(data->gradient, int(t * FIXPT_SIZE)), length);
3385 else
3386 GradientBase::memfill(buffer, GradientBase::fetchSingle(data->gradient, t / GRADIENT_STOPTABLE_SIZE), length);
3387 } else {
3388 if (std::abs(t) < FIXPT_MAX && std::abs(inc) < FIXPT_MAX && std::abs(t + inc * length) < FIXPT_MAX) {
3389 // we can use fixed point math
3390 int t_fixed = int(t * FIXPT_SIZE);
3391 int inc_fixed = int(inc * FIXPT_SIZE);
3392 while (buffer < end) {
3393 *buffer = GradientBase::fetchSingle(data->gradient, t_fixed);
3394 t_fixed += inc_fixed;
3395 ++buffer;
3396 }
3397 } else {
3398 // we have to fall back to float math
3399 while (buffer < end) {
3400 *buffer = GradientBase::fetchSingle(data->gradient, t/GRADIENT_STOPTABLE_SIZE);
3401 t += inc;
3402 ++buffer;
3403 }
3404 }
3405 }
3406 } else { // fall back to float math here as well
3407 qreal rw = data->m23 * (y + qreal(0.5)) + data->m13 * (x + qreal(0.5)) + data->m33;
3408 while (buffer < end) {
3409 qreal x = rx/rw;
3410 qreal y = ry/rw;
3411 t = (op->linear.dx*x + op->linear.dy *y) + op->linear.off;
3412
3413 *buffer = GradientBase::fetchSingle(data->gradient, t);
3414 rx += data->m11;
3415 ry += data->m12;
3416 rw += data->m13;
3417 if (!rw) {
3418 rw += data->m13;
3419 }
3420 ++buffer;
3421 }
3422 }
3423
3424 return b;
3425}
3426
3428 int y, int x, int length)
3429{
3430 return qt_fetch_linear_gradient_template<GradientBase32, uint>(buffer, op, data, y, x, length);
3431}
3432
3433#if QT_CONFIG(raster_64bit)
3434static const QRgba64 * QT_FASTCALL qt_fetch_linear_gradient_rgb64(QRgba64 *buffer, const Operator *op, const QSpanData *data,
3435 int y, int x, int length)
3436{
3437 return qt_fetch_linear_gradient_template<GradientBase64, QRgba64>(buffer, op, data, y, x, length);
3438}
3439#endif
3440#if QT_CONFIG(raster_fp)
3441static const QRgbaFloat32 * QT_FASTCALL qt_fetch_linear_gradient_rgbfp(QRgbaFloat32 *buffer, const Operator *op, const QSpanData *data,
3442 int y, int x, int length)
3443{
3444 return qt_fetch_linear_gradient_template<GradientBaseFP, QRgbaFloat32>(buffer, op, data, y, x, length);
3445}
3446#endif
3447
3449{
3450 v->dx = data->gradient.radial.center.x - data->gradient.radial.focal.x;
3451 v->dy = data->gradient.radial.center.y - data->gradient.radial.focal.y;
3452
3453 v->dr = data->gradient.radial.center.radius - data->gradient.radial.focal.radius;
3454 v->sqrfr = data->gradient.radial.focal.radius * data->gradient.radial.focal.radius;
3455
3456 v->a = v->dr * v->dr - v->dx*v->dx - v->dy*v->dy;
3457
3458 v->extended = !qFuzzyIsNull(data->gradient.radial.focal.radius) || v->a <= 0;
3459}
3460
3461template <class GradientBase>
3462class RadialFetchPlain : public GradientBase
3463{
3464public:
3465 typedef typename GradientBase::Type BlendType;
3467 const Operator *op, const QSpanData *data, qreal det,
3468 qreal delta_det, qreal delta_delta_det, qreal b, qreal delta_b)
3469 {
3470 if (op->radial.extended) {
3471 while (buffer < end) {
3472 BlendType result = GradientBase::null();
3473 if (det >= 0) {
3474 qreal w = qSqrt(det) - b;
3475 if (data->gradient.radial.focal.radius + op->radial.dr * w >= 0)
3476 result = GradientBase::fetchSingle(data->gradient, w);
3477 }
3478
3479 *buffer = result;
3480
3481 det += delta_det;
3482 delta_det += delta_delta_det;
3483 b += delta_b;
3484
3485 ++buffer;
3486 }
3487 } else {
3488 while (buffer < end) {
3489 BlendType result = GradientBase::null();
3490 if (det >= 0) {
3491 qreal w = qSqrt(det) - b;
3492 result = GradientBase::fetchSingle(data->gradient, w);
3493 }
3494
3495 *buffer++ = result;
3496
3497 det += delta_det;
3498 delta_det += delta_delta_det;
3499 b += delta_b;
3500 }
3501 }
3502 }
3503};
3504
3506 int y, int x, int length)
3507{
3508 return qt_fetch_radial_gradient_template<RadialFetchPlain<GradientBase32>, uint>(buffer, op, data, y, x, length);
3509}
3510
3512
3513#if QT_CONFIG(raster_64bit)
3514const QRgba64 * QT_FASTCALL qt_fetch_radial_gradient_rgb64(QRgba64 *buffer, const Operator *op, const QSpanData *data,
3515 int y, int x, int length)
3516{
3517 return qt_fetch_radial_gradient_template<RadialFetchPlain<GradientBase64>, QRgba64>(buffer, op, data, y, x, length);
3518}
3519#endif
3520
3521#if QT_CONFIG(raster_fp)
3522static const QRgbaFloat32 * QT_FASTCALL qt_fetch_radial_gradient_rgbfp(QRgbaFloat32 *buffer, const Operator *op, const QSpanData *data,
3523 int y, int x, int length)
3524{
3525 return qt_fetch_radial_gradient_template<RadialFetchPlain<GradientBaseFP>, QRgbaFloat32>(buffer, op, data, y, x, length);
3526}
3527#endif
3528
3529template <class GradientBase, typename BlendType>
3530static inline const BlendType * QT_FASTCALL qt_fetch_conical_gradient_template(
3531 BlendType *buffer, const QSpanData *data,
3532 int y, int x, int length)
3533{
3534 const BlendType *b = buffer;
3535 qreal rx = data->m21 * (y + qreal(0.5))
3536 + data->dx + data->m11 * (x + qreal(0.5));
3537 qreal ry = data->m22 * (y + qreal(0.5))
3538 + data->dy + data->m12 * (x + qreal(0.5));
3539 bool affine = !data->m13 && !data->m23;
3540
3541 const qreal inv2pi = M_1_PI / 2.0;
3542
3543 const BlendType *end = buffer + length;
3544 if (affine) {
3545 rx -= data->gradient.conical.center.x;
3546 ry -= data->gradient.conical.center.y;
3547 while (buffer < end) {
3548 qreal angle = qAtan2(ry, rx) + data->gradient.conical.angle;
3549
3550 *buffer = GradientBase::fetchSingle(data->gradient, 1 - angle * inv2pi);
3551
3552 rx += data->m11;
3553 ry += data->m12;
3554 ++buffer;
3555 }
3556 } else {
3557 qreal rw = data->m23 * (y + qreal(0.5))
3558 + data->m33 + data->m13 * (x + qreal(0.5));
3559 if (!rw)
3560 rw = 1;
3561 while (buffer < end) {
3562 qreal angle = qAtan2(ry/rw - data->gradient.conical.center.x,
3563 rx/rw - data->gradient.conical.center.y)
3564 + data->gradient.conical.angle;
3565
3566 *buffer = GradientBase::fetchSingle(data->gradient, 1 - angle * inv2pi);
3567
3568 rx += data->m11;
3569 ry += data->m12;
3570 rw += data->m13;
3571 if (!rw) {
3572 rw += data->m13;
3573 }
3574 ++buffer;
3575 }
3576 }
3577 return b;
3578}
3579
3581 int y, int x, int length)
3582{
3583 return qt_fetch_conical_gradient_template<GradientBase32, uint>(buffer, data, y, x, length);
3584}
3585
3586#if QT_CONFIG(raster_64bit)
3587static const QRgba64 * QT_FASTCALL qt_fetch_conical_gradient_rgb64(QRgba64 *buffer, const Operator *, const QSpanData *data,
3588 int y, int x, int length)
3589{
3590 return qt_fetch_conical_gradient_template<GradientBase64, QRgba64>(buffer, data, y, x, length);
3591}
3592#endif
3593
3594#if QT_CONFIG(raster_fp)
3595static const QRgbaFloat32 * QT_FASTCALL qt_fetch_conical_gradient_rgbfp(QRgbaFloat32 *buffer, const Operator *, const QSpanData *data,
3596 int y, int x, int length)
3597{
3598 return qt_fetch_conical_gradient_template<GradientBaseFP, QRgbaFloat32>(buffer, data, y, x, length);
3599}
3600#endif
3601
3605
3607#if QT_CONFIG(raster_64bit)
3608static const CompositionFunctionSolid64 *functionForModeSolid64 = qt_functionForModeSolid64_C;
3609#endif
3610#if QT_CONFIG(raster_fp)
3611static const CompositionFunctionSolidFP *functionForModeSolidFP = qt_functionForModeSolidFP_C;
3612#endif
3613
3617
3619#if QT_CONFIG(raster_64bit)
3620static const CompositionFunction64 *functionForMode64 = qt_functionForMode64_C;
3621#endif
3622#if QT_CONFIG(raster_fp)
3623static const CompositionFunctionFP *functionForModeFP = qt_functionForModeFP_C;
3624#endif
3625
3627{
3629 if (data->texture.type == QTextureData::Pattern)
3630 ft = BlendTiled;
3631 else if (data->txop <= QTransform::TxTranslate)
3632 if (data->texture.type == QTextureData::Tiled)
3633 ft = BlendTiled;
3634 else
3635 ft = BlendUntransformed;
3636 else if (data->bilinear)
3637 if (data->texture.type == QTextureData::Tiled)
3639 else
3641 else
3642 if (data->texture.type == QTextureData::Tiled)
3644 else
3645 ft = BlendTransformed;
3646 return ft;
3647}
3648
3649static inline Operator getOperator(const QSpanData *data, const QT_FT_Span *spans, int spanCount)
3650{
3651 Operator op;
3652 bool solidSource = false;
3653 switch(data->type) {
3654 case QSpanData::Solid:
3655 solidSource = data->solidColor.alphaF() >= 1.0f;
3656 op.srcFetch = nullptr;
3657 op.srcFetch64 = nullptr;
3658 op.srcFetchFP = nullptr;
3659 break;
3661 solidSource = !data->gradient.alphaColor;
3664#if QT_CONFIG(raster_64bit)
3665 op.srcFetch64 = qt_fetch_linear_gradient_rgb64;
3666#endif
3667#if QT_CONFIG(raster_fp)
3668 op.srcFetchFP = qt_fetch_linear_gradient_rgbfp;
3669#endif
3670 break;
3672 solidSource = !data->gradient.alphaColor;
3675#if QT_CONFIG(raster_64bit)
3676 op.srcFetch64 = qt_fetch_radial_gradient_rgb64;
3677#endif
3678#if QT_CONFIG(raster_fp)
3679 op.srcFetchFP = qt_fetch_radial_gradient_rgbfp;
3680#endif
3681 break;
3683 solidSource = !data->gradient.alphaColor;
3685#if QT_CONFIG(raster_64bit)
3686 op.srcFetch64 = qt_fetch_conical_gradient_rgb64;
3687#endif
3688#if QT_CONFIG(raster_fp)
3689 op.srcFetchFP = qt_fetch_conical_gradient_rgbfp;
3690#endif
3691 break;
3692 case QSpanData::Texture:
3693 solidSource = !data->texture.hasAlpha;
3694 op.srcFetch = getSourceFetch(getBlendType(data), data->texture.format);
3695#if QT_CONFIG(raster_64bit)
3696 op.srcFetch64 = getSourceFetch64(getBlendType(data), data->texture.format);
3697#endif
3698#if QT_CONFIG(raster_fp)
3699 op.srcFetchFP = getSourceFetchFP(getBlendType(data), data->texture.format);
3700#endif
3701 break;
3702 default:
3703 Q_UNREACHABLE();
3704 break;
3705 }
3706#if !QT_CONFIG(raster_64bit)
3707 op.srcFetch64 = nullptr;
3708#endif
3709#if !QT_CONFIG(raster_fp)
3710 op.srcFetchFP = nullptr;
3711#endif
3712
3713 op.mode = data->rasterBuffer->compositionMode;
3714 if (op.mode == QPainter::CompositionMode_SourceOver && solidSource)
3716
3717 op.destFetch = destFetchProc[data->rasterBuffer->format];
3718#if QT_CONFIG(raster_64bit)
3719 op.destFetch64 = destFetchProc64[data->rasterBuffer->format];
3720#else
3721 op.destFetch64 = nullptr;
3722#endif
3723#if QT_CONFIG(raster_fp)
3724 op.destFetchFP = destFetchProcFP[data->rasterBuffer->format];
3725#else
3726 op.destFetchFP = nullptr;
3727#endif
3729 (data->type != QSpanData::Texture || data->texture.const_alpha == 256)) {
3730 const QT_FT_Span *lastSpan = spans + spanCount;
3731 bool alphaSpans = false;
3732 while (spans < lastSpan) {
3733 if (spans->coverage != 255) {
3734 alphaSpans = true;
3735 break;
3736 }
3737 ++spans;
3738 }
3739 if (!alphaSpans && spanCount > 0) {
3740 // If all spans are opaque we do not need to fetch dest.
3741 // But don't clear passthrough destFetch as they are just as fast and save destStore.
3742 if (op.destFetch != destFetchARGB32P)
3744#if QT_CONFIG(raster_64bit)
3745 if (op.destFetch64 != destFetchRGB64)
3746 op.destFetch64 = destFetch64Undefined;
3747#endif
3748#if QT_CONFIG(raster_fp)
3749 if (op.destFetchFP != destFetchRGBFP)
3750 op.destFetchFP = destFetchFPUndefined;
3751#endif
3752 }
3753 }
3754
3755 op.destStore = destStoreProc[data->rasterBuffer->format];
3757 op.func = functionForMode[op.mode];
3758#if QT_CONFIG(raster_64bit)
3759 op.destStore64 = destStoreProc64[data->rasterBuffer->format];
3760 op.funcSolid64 = functionForModeSolid64[op.mode];
3761 op.func64 = functionForMode64[op.mode];
3762#else
3763 op.destStore64 = nullptr;
3764 op.funcSolid64 = nullptr;
3765 op.func64 = nullptr;
3766#endif
3767#if QT_CONFIG(raster_fp)
3768 op.destStoreFP = destStoreFP;
3769 op.funcSolidFP = functionForModeSolidFP[op.mode];
3770 op.funcFP = functionForModeFP[op.mode];
3771#else
3772 op.destStoreFP = nullptr;
3773 op.funcSolidFP = nullptr;
3774 op.funcFP = nullptr;
3775#endif
3776
3777 return op;
3778}
3779
3780static void spanfill_from_first(QRasterBuffer *rasterBuffer, QPixelLayout::BPP bpp, int x, int y, int length)
3781{
3782 switch (bpp) {
3784 QRgbaFloat32 *dest = reinterpret_cast<QRgbaFloat32 *>(rasterBuffer->scanLine(y)) + x;
3785 qt_memfill_template(dest + 1, dest[0], length - 1);
3786 break;
3787 }
3789 case QPixelLayout::BPP64: {
3790 quint64 *dest = reinterpret_cast<quint64 *>(rasterBuffer->scanLine(y)) + x;
3791 qt_memfill_template(dest + 1, dest[0], length - 1);
3792 break;
3793 }
3794 case QPixelLayout::BPP32: {
3795 quint32 *dest = reinterpret_cast<quint32 *>(rasterBuffer->scanLine(y)) + x;
3796 qt_memfill_template(dest + 1, dest[0], length - 1);
3797 break;
3798 }
3799 case QPixelLayout::BPP24: {
3800 quint24 *dest = reinterpret_cast<quint24 *>(rasterBuffer->scanLine(y)) + x;
3801 qt_memfill_template(dest + 1, dest[0], length - 1);
3802 break;
3803 }
3804 case QPixelLayout::BPP16: {
3805 quint16 *dest = reinterpret_cast<quint16 *>(rasterBuffer->scanLine(y)) + x;
3806 qt_memfill_template(dest + 1, dest[0], length - 1);
3807 break;
3808 }
3809 case QPixelLayout::BPP8: {
3810 uchar *dest = rasterBuffer->scanLine(y) + x;
3811 memset(dest + 1, dest[0], length - 1);
3812 break;
3813 }
3814 default:
3815 Q_UNREACHABLE();
3816 }
3817}
3818
3819
3820// -------------------- blend methods ---------------------
3821
3822#if defined(QT_USE_THREAD_PARALLEL_FILLS)
3823#define QT_THREAD_PARALLEL_FILLS(function) \
3824 const int segments = (count + 32) / 64; \
3825 QThreadPool *threadPool = QThreadPoolPrivate::qtGuiInstance(); \
3826 if (segments > 1 && qPixelLayouts[data->rasterBuffer->format].bpp >= QPixelLayout::BPP8 \
3827 && threadPool && !threadPool->contains(QThread::currentThread())) { \
3828 QSemaphore semaphore; \
3829 int c = 0; \
3830 for (int i = 0; i < segments; ++i) { \
3831 int cn = (count - c) / (segments - i); \
3832 threadPool->start([&, c, cn]() { \
3833 function(c, c + cn); \
3834 semaphore.release(1); \
3835 }, 1); \
3836 c += cn; \
3837 } \
3838 semaphore.acquire(segments); \
3839 } else \
3840 function(0, count)
3841#else
3842#define QT_THREAD_PARALLEL_FILLS(function) function(0, count)
3843#endif
3844
3845static void blend_color_generic(int count, const QT_FT_Span *spans, void *userData)
3846{
3847 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
3848 const Operator op = getOperator(data, nullptr, 0);
3849 const uint color = data->solidColor.rgba();
3850 const bool solidFill = op.mode == QPainter::CompositionMode_Source;
3851 const QPixelLayout::BPP bpp = qPixelLayouts[data->rasterBuffer->format].bpp;
3852
3853 auto function = [=] (int cStart, int cEnd) {
3854 alignas(16) uint buffer[BufferSize];
3855 for (int c = cStart; c < cEnd; ++c) {
3856 int x = spans[c].x;
3857 int length = spans[c].len;
3858 if (solidFill && bpp >= QPixelLayout::BPP8 && spans[c].coverage == 255 && length && op.destStore) {
3859 // If dest doesn't matter we don't need to bother with blending or converting all the identical pixels
3860 op.destStore(data->rasterBuffer, x, spans[c].y, &color, 1);
3861 spanfill_from_first(data->rasterBuffer, bpp, x, spans[c].y, length);
3862 length = 0;
3863 }
3864
3865 while (length) {
3866 int l = qMin(BufferSize, length);
3867 uint *dest = op.destFetch(buffer, data->rasterBuffer, x, spans[c].y, l);
3868 op.funcSolid(dest, l, color, spans[c].coverage);
3869 if (op.destStore)
3870 op.destStore(data->rasterBuffer, x, spans[c].y, dest, l);
3871 length -= l;
3872 x += l;
3873 }
3874 }
3875 };
3876 QT_THREAD_PARALLEL_FILLS(function);
3877}
3878
3879static void blend_color_argb(int count, const QT_FT_Span *spans, void *userData)
3880{
3881 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
3882
3883 const Operator op = getOperator(data, nullptr, 0);
3884 const uint color = data->solidColor.rgba();
3885
3887 // inline for performance
3888 while (count--) {
3889 uint *target = ((uint *)data->rasterBuffer->scanLine(spans->y)) + spans->x;
3890 if (spans->coverage == 255) {
3891 qt_memfill(target, color, spans->len);
3892#ifdef __SSE2__
3893 } else if (spans->len > 16) {
3894 op.funcSolid(target, spans->len, color, spans->coverage);
3895#endif
3896 } else {
3897 uint c = BYTE_MUL(color, spans->coverage);
3898 int ialpha = 255 - spans->coverage;
3899 for (int i = 0; i < spans->len; ++i)
3900 target[i] = c + BYTE_MUL(target[i], ialpha);
3901 }
3902 ++spans;
3903 }
3904 return;
3905 }
3906 const auto funcSolid = op.funcSolid;
3907 auto function = [=] (int cStart, int cEnd) {
3908 for (int c = cStart; c < cEnd; ++c) {
3909 uint *target = ((uint *)data->rasterBuffer->scanLine(spans[c].y)) + spans[c].x;
3910 funcSolid(target, spans[c].len, color, spans[c].coverage);
3911 }
3912 };
3913 QT_THREAD_PARALLEL_FILLS(function);
3914}
3915
3916static void blend_color_generic_rgb64(int count, const QT_FT_Span *spans, void *userData)
3917{
3918#if QT_CONFIG(raster_64bit)
3919 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
3920 const Operator op = getOperator(data, nullptr, 0);
3921 if (!op.funcSolid64) {
3922 qCDebug(lcQtGuiDrawHelper, "blend_color_generic_rgb64: unsupported 64bit blend attempted, falling back to 32-bit");
3923 return blend_color_generic(count, spans, userData);
3924 }
3925
3926 const QRgba64 color = data->solidColor.rgba64();
3927 const bool solidFill = op.mode == QPainter::CompositionMode_Source;
3928 const QPixelLayout::BPP bpp = qPixelLayouts[data->rasterBuffer->format].bpp;
3929
3930 auto function = [=, &op] (int cStart, int cEnd)
3931 {
3932 alignas(16) QRgba64 buffer[BufferSize];
3933 for (int c = cStart; c < cEnd; ++c) {
3934 int x = spans[c].x;
3935 int length = spans[c].len;
3936 if (solidFill && bpp >= QPixelLayout::BPP8 && spans[c].coverage == 255 && length && op.destStore64) {
3937 // If dest doesn't matter we don't need to bother with blending or converting all the identical pixels
3938 op.destStore64(data->rasterBuffer, x, spans[c].y, &color, 1);
3939 spanfill_from_first(data->rasterBuffer, bpp, x, spans[c].y, length);
3940 length = 0;
3941 }
3942
3943 while (length) {
3944 int l = qMin(BufferSize, length);
3945 QRgba64 *dest = op.destFetch64(buffer, data->rasterBuffer, x, spans[c].y, l);
3946 op.funcSolid64(dest, l, color, spans[c].coverage);
3947 if (op.destStore64)
3948 op.destStore64(data->rasterBuffer, x, spans[c].y, dest, l);
3949 length -= l;
3950 x += l;
3951 }
3952 }
3953 };
3954 QT_THREAD_PARALLEL_FILLS(function);
3955#else
3956 blend_color_generic(count, spans, userData);
3957#endif
3958}
3959
3960static void blend_color_generic_fp(int count, const QT_FT_Span *spans, void *userData)
3961{
3962#if QT_CONFIG(raster_fp)
3963 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
3964 const Operator op = getOperator(data, nullptr, 0);
3965 if (!op.funcSolidFP || !op.destFetchFP) {
3966 qCDebug(lcQtGuiDrawHelper, "blend_color_generic_fp: unsupported 4xF16 blend attempted, falling back to 32-bit");
3967 return blend_color_generic(count, spans, userData);
3968 }
3969
3970 float r, g, b, a;
3971 data->solidColor.getRgbF(&r, &g, &b, &a);
3972 const QRgbaFloat32 color{r, g, b, a};
3973 const bool solidFill = op.mode == QPainter::CompositionMode_Source;
3974 QPixelLayout::BPP bpp = qPixelLayouts[data->rasterBuffer->format].bpp;
3975
3976 auto function = [=, &op] (int cStart, int cEnd)
3977 {
3978 alignas(16) QRgbaFloat32 buffer[BufferSize];
3979 for (int c = cStart; c < cEnd; ++c) {
3980 int x = spans[c].x;
3981 int length = spans[c].len;
3982 if (solidFill && bpp >= QPixelLayout::BPP8 && spans[c].coverage == 255 && length && op.destStoreFP) {
3983 // If dest doesn't matter we don't need to bother with blending or converting all the identical pixels
3984 op.destStoreFP(data->rasterBuffer, x, spans[c].y, &color, 1);
3985 spanfill_from_first(data->rasterBuffer, bpp, x, spans[c].y, length);
3986 length = 0;
3987 }
3988
3989 while (length) {
3990 int l = qMin(BufferSize, length);
3991 QRgbaFloat32 *dest = op.destFetchFP(buffer, data->rasterBuffer, x, spans[c].y, l);
3992 op.funcSolidFP(dest, l, color, spans[c].coverage);
3993 if (op.destStoreFP)
3994 op.destStoreFP(data->rasterBuffer, x, spans[c].y, dest, l);
3995 length -= l;
3996 x += l;
3997 }
3998 }
3999 };
4000 QT_THREAD_PARALLEL_FILLS(function);
4001#else
4002 blend_color_generic(count, spans, userData);
4003#endif
4004}
4005
4006template <typename T>
4007void handleSpans(int count, const QT_FT_Span *spans, const QSpanData *data, const Operator &op)
4008{
4009 const int const_alpha = (data->type == QSpanData::Texture) ? data->texture.const_alpha : 256;
4010 const bool solidSource = op.mode == QPainter::CompositionMode_Source && const_alpha == 256;
4011
4012 auto function = [=, &op] (int cStart, int cEnd)
4013 {
4014 T handler(data, op);
4015 int coverage = 0;
4016 for (int c = cStart; c < cEnd;) {
4017 if (!spans[c].len) {
4018 ++c;
4019 continue;
4020 }
4021 int x = spans[c].x;
4022 const int y = spans[c].y;
4023 int right = x + spans[c].len;
4024 const bool fetchDest = !solidSource || spans[c].coverage < 255;
4025
4026 // compute length of adjacent spans
4027 for (int i = c + 1; i < cEnd && spans[i].y == y && spans[i].x == right && fetchDest == (!solidSource || spans[i].coverage < 255); ++i)
4028 right += spans[i].len;
4029 int length = right - x;
4030
4031 while (length) {
4032 int l = qMin(BufferSize, length);
4033 length -= l;
4034
4035 int process_length = l;
4036 int process_x = x;
4037
4038 const auto *src = handler.fetch(process_x, y, process_length, fetchDest);
4039 int offset = 0;
4040 while (l > 0) {
4041 if (x == spans[c].x) // new span?
4042 coverage = (spans[c].coverage * const_alpha) >> 8;
4043
4044 int right = spans[c].x + spans[c].len;
4045 int len = qMin(l, right - x);
4046
4047 handler.process(x, y, len, coverage, src, offset);
4048
4049 l -= len;
4050 x += len;
4051 offset += len;
4052
4053 if (x == right) // done with current span?
4054 ++c;
4055 }
4056 handler.store(process_x, y, process_length);
4057 }
4058 }
4059 };
4060 QT_THREAD_PARALLEL_FILLS(function);
4061}
4062
4064{
4066 const Operator &op;
4067};
4068
4070{
4071public:
4072 uint *dest = nullptr;
4073 alignas(16) uint buffer[BufferSize];
4074 alignas(16) uint src_buffer[BufferSize];
4076 : QBlendBase{d, o}
4077 {
4078 }
4079
4080 const uint *fetch(int x, int y, int len, bool fetchDest)
4081 {
4082 if (fetchDest || op.destFetch == destFetchARGB32P)
4083 dest = op.destFetch(buffer, data->rasterBuffer, x, y, len);
4084 else
4085 dest = buffer;
4086 return op.srcFetch(src_buffer, &op, data, y, x, len);
4087 }
4088
4089 void process(int, int, int len, int coverage, const uint *src, int offset)
4090 {
4091 op.func(dest + offset, src + offset, len, coverage);
4092 }
4093
4094 void store(int x, int y, int len)
4095 {
4096 if (op.destStore)
4097 op.destStore(data->rasterBuffer, x, y, dest, len);
4098 }
4099};
4100
4101#if QT_CONFIG(raster_64bit)
4102class BlendSrcGenericRGB64 : public QBlendBase
4103{
4104public:
4105 QRgba64 *dest = nullptr;
4106 alignas(16) QRgba64 buffer[BufferSize];
4107 alignas(16) QRgba64 src_buffer[BufferSize];
4108 BlendSrcGenericRGB64(const QSpanData *d, const Operator &o)
4109 : QBlendBase{d, o}
4110 {
4111 }
4112
4113 bool isSupported() const
4114 {
4115 return op.func64 && op.destFetch64;
4116 }
4117
4118 const QRgba64 *fetch(int x, int y, int len, bool fetchDest)
4119 {
4120 if (fetchDest || op.destFetch64 == destFetchRGB64)
4121 dest = op.destFetch64(buffer, data->rasterBuffer, x, y, len);
4122 else
4123 dest = buffer;
4124 return op.srcFetch64(src_buffer, &op, data, y, x, len);
4125 }
4126
4127 void process(int, int, int len, int coverage, const QRgba64 *src, int offset)
4128 {
4129 op.func64(dest + offset, src + offset, len, coverage);
4130 }
4131
4132 void store(int x, int y, int len)
4133 {
4134 if (op.destStore64)
4135 op.destStore64(data->rasterBuffer, x, y, dest, len);
4136 }
4137};
4138#endif
4139
4140#if QT_CONFIG(raster_fp)
4141class BlendSrcGenericRGBFP : public QBlendBase
4142{
4143public:
4144 QRgbaFloat32 *dest = nullptr;
4145 alignas(16) QRgbaFloat32 buffer[BufferSize];
4146 alignas(16) QRgbaFloat32 src_buffer[BufferSize];
4147 BlendSrcGenericRGBFP(const QSpanData *d, const Operator &o)
4148 : QBlendBase{d, o}
4149 {
4150 }
4151
4152 bool isSupported() const
4153 {
4154 return op.funcFP && op.destFetchFP && op.srcFetchFP;
4155 }
4156
4157 const QRgbaFloat32 *fetch(int x, int y, int len, bool fetchDest)
4158 {
4159 if (fetchDest || op.destFetchFP == destFetchRGBFP)
4160 dest = op.destFetchFP(buffer, data->rasterBuffer, x, y, len);
4161 else
4162 dest = buffer;
4163 return op.srcFetchFP(src_buffer, &op, data, y, x, len);
4164 }
4165
4166 void process(int, int, int len, int coverage, const QRgbaFloat32 *src, int offset)
4167 {
4168 op.funcFP(dest + offset, src + offset, len, coverage);
4169 }
4170
4171 void store(int x, int y, int len)
4172 {
4173 if (op.destStoreFP)
4174 op.destStoreFP(data->rasterBuffer, x, y, dest, len);
4175 }
4176};
4177#endif
4178
4179static void blend_src_generic(int count, const QT_FT_Span *spans, void *userData)
4180{
4181 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4182 const Operator op = getOperator(data, nullptr, 0);
4183 handleSpans<BlendSrcGeneric>(count, spans, data, op);
4184}
4185
4186#if QT_CONFIG(raster_64bit)
4187static void blend_src_generic_rgb64(int count, const QT_FT_Span *spans, void *userData)
4188{
4189 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4190 const Operator op = getOperator(data, nullptr, 0);
4191 if (op.func64 && op.destFetch64) {
4192 handleSpans<BlendSrcGenericRGB64>(count, spans, data, op);
4193 } else {
4194 qCDebug(lcQtGuiDrawHelper, "blend_src_generic_rgb64: unsupported 64-bit blend attempted, falling back to 32-bit");
4195 handleSpans<BlendSrcGeneric>(count, spans, data, op);
4196 }
4197}
4198#endif
4199
4200#if QT_CONFIG(raster_fp)
4201static void blend_src_generic_fp(int count, const QT_FT_Span *spans, void *userData)
4202{
4203 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4204 const Operator op = getOperator(data, spans, count);
4205 if (op.funcFP && op.destFetchFP && op.srcFetchFP) {
4206 handleSpans<BlendSrcGenericRGBFP>(count, spans, data, op);
4207 } else {
4208 qCDebug(lcQtGuiDrawHelper, "blend_src_generic_fp: unsupported 4xFP blend attempted, falling back to 32-bit");
4209 handleSpans<BlendSrcGeneric>(count, spans, data, op);
4210 }
4211}
4212#endif
4213
4214static void blend_untransformed_generic(int count, const QT_FT_Span *spans, void *userData)
4215{
4216 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4217
4218 const Operator op = getOperator(data, spans, count);
4219
4220 const int image_width = data->texture.width;
4221 const int image_height = data->texture.height;
4222 const int const_alpha = data->texture.const_alpha;
4223 const int xoff = -qRound(-data->dx);
4224 const int yoff = -qRound(-data->dy);
4225 const bool solidSource = op.mode == QPainter::CompositionMode_Source && const_alpha == 256 && op.destFetch != destFetchARGB32P;
4226
4227 auto function = [=, &op] (int cStart, int cEnd)
4228 {
4229 alignas(16) uint buffer[BufferSize];
4230 alignas(16) uint src_buffer[BufferSize];
4231 for (int c = cStart; c < cEnd; ++c) {
4232 if (!spans[c].len)
4233 continue;
4234 int x = spans[c].x;
4235 int length = spans[c].len;
4236 int sx = xoff + x;
4237 int sy = yoff + spans[c].y;
4238 const bool fetchDest = !solidSource || spans[c].coverage < 255;
4239 if (sy >= 0 && sy < image_height && sx < image_width) {
4240 if (sx < 0) {
4241 x -= sx;
4242 length += sx;
4243 sx = 0;
4244 }
4245 if (sx + length > image_width)
4246 length = image_width - sx;
4247 if (length > 0) {
4248 const int coverage = (spans[c].coverage * const_alpha) >> 8;
4249 while (length) {
4250 int l = qMin(BufferSize, length);
4251 const uint *src = op.srcFetch(src_buffer, &op, data, sy, sx, l);
4252 uint *dest = fetchDest ? op.destFetch(buffer, data->rasterBuffer, x, spans[c].y, l) : buffer;
4253 op.func(dest, src, l, coverage);
4254 if (op.destStore)
4255 op.destStore(data->rasterBuffer, x, spans[c].y, dest, l);
4256 x += l;
4257 sx += l;
4258 length -= l;
4259 }
4260 }
4261 }
4262 }
4263 };
4264 QT_THREAD_PARALLEL_FILLS(function);
4265}
4266
4267#if QT_CONFIG(raster_64bit)
4268static void blend_untransformed_generic_rgb64(int count, const QT_FT_Span *spans, void *userData)
4269{
4270 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4271
4272 const Operator op = getOperator(data, spans, count);
4273 if (!op.func64) {
4274 qCDebug(lcQtGuiDrawHelper, "blend_untransformed_generic_rgb64: unsupported 64-bit blend attempted, falling back to 32-bit");
4275 return blend_untransformed_generic(count, spans, userData);
4276 }
4277
4278 const int image_width = data->texture.width;
4279 const int image_height = data->texture.height;
4280 const int const_alpha = data->texture.const_alpha;
4281 const int xoff = -qRound(-data->dx);
4282 const int yoff = -qRound(-data->dy);
4283 const bool solidSource = op.mode == QPainter::CompositionMode_Source && const_alpha == 256 && op.destFetch64 != destFetchRGB64;
4284
4285 auto function = [=, &op] (int cStart, int cEnd)
4286 {
4287 alignas(16) QRgba64 buffer[BufferSize];
4288 alignas(16) QRgba64 src_buffer[BufferSize];
4289 for (int c = cStart; c < cEnd; ++c) {
4290 if (!spans[c].len)
4291 continue;
4292 int x = spans[c].x;
4293 int length = spans[c].len;
4294 int sx = xoff + x;
4295 int sy = yoff + spans[c].y;
4296 const bool fetchDest = !solidSource || spans[c].coverage < 255;
4297 if (sy >= 0 && sy < image_height && sx < image_width) {
4298 if (sx < 0) {
4299 x -= sx;
4300 length += sx;
4301 sx = 0;
4302 }
4303 if (sx + length > image_width)
4304 length = image_width - sx;
4305 if (length > 0) {
4306 const int coverage = (spans[c].coverage * const_alpha) >> 8;
4307 while (length) {
4308 int l = qMin(BufferSize, length);
4309 const QRgba64 *src = op.srcFetch64(src_buffer, &op, data, sy, sx, l);
4310 QRgba64 *dest = fetchDest ? op.destFetch64(buffer, data->rasterBuffer, x, spans[c].y, l) : buffer;
4311 op.func64(dest, src, l, coverage);
4312 if (op.destStore64)
4313 op.destStore64(data->rasterBuffer, x, spans[c].y, dest, l);
4314 x += l;
4315 sx += l;
4316 length -= l;
4317 }
4318 }
4319 }
4320 }
4321 };
4322 QT_THREAD_PARALLEL_FILLS(function);
4323}
4324#endif
4325
4326#if QT_CONFIG(raster_fp)
4327static void blend_untransformed_generic_fp(int count, const QT_FT_Span *spans, void *userData)
4328{
4329 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4330
4331 const Operator op = getOperator(data, spans, count);
4332 if (!op.funcFP) {
4333 qCDebug(lcQtGuiDrawHelper, "blend_untransformed_generic_rgbaf16: unsupported 4xFP16 blend attempted, falling back to 32-bit");
4334 return blend_untransformed_generic(count, spans, userData);
4335 }
4336
4337 const int image_width = data->texture.width;
4338 const int image_height = data->texture.height;
4339 const int xoff = -qRound(-data->dx);
4340 const int yoff = -qRound(-data->dy);
4341 const bool solidSource = op.mode == QPainter::CompositionMode_Source && data->texture.const_alpha == 256 && op.destFetchFP != destFetchRGBFP;
4342
4343 auto function = [=, &op] (int cStart, int cEnd)
4344 {
4345 alignas(16) QRgbaFloat32 buffer[BufferSize];
4346 alignas(16) QRgbaFloat32 src_buffer[BufferSize];
4347 for (int c = cStart; c < cEnd; ++c) {
4348 if (!spans[c].len)
4349 continue;
4350 int x = spans[c].x;
4351 int length = spans[c].len;
4352 int sx = xoff + x;
4353 int sy = yoff + spans[c].y;
4354 const bool fetchDest = !solidSource || spans[c].coverage < 255;
4355 if (sy >= 0 && sy < image_height && sx < image_width) {
4356 if (sx < 0) {
4357 x -= sx;
4358 length += sx;
4359 sx = 0;
4360 }
4361 if (sx + length > image_width)
4362 length = image_width - sx;
4363 if (length > 0) {
4364 const int coverage = (spans[c].coverage * data->texture.const_alpha) >> 8;
4365 while (length) {
4366 int l = qMin(BufferSize, length);
4367 const QRgbaFloat32 *src = op.srcFetchFP(src_buffer, &op, data, sy, sx, l);
4368 QRgbaFloat32 *dest = fetchDest ? op.destFetchFP(buffer, data->rasterBuffer, x, spans[c].y, l) : buffer;
4369 op.funcFP(dest, src, l, coverage);
4370 if (op.destStoreFP)
4371 op.destStoreFP(data->rasterBuffer, x, spans[c].y, dest, l);
4372 x += l;
4373 sx += l;
4374 length -= l;
4375 }
4376 }
4377 }
4378 }
4379 };
4380 QT_THREAD_PARALLEL_FILLS(function);
4381}
4382#endif
4383
4384static void blend_untransformed_argb(int count, const QT_FT_Span *spans, void *userData)
4385{
4386 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4387 if (data->texture.format != QImage::Format_ARGB32_Premultiplied
4388 && data->texture.format != QImage::Format_RGB32) {
4389 blend_untransformed_generic(count, spans, userData);
4390 return;
4391 }
4392
4393 const Operator op = getOperator(data, spans, count);
4394
4395 const int image_width = data->texture.width;
4396 const int image_height = data->texture.height;
4397 const int const_alpha = data->texture.const_alpha;
4398 const int xoff = -qRound(-data->dx);
4399 const int yoff = -qRound(-data->dy);
4400
4401 auto function = [=, &op] (int cStart, int cEnd)
4402 {
4403 for (int c = cStart; c < cEnd; ++c) {
4404 if (!spans[c].len)
4405 continue;
4406 int x = spans[c].x;
4407 int length = spans[c].len;
4408 int sx = xoff + x;
4409 int sy = yoff + spans[c].y;
4410 if (sy >= 0 && sy < image_height && sx < image_width) {
4411 if (sx < 0) {
4412 x -= sx;
4413 length += sx;
4414 sx = 0;
4415 }
4416 if (sx + length > image_width)
4417 length = image_width - sx;
4418 if (length > 0) {
4419 const int coverage = (spans[c].coverage * const_alpha) >> 8;
4420 const uint *src = (const uint *)data->texture.scanLine(sy) + sx;
4421 uint *dest = ((uint *)data->rasterBuffer->scanLine(spans[c].y)) + x;
4422 op.func(dest, src, length, coverage);
4423 }
4424 }
4425 }
4426 };
4427 QT_THREAD_PARALLEL_FILLS(function);
4428}
4429
4431 quint16 y, quint8 b)
4432{
4433 quint16 t = ((((x & 0x07e0) * a) + ((y & 0x07e0) * b)) >> 5) & 0x07e0;
4434 t |= ((((x & 0xf81f) * a) + ((y & 0xf81f) * b)) >> 5) & 0xf81f;
4435
4436 return t;
4437}
4438
4440 quint32 y, quint8 b)
4441{
4442 uint t;
4443 t = ((((x & 0xf81f07e0) >> 5) * a) + (((y & 0xf81f07e0) >> 5) * b)) & 0xf81f07e0;
4444 t |= ((((x & 0x07e0f81f) * a) + ((y & 0x07e0f81f) * b)) >> 5) & 0x07e0f81f;
4445 return t;
4446}
4447
4450 int length,
4451 const quint8 alpha,
4452 const quint8 ialpha)
4453{
4454 const int dstAlign = ((quintptr)dest) & 0x3;
4455 if (dstAlign) {
4456 *dest = interpolate_pixel_rgb16_255(*src, alpha, *dest, ialpha);
4457 ++dest;
4458 ++src;
4459 --length;
4460 }
4461 const int srcAlign = ((quintptr)src) & 0x3;
4462 int length32 = length >> 1;
4463 if (length32 && srcAlign == 0) {
4464 while (length32--) {
4465 const quint32 *src32 = reinterpret_cast<const quint32*>(src);
4466 quint32 *dest32 = reinterpret_cast<quint32*>(dest);
4467 *dest32 = interpolate_pixel_rgb16x2_255(*src32, alpha,
4468 *dest32, ialpha);
4469 dest += 2;
4470 src += 2;
4471 }
4472 length &= 0x1;
4473 }
4474 while (length--) {
4475 *dest = interpolate_pixel_rgb16_255(*src, alpha, *dest, ialpha);
4476 ++dest;
4477 ++src;
4478 }
4479}
4480
4481static void blend_untransformed_rgb565(int count, const QT_FT_Span *spans, void *userData)
4482{
4483 QSpanData *data = reinterpret_cast<QSpanData*>(userData);
4485
4486 if (data->texture.format != QImage::Format_RGB16
4489 {
4490 blend_untransformed_generic(count, spans, userData);
4491 return;
4492 }
4493
4494 const int image_width = data->texture.width;
4495 const int image_height = data->texture.height;
4496 int xoff = -qRound(-data->dx);
4497 int yoff = -qRound(-data->dy);
4498
4499 auto function = [=](int cStart, int cEnd)
4500 {
4501 for (int c = cStart; c < cEnd; ++c) {
4502 if (!spans[c].len)
4503 continue;
4504 const quint8 coverage = (data->texture.const_alpha * spans[c].coverage) >> 8;
4505 if (coverage == 0)
4506 continue;
4507
4508 int x = spans[c].x;
4509 int length = spans[c].len;
4510 int sx = xoff + x;
4511 int sy = yoff + spans[c].y;
4512 if (sy >= 0 && sy < image_height && sx < image_width) {
4513 if (sx < 0) {
4514 x -= sx;
4515 length += sx;
4516 sx = 0;
4517 }
4518 if (sx + length > image_width)
4519 length = image_width - sx;
4520 if (length > 0) {
4521 quint16 *dest = (quint16 *)data->rasterBuffer->scanLine(spans[c].y) + x;
4522 const quint16 *src = (const quint16 *)data->texture.scanLine(sy) + sx;
4523 if (coverage == 255) {
4524 memcpy(dest, src, length * sizeof(quint16));
4525 } else {
4526 const quint8 alpha = (coverage + 1) >> 3;
4527 const quint8 ialpha = 0x20 - alpha;
4528 if (alpha > 0)
4530 }
4531 }
4532 }
4533 }
4534 };
4535 QT_THREAD_PARALLEL_FILLS(function);
4536}
4537
4538static void blend_tiled_generic(int count, const QT_FT_Span *spans, void *userData)
4539{
4540 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4541
4542 const Operator op = getOperator(data, spans, count);
4543
4544 const int image_width = data->texture.width;
4545 const int image_height = data->texture.height;
4546 const int const_alpha = data->texture.const_alpha;
4547 int xoff = -qRound(-data->dx) % image_width;
4548 int yoff = -qRound(-data->dy) % image_height;
4549
4550 if (xoff < 0)
4551 xoff += image_width;
4552 if (yoff < 0)
4553 yoff += image_height;
4554
4555 auto function = [=, &op](int cStart, int cEnd)
4556 {
4557 alignas(16) uint buffer[BufferSize];
4558 alignas(16) uint src_buffer[BufferSize];
4559 for (int c = cStart; c < cEnd; ++c) {
4560 int x = spans[c].x;
4561 int length = spans[c].len;
4562 int sx = (xoff + spans[c].x) % image_width;
4563 int sy = (spans[c].y + yoff) % image_height;
4564 if (sx < 0)
4565 sx += image_width;
4566 if (sy < 0)
4567 sy += image_height;
4568
4569 const int coverage = (spans[c].coverage * const_alpha) >> 8;
4570 while (length) {
4571 int l = qMin(image_width - sx, length);
4572 if (BufferSize < l)
4573 l = BufferSize;
4574 const uint *src = op.srcFetch(src_buffer, &op, data, sy, sx, l);
4575 uint *dest = op.destFetch(buffer, data->rasterBuffer, x, spans[c].y, l);
4576 op.func(dest, src, l, coverage);
4577 if (op.destStore)
4578 op.destStore(data->rasterBuffer, x, spans[c].y, dest, l);
4579 x += l;
4580 sx += l;
4581 length -= l;
4582 if (sx >= image_width)
4583 sx = 0;
4584 }
4585 }
4586 };
4587 QT_THREAD_PARALLEL_FILLS(function);
4588}
4589
4590#if QT_CONFIG(raster_64bit)
4591static void blend_tiled_generic_rgb64(int count, const QT_FT_Span *spans, void *userData)
4592{
4593 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4594
4595 const Operator op = getOperator(data, spans, count);
4596 if (!op.func64) {
4597 qCDebug(lcQtGuiDrawHelper, "blend_tiled_generic_rgb64: unsupported 64-bit blend attempted, falling back to 32-bit");
4598 return blend_tiled_generic(count, spans, userData);
4599 }
4600
4601 const int image_width = data->texture.width;
4602 const int image_height = data->texture.height;
4603 int xoff = -qRound(-data->dx) % image_width;
4604 int yoff = -qRound(-data->dy) % image_height;
4605
4606 if (xoff < 0)
4607 xoff += image_width;
4608 if (yoff < 0)
4609 yoff += image_height;
4610
4611 bool isBpp32 = qPixelLayouts[data->rasterBuffer->format].bpp == QPixelLayout::BPP32;
4612 bool isBpp64 = qPixelLayouts[data->rasterBuffer->format].bpp == QPixelLayout::BPP64;
4613 if (op.destFetch64 == destFetch64Undefined && image_width <= BufferSize && (isBpp32 || isBpp64)) {
4614 alignas(16) QRgba64 src_buffer[BufferSize];
4615 // If destination isn't blended into the result, we can do the tiling directly on destination pixels.
4616 while (count--) {
4617 int x = spans->x;
4618 int y = spans->y;
4619 int length = spans->len;
4620 int sx = (xoff + spans->x) % image_width;
4621 int sy = (spans->y + yoff) % image_height;
4622 if (sx < 0)
4623 sx += image_width;
4624 if (sy < 0)
4625 sy += image_height;
4626
4627 int sl = qMin(image_width, length);
4628 if (sx > 0 && sl > 0) {
4629 int l = qMin(image_width - sx, sl);
4630 const QRgba64 *src = op.srcFetch64(src_buffer, &op, data, sy, sx, l);
4631 op.destStore64(data->rasterBuffer, x, y, src, l);
4632 x += l;
4633 sx += l;
4634 sl -= l;
4635 if (sx >= image_width)
4636 sx = 0;
4637 }
4638 if (sl > 0) {
4639 Q_ASSERT(sx == 0);
4640 const QRgba64 *src = op.srcFetch64(src_buffer, &op, data, sy, sx, sl);
4641 op.destStore64(data->rasterBuffer, x, y, src, sl);
4642 x += sl;
4643 sx += sl;
4644 sl -= sl;
4645 if (sx >= image_width)
4646 sx = 0;
4647 }
4648 if (isBpp32) {
4649 uint *dest = reinterpret_cast<uint *>(data->rasterBuffer->scanLine(y)) + x - image_width;
4650 for (int i = image_width; i < length; ++i)
4651 dest[i] = dest[i - image_width];
4652 } else {
4653 quint64 *dest = reinterpret_cast<quint64 *>(data->rasterBuffer->scanLine(y)) + x - image_width;
4654 for (int i = image_width; i < length; ++i)
4655 dest[i] = dest[i - image_width];
4656 }
4657 ++spans;
4658 }
4659 return;
4660 }
4661
4662 auto function = [=, &op](int cStart, int cEnd)
4663 {
4664 alignas(16) QRgba64 buffer[BufferSize];
4665 alignas(16) QRgba64 src_buffer[BufferSize];
4666 for (int c = cStart; c < cEnd; ++c) {
4667 int x = spans[c].x;
4668 int length = spans[c].len;
4669 int sx = (xoff + spans[c].x) % image_width;
4670 int sy = (spans[c].y + yoff) % image_height;
4671 if (sx < 0)
4672 sx += image_width;
4673 if (sy < 0)
4674 sy += image_height;
4675
4676 const int coverage = (spans[c].coverage * data->texture.const_alpha) >> 8;
4677 while (length) {
4678 int l = qMin(image_width - sx, length);
4679 if (BufferSize < l)
4680 l = BufferSize;
4681 const QRgba64 *src = op.srcFetch64(src_buffer, &op, data, sy, sx, l);
4682 QRgba64 *dest = op.destFetch64(buffer, data->rasterBuffer, x, spans[c].y, l);
4683 op.func64(dest, src, l, coverage);
4684 if (op.destStore64)
4685 op.destStore64(data->rasterBuffer, x, spans[c].y, dest, l);
4686 x += l;
4687 sx += l;
4688 length -= l;
4689 if (sx >= image_width)
4690 sx = 0;
4691 }
4692 }
4693 };
4694 QT_THREAD_PARALLEL_FILLS(function);
4695}
4696#endif
4697
4698#if QT_CONFIG(raster_fp)
4699static void blend_tiled_generic_fp(int count, const QT_FT_Span *spans, void *userData)
4700{
4701 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4702
4703 const Operator op = getOperator(data, spans, count);
4704 if (!op.funcFP) {
4705 qCDebug(lcQtGuiDrawHelper, "blend_tiled_generic_fp: unsupported 4xFP blend attempted, falling back to 32-bit");
4706 return blend_tiled_generic(count, spans, userData);
4707 }
4708
4709 const int image_width = data->texture.width;
4710 const int image_height = data->texture.height;
4711 int xoff = -qRound(-data->dx) % image_width;
4712 int yoff = -qRound(-data->dy) % image_height;
4713
4714 if (xoff < 0)
4715 xoff += image_width;
4716 if (yoff < 0)
4717 yoff += image_height;
4718
4719 // Consider tiling optimizing like the other versions.
4720
4721 auto function = [=, &op](int cStart, int cEnd)
4722 {
4723 alignas(16) QRgbaFloat32 buffer[BufferSize];
4724 alignas(16) QRgbaFloat32 src_buffer[BufferSize];
4725 for (int c = cStart; c < cEnd; ++c) {
4726 int x = spans[c].x;
4727 int length = spans[c].len;
4728 int sx = (xoff + spans[c].x) % image_width;
4729 int sy = (spans[c].y + yoff) % image_height;
4730 if (sx < 0)
4731 sx += image_width;
4732 if (sy < 0)
4733 sy += image_height;
4734
4735 const int coverage = (spans[c].coverage * data->texture.const_alpha) >> 8;
4736 while (length) {
4737 int l = qMin(image_width - sx, length);
4738 if (BufferSize < l)
4739 l = BufferSize;
4740 const QRgbaFloat32 *src = op.srcFetchFP(src_buffer, &op, data, sy, sx, l);
4741 QRgbaFloat32 *dest = op.destFetchFP(buffer, data->rasterBuffer, x, spans[c].y, l);
4742 op.funcFP(dest, src, l, coverage);
4743 if (op.destStoreFP)
4744 op.destStoreFP(data->rasterBuffer, x, spans[c].y, dest, l);
4745 x += l;
4746 sx += l;
4747 length -= l;
4748 if (sx >= image_width)
4749 sx = 0;
4750 }
4751 }
4752 };
4753 QT_THREAD_PARALLEL_FILLS(function);
4754}
4755#endif
4756
4757static void blend_tiled_argb(int count, const QT_FT_Span *spans, void *userData)
4758{
4759 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4760 if (data->texture.format != QImage::Format_ARGB32_Premultiplied
4761 && data->texture.format != QImage::Format_RGB32) {
4762 blend_tiled_generic(count, spans, userData);
4763 return;
4764 }
4765
4766 const Operator op = getOperator(data, spans, count);
4767
4768 const int image_width = data->texture.width;
4769 const int image_height = data->texture.height;
4770 int xoff = -qRound(-data->dx) % image_width;
4771 int yoff = -qRound(-data->dy) % image_height;
4772
4773 if (xoff < 0)
4774 xoff += image_width;
4775 if (yoff < 0)
4776 yoff += image_height;
4777 const auto func = op.func;
4778 const int const_alpha = data->texture.const_alpha;
4779
4780 auto function = [=] (int cStart, int cEnd) {
4781 for (int c = cStart; c < cEnd; ++c) {
4782 int x = spans[c].x;
4783 int length = spans[c].len;
4784 int sx = (xoff + spans[c].x) % image_width;
4785 int sy = (spans[c].y + yoff) % image_height;
4786 if (sx < 0)
4787 sx += image_width;
4788 if (sy < 0)
4789 sy += image_height;
4790
4791 const int coverage = (spans[c].coverage * const_alpha) >> 8;
4792 while (length) {
4793 int l = qMin(image_width - sx, length);
4794 if (BufferSize < l)
4795 l = BufferSize;
4796 const uint *src = (const uint *)data->texture.scanLine(sy) + sx;
4797 uint *dest = ((uint *)data->rasterBuffer->scanLine(spans[c].y)) + x;
4798 func(dest, src, l, coverage);
4799 x += l;
4800 sx += l;
4801 length -= l;
4802 if (sx >= image_width)
4803 sx = 0;
4804 }
4805 }
4806 };
4807 QT_THREAD_PARALLEL_FILLS(function);
4808}
4809
4810static void blend_tiled_rgb565(int count, const QT_FT_Span *spans, void *userData)
4811{
4812 QSpanData *data = reinterpret_cast<QSpanData*>(userData);
4814
4815 if (data->texture.format != QImage::Format_RGB16
4818 {
4819 blend_tiled_generic(count, spans, userData);
4820 return;
4821 }
4822
4823 const int image_width = data->texture.width;
4824 const int image_height = data->texture.height;
4825 int xoff = -qRound(-data->dx) % image_width;
4826 int yoff = -qRound(-data->dy) % image_height;
4827
4828 if (xoff < 0)
4829 xoff += image_width;
4830 if (yoff < 0)
4831 yoff += image_height;
4832
4833 const int const_alpha = data->texture.const_alpha;
4834 auto function = [=] (int cStart, int cEnd) {
4835 for (int c = cStart; c < cEnd; ++c) {
4836 const quint8 coverage = (const_alpha * spans[c].coverage) >> 8;
4837 if (coverage == 0)
4838 continue;
4839
4840 int x = spans[c].x;
4841 int length = spans[c].len;
4842 int sx = (xoff + spans[c].x) % image_width;
4843 int sy = (spans[c].y + yoff) % image_height;
4844 if (sx < 0)
4845 sx += image_width;
4846 if (sy < 0)
4847 sy += image_height;
4848
4849 if (coverage == 255) {
4850 // Copy the first texture block
4851 length = qMin(image_width,length);
4852 int tx = x;
4853 while (length) {
4854 int l = qMin(image_width - sx, length);
4855 if (BufferSize < l)
4856 l = BufferSize;
4857 quint16 *dest = ((quint16 *)data->rasterBuffer->scanLine(spans[c].y)) + tx;
4858 const quint16 *src = (const quint16 *)data->texture.scanLine(sy) + sx;
4859 memcpy(dest, src, l * sizeof(quint16));
4860 length -= l;
4861 tx += l;
4862 sx += l;
4863 if (sx >= image_width)
4864 sx = 0;
4865 }
4866
4867 // Now use the rasterBuffer as the source of the texture,
4868 // We can now progressively copy larger blocks
4869 // - Less cpu time in code figuring out what to copy
4870 // We are dealing with one block of data
4871 // - More likely to fit in the cache
4872 // - can use memcpy
4873 int copy_image_width = qMin(image_width, int(spans[c].len));
4874 length = spans[c].len - copy_image_width;
4875 quint16 *src = ((quint16 *)data->rasterBuffer->scanLine(spans[c].y)) + x;
4876 quint16 *dest = src + copy_image_width;
4877 while (copy_image_width < length) {
4878 memcpy(dest, src, copy_image_width * sizeof(quint16));
4879 dest += copy_image_width;
4880 length -= copy_image_width;
4881 copy_image_width *= 2;
4882 }
4883 if (length > 0)
4884 memcpy(dest, src, length * sizeof(quint16));
4885 } else {
4886 const quint8 alpha = (coverage + 1) >> 3;
4887 const quint8 ialpha = 0x20 - alpha;
4888 if (alpha > 0) {
4889 while (length) {
4890 int l = qMin(image_width - sx, length);
4891 if (BufferSize < l)
4892 l = BufferSize;
4893 quint16 *dest = ((quint16 *)data->rasterBuffer->scanLine(spans[c].y)) + x;
4894 const quint16 *src = (const quint16 *)data->texture.scanLine(sy) + sx;
4895 blend_sourceOver_rgb16_rgb16(dest, src, l, alpha, ialpha);
4896 x += l;
4897 sx += l;
4898 length -= l;
4899 if (sx >= image_width)
4900 sx = 0;
4901 }
4902 }
4903 }
4904 }
4905 };
4906 QT_THREAD_PARALLEL_FILLS(function);
4907}
4908
4909/* Image formats here are target formats */
4911 blend_untransformed_argb, // Untransformed
4912 blend_tiled_argb, // Tiled
4913 blend_src_generic, // Transformed
4914 blend_src_generic, // TransformedTiled
4915 blend_src_generic, // TransformedBilinear
4916 blend_src_generic // TransformedBilinearTiled
4917};
4918
4920 blend_untransformed_rgb565, // Untransformed
4921 blend_tiled_rgb565, // Tiled
4922 blend_src_generic, // Transformed
4923 blend_src_generic, // TransformedTiled
4924 blend_src_generic, // TransformedBilinear
4925 blend_src_generic // TransformedBilinearTiled
4926};
4927
4929 blend_untransformed_generic, // Untransformed
4930 blend_tiled_generic, // Tiled
4931 blend_src_generic, // Transformed
4932 blend_src_generic, // TransformedTiled
4933 blend_src_generic, // TransformedBilinear
4934 blend_src_generic // TransformedBilinearTiled
4935};
4936
4937#if QT_CONFIG(raster_64bit)
4938static const ProcessSpans processTextureSpansGeneric64[NBlendTypes] = {
4939 blend_untransformed_generic_rgb64, // Untransformed
4940 blend_tiled_generic_rgb64, // Tiled
4941 blend_src_generic_rgb64, // Transformed
4942 blend_src_generic_rgb64, // TransformedTiled
4943 blend_src_generic_rgb64, // TransformedBilinear
4944 blend_src_generic_rgb64 // TransformedBilinearTiled
4945};
4946#endif
4947
4948#if QT_CONFIG(raster_fp)
4949static const ProcessSpans processTextureSpansGenericFP[NBlendTypes] = {
4950 blend_untransformed_generic_fp, // Untransformed
4951 blend_tiled_generic_fp, // Tiled
4952 blend_src_generic_fp, // Transformed
4953 blend_src_generic_fp, // TransformedTiled
4954 blend_src_generic_fp, // TransformedBilinear
4955 blend_src_generic_fp // TransformedBilinearTiled
4956};
4957#endif
4958void qBlendTexture(int count, const QT_FT_Span *spans, void *userData)
4959{
4960 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
4961 TextureBlendType blendType = getBlendType(data);
4962 ProcessSpans proc;
4963 switch (data->rasterBuffer->format) {
4965 Q_UNREACHABLE_RETURN();
4967 proc = processTextureSpansARGB32PM[blendType];
4968 break;
4970 proc = processTextureSpansRGB16[blendType];
4971 break;
4972#if defined(__SSE2__) || defined(__ARM_NEON__) || (Q_PROCESSOR_WORDSIZE == 8)
4975#endif
4984#if !QT_CONFIG(raster_fp)
4991#endif
4992#if QT_CONFIG(raster_64bit)
4993 proc = processTextureSpansGeneric64[blendType];
4994 break;
4995#endif // QT_CONFIG(raster_64bit)
4996#if QT_CONFIG(raster_fp)
5003 proc = processTextureSpansGenericFP[blendType];
5004 break;
5005#endif
5006 default:
5007 proc = processTextureSpansGeneric[blendType];
5008 break;
5009 }
5010 proc(count, spans, userData);
5011}
5012
5013static inline bool calculate_fixed_gradient_factors(int count, const QT_FT_Span *spans,
5014 const QSpanData *data,
5015 const LinearGradientValues &linear,
5016 int *pyinc, int *poff)
5017{
5018 /*
5019 The logic for vertical gradient calculations is a mathematically
5020 reduced copy of that in fetchLinearGradient() - which is basically:
5021
5022 qreal ry = data->m22 * (y + 0.5) + data->dy;
5023 qreal t = linear.dy*ry + linear.off;
5024 t *= (GRADIENT_STOPTABLE_SIZE - 1);
5025 quint32 color =
5026 qt_gradient_pixel_fixed(&data->gradient,
5027 int(t * FIXPT_SIZE));
5028
5029 This has then been converted to fixed point to improve performance.
5030 */
5031 const int gss = GRADIENT_STOPTABLE_SIZE - 1;
5032 qreal ryinc = linear.dy * data->m22 * gss * FIXPT_SIZE;
5033 qreal roff = (linear.dy * (data->m22 * qreal(0.5) + data->dy) + linear.off) * gss * FIXPT_SIZE;
5034 const int limit = std::numeric_limits<int>::max() - FIXPT_SIZE;
5035 if (count && (std::fabs(ryinc) < limit) && (std::fabs(roff) < limit)
5036 && (std::fabs(ryinc * spans->y + roff) < limit)
5037 && (std::fabs(ryinc * (spans + count - 1)->y + roff) < limit)) {
5038 *pyinc = int(ryinc);
5039 *poff = int(roff);
5040 return true;
5041 }
5042 return false;
5043}
5044
5045static bool blend_vertical_gradient_argb(int count, const QT_FT_Span *spans, void *userData)
5046{
5047 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
5048
5049 LinearGradientValues linear;
5050 getLinearGradientValues(&linear, data);
5051
5052 CompositionFunctionSolid funcSolid =
5053 functionForModeSolid[data->rasterBuffer->compositionMode];
5054
5055 int yinc(0), off(0);
5056 if (!calculate_fixed_gradient_factors(count, spans, data, linear, &yinc, &off))
5057 return false;
5058
5059 while (count--) {
5060 int y = spans->y;
5061 int x = spans->x;
5062
5063 quint32 *dst = (quint32 *)(data->rasterBuffer->scanLine(y)) + x;
5064 quint32 color =
5065 qt_gradient_pixel_fixed(&data->gradient, yinc * y + off);
5066
5067 funcSolid(dst, spans->len, color, spans->coverage);
5068 ++spans;
5069 }
5070 return true;
5071}
5072
5073template<ProcessSpans blend_color>
5074static bool blend_vertical_gradient(int count, const QT_FT_Span *spans, void *userData)
5075{
5076 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
5077
5078 LinearGradientValues linear;
5079 getLinearGradientValues(&linear, data);
5080
5081 int yinc(0), off(0);
5082 if (!calculate_fixed_gradient_factors(count, spans, data, linear, &yinc, &off))
5083 return false;
5084
5085 while (count--) {
5086 int y = spans->y;
5087
5088#if QT_CONFIG(raster_64bit)
5089 data->solidColor = qt_gradient_pixel64_fixed(&data->gradient, yinc * y + off);
5090#else
5091 data->solidColor = qt_gradient_pixel_fixed(&data->gradient, yinc * y + off);
5092#endif
5093 blend_color(1, spans, userData);
5094 ++spans;
5095 }
5096 return true;
5097}
5098
5099void qBlendGradient(int count, const QT_FT_Span *spans, void *userData)
5100{
5101 QSpanData *data = reinterpret_cast<QSpanData *>(userData);
5102 bool isVerticalGradient =
5105 data->gradient.linear.end.x == data->gradient.linear.origin.x;
5106 switch (data->rasterBuffer->format) {
5108 break;
5111 if (isVerticalGradient && blend_vertical_gradient_argb(count, spans, userData))
5112 return;
5113 return blend_src_generic(count, spans, userData);
5114#if defined(__SSE2__) || defined(__ARM_NEON__) || (Q_PROCESSOR_WORDSIZE == 8)
5117#endif
5125#if !QT_CONFIG(raster_fp)
5132#endif
5133#if QT_CONFIG(raster_64bit)
5134 if (isVerticalGradient && blend_vertical_gradient<blend_color_generic_rgb64>(count, spans, userData))
5135 return;
5136 return blend_src_generic_rgb64(count, spans, userData);
5137#endif // QT_CONFIG(raster_64bit)
5138#if QT_CONFIG(raster_fp)
5145 if (isVerticalGradient && blend_vertical_gradient<blend_color_generic_fp>(count, spans, userData))
5146 return;
5147 return blend_src_generic_fp(count, spans, userData);
5148#endif
5149 default:
5150 if (isVerticalGradient && blend_vertical_gradient<blend_color_generic>(count, spans, userData))
5151 return;
5152 return blend_src_generic(count, spans, userData);
5153 }
5154 Q_UNREACHABLE();
5155}
5156
5157template <class DST> static
5158inline void qt_bitmapblit_template(QRasterBuffer *rasterBuffer,
5159 int x, int y, DST color,
5160 const uchar *map,
5161 int mapWidth, int mapHeight, int mapStride)
5162{
5163 DST *dest = reinterpret_cast<DST *>(rasterBuffer->scanLine(y)) + x;
5164 const int destStride = rasterBuffer->stride<DST>();
5165
5166 if (mapWidth > 8) {
5167 while (--mapHeight >= 0) {
5168 int x0 = 0;
5169 int n = 0;
5170 for (int x = 0; x < mapWidth; x += 8) {
5171 uchar s = map[x >> 3];
5172 for (int i = 0; i < 8; ++i) {
5173 if (s & 0x80) {
5174 ++n;
5175 } else {
5176 if (n) {
5177 qt_memfill(dest + x0, color, n);
5178 x0 += n + 1;
5179 n = 0;
5180 } else {
5181 ++x0;
5182 }
5183 if (!s) {
5184 x0 += 8 - 1 - i;
5185 break;
5186 }
5187 }
5188 s <<= 1;
5189 }
5190 }
5191 if (n)
5192 qt_memfill(dest + x0, color, n);
5193 dest += destStride;
5194 map += mapStride;
5195 }
5196 } else {
5197 while (--mapHeight >= 0) {
5198 int x0 = 0;
5199 int n = 0;
5200 for (uchar s = *map; s; s <<= 1) {
5201 if (s & 0x80) {
5202 ++n;
5203 } else if (n) {
5204 qt_memfill(dest + x0, color, n);
5205 x0 += n + 1;
5206 n = 0;
5207 } else {
5208 ++x0;
5209 }
5210 }
5211 if (n)
5212 qt_memfill(dest + x0, color, n);
5213 dest += destStride;
5214 map += mapStride;
5215 }
5216 }
5217}
5218
5219inline static void qt_bitmapblit_argb32(QRasterBuffer *rasterBuffer,
5220 int x, int y, const QRgba64 &color,
5221 const uchar *map,
5222 int mapWidth, int mapHeight, int mapStride)
5223{
5224 qt_bitmapblit_template<quint32>(rasterBuffer, x, y, color.toArgb32(),
5225 map, mapWidth, mapHeight, mapStride);
5226}
5227
5228inline static void qt_bitmapblit_rgba8888(QRasterBuffer *rasterBuffer,
5229 int x, int y, const QRgba64 &color,
5230 const uchar *map,
5231 int mapWidth, int mapHeight, int mapStride)
5232{
5233 qt_bitmapblit_template<quint32>(rasterBuffer, x, y, ARGB2RGBA(color.toArgb32()),
5234 map, mapWidth, mapHeight, mapStride);
5235}
5236
5237template<QtPixelOrder PixelOrder>
5238inline static void qt_bitmapblit_rgb30(QRasterBuffer *rasterBuffer,
5239 int x, int y, const QRgba64 &color,
5240 const uchar *map,
5241 int mapWidth, int mapHeight, int mapStride)
5242{
5243 qt_bitmapblit_template<quint32>(rasterBuffer, x, y, qConvertRgb64ToRgb30<PixelOrder>(color),
5244 map, mapWidth, mapHeight, mapStride);
5245}
5246
5247inline static void qt_bitmapblit_quint16(QRasterBuffer *rasterBuffer,
5248 int x, int y, const QRgba64 &color,
5249 const uchar *map,
5250 int mapWidth, int mapHeight, int mapStride)
5251{
5252 qt_bitmapblit_template<quint16>(rasterBuffer, x, y, color.toRgb16(),
5253 map, mapWidth, mapHeight, mapStride);
5254}
5255
5256static inline void grayBlendPixel(quint32 *dst, int coverage, QRgba64 srcLinear, const QColorTrcLut *colorProfile)
5257{
5258 // Do a gammacorrected gray alphablend...
5259 const QRgba64 dstLinear = colorProfile ? colorProfile->toLinear64(*dst) : QRgba64::fromArgb32(*dst);
5260
5261 QRgba64 blend = interpolate255(srcLinear, coverage, dstLinear, 255 - coverage);
5262
5263 *dst = colorProfile ? colorProfile->fromLinear64(blend) : toArgb32(blend);
5264}
5265
5266static inline void alphamapblend_argb32(quint32 *dst, int coverage, QRgba64 srcLinear, quint32 src, const QColorTrcLut *colorProfile)
5267{
5268 if (coverage == 0) {
5269 // nothing
5270 } else if (coverage == 255 || !colorProfile) {
5271 blend_pixel(*dst, src, coverage);
5272 } else if (*dst < 0xff000000) {
5273 // Give up and do a naive gray alphablend. Needed to deal with ARGB32 and invalid ARGB32_premultiplied, see QTBUG-60571
5274 blend_pixel(*dst, src, coverage);
5275 } else if (src >= 0xff000000) {
5276 grayBlendPixel(dst, coverage, srcLinear, colorProfile);
5277 } else {
5278 // First do naive blend with text-color
5279 QRgb s = *dst;
5280 blend_pixel(s, src);
5281 // Then gamma-corrected blend with glyph shape
5282 QRgba64 s64 = colorProfile ? colorProfile->toLinear64(s) : QRgba64::fromArgb32(s);
5283 grayBlendPixel(dst, coverage, s64, colorProfile);
5284 }
5285}
5286
5287#if QT_CONFIG(raster_64bit)
5288
5289static inline void grayBlendPixel(QRgba64 &dst, int coverage, QRgba64 srcLinear, const QColorTrcLut *colorProfile)
5290{
5291 // Do a gammacorrected gray alphablend...
5292 QRgba64 dstColor = dst;
5293 if (colorProfile) {
5294 if (dstColor.isOpaque())
5295 dstColor = colorProfile->toLinear(dstColor);
5296 else if (!dstColor.isTransparent())
5297 dstColor = colorProfile->toLinear(dstColor.unpremultiplied()).premultiplied();
5298 }
5299
5300 blend_pixel(dstColor, srcLinear, coverage);
5301
5302 if (colorProfile) {
5303 if (dstColor.isOpaque())
5304 dstColor = colorProfile->fromLinear(dstColor);
5305 else if (!dstColor.isTransparent())
5306 dstColor = colorProfile->fromLinear(dstColor.unpremultiplied()).premultiplied();
5307 }
5308 dst = dstColor;
5309}
5310
5311static inline void alphamapblend_generic(int coverage, QRgba64 *dest, int x, const QRgba64 &srcLinear, const QRgba64 &src, const QColorTrcLut *colorProfile)
5312{
5313 if (coverage == 0) {
5314 // nothing
5315 } else if (coverage == 255) {
5316 blend_pixel(dest[x], src);
5317 } else if (src.isOpaque()) {
5318 grayBlendPixel(dest[x], coverage, srcLinear, colorProfile);
5319 } else {
5320 // First do naive blend with text-color
5321 QRgba64 s = dest[x];
5322 blend_pixel(s, src);
5323 // Then gamma-corrected blend with glyph shape
5324 if (colorProfile)
5325 s = colorProfile->toLinear(s);
5326 grayBlendPixel(dest[x], coverage, s, colorProfile);
5327 }
5328}
5329
5330static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer,
5331 int x, int y, const QRgba64 &color,
5332 const uchar *map,
5333 int mapWidth, int mapHeight, int mapStride,
5334 const QClipData *clip, bool useGammaCorrection)
5335{
5336 if (color.isTransparent())
5337 return;
5338
5339 const QColorTrcLut *colorProfile = nullptr;
5340
5341 if (useGammaCorrection)
5342 colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA8Text();
5343
5344 QRgba64 srcColor = color;
5345 if (colorProfile && color.isOpaque())
5346 srcColor = colorProfile->toLinear(srcColor);
5347
5348 alignas(8) QRgba64 buffer[BufferSize];
5349 const DestFetchProc64 destFetch64 = destFetchProc64[rasterBuffer->format];
5350 const DestStoreProc64 destStore64 = destStoreProc64[rasterBuffer->format];
5351
5352 if (!clip) {
5353 for (int ly = 0; ly < mapHeight; ++ly) {
5354 int i = x;
5355 int length = mapWidth;
5356 while (length > 0) {
5357 int l = qMin(BufferSize, length);
5358 QRgba64 *dest = destFetch64(buffer, rasterBuffer, i, y + ly, l);
5359 for (int j=0; j < l; ++j) {
5360 const int coverage = map[j + (i - x)];
5361 alphamapblend_generic(coverage, dest, j, srcColor, color, colorProfile);
5362 }
5363 if (destStore64)
5364 destStore64(rasterBuffer, i, y + ly, dest, l);
5365 length -= l;
5366 i += l;
5367 }
5368 map += mapStride;
5369 }
5370 } else {
5371 int bottom = qMin(y + mapHeight, rasterBuffer->height());
5372
5373 int top = qMax(y, 0);
5374 map += (top - y) * mapStride;
5375
5376 const_cast<QClipData *>(clip)->initialize();
5377 for (int yp = top; yp<bottom; ++yp) {
5378 const QClipData::ClipLine &line = clip->m_clipLines[yp];
5379
5380 for (int i=0; i<line.count; ++i) {
5381 const QT_FT_Span &clip = line.spans[i];
5382
5383 int start = qMax<int>(x, clip.x);
5384 int end = qMin<int>(x + mapWidth, clip.x + clip.len);
5385 if (end <= start)
5386 continue;
5388 QRgba64 *dest = destFetch64(buffer, rasterBuffer, start, clip.y, end - start);
5389
5390 for (int xp=start; xp<end; ++xp) {
5391 const int coverage = map[xp - x];
5392 alphamapblend_generic(coverage, dest, xp - start, srcColor, color, colorProfile);
5393 }
5394 if (destStore64)
5395 destStore64(rasterBuffer, start, clip.y, dest, end - start);
5396 } // for (i -> line.count)
5397 map += mapStride;
5398 } // for (yp -> bottom)
5399 }
5400}
5401#else
5402static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer,
5403 int x, int y, const QRgba64 &color,
5404 const uchar *map,
5405 int mapWidth, int mapHeight, int mapStride,
5406 const QClipData *clip, bool useGammaCorrection)
5407{
5408 if (color.isTransparent())
5409 return;
5410
5411 const quint32 c = color.toArgb32();
5412
5413 const QColorTrcLut *colorProfile = nullptr;
5414
5415 if (useGammaCorrection)
5416 colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA8Text();
5417
5418 QRgba64 srcColor = color;
5419 if (colorProfile && color.isOpaque())
5420 srcColor = colorProfile->toLinear(srcColor);
5421
5423 const DestFetchProc destFetch = destFetchProc[rasterBuffer->format];
5424 const DestStoreProc destStore = destStoreProc[rasterBuffer->format];
5425
5426 if (!clip) {
5427 for (int ly = 0; ly < mapHeight; ++ly) {
5428 int i = x;
5429 int length = mapWidth;
5430 while (length > 0) {
5431 int l = qMin(BufferSize, length);
5432 quint32 *dest = destFetch(buffer, rasterBuffer, i, y + ly, l);
5433 for (int j=0; j < l; ++j) {
5434 const int coverage = map[j + (i - x)];
5435 alphamapblend_argb32(dest + j, coverage, srcColor, c, colorProfile);
5436 }
5437 if (destStore)
5438 destStore(rasterBuffer, i, y + ly, dest, l);
5439 length -= l;
5440 i += l;
5441 }
5442 map += mapStride;
5443 }
5444 } else {
5445 int bottom = qMin(y + mapHeight, rasterBuffer->height());
5446
5447 int top = qMax(y, 0);
5448 map += (top - y) * mapStride;
5449
5450 const_cast<QClipData *>(clip)->initialize();
5451 for (int yp = top; yp<bottom; ++yp) {
5452 const QClipData::ClipLine &line = clip->m_clipLines[yp];
5453
5454 for (int i=0; i<line.count; ++i) {
5455 const QT_FT_Span &clip = line.spans[i];
5456
5457 int start = qMax<int>(x, clip.x);
5458 int end = qMin<int>(x + mapWidth, clip.x + clip.len);
5459 if (end <= start)
5460 continue;
5462 quint32 *dest = destFetch(buffer, rasterBuffer, start, clip.y, end - start);
5463
5464 for (int xp=start; xp<end; ++xp) {
5465 const int coverage = map[xp - x];
5466 alphamapblend_argb32(dest + xp - x, coverage, srcColor, color, colorProfile);
5467 }
5468 if (destStore)
5469 destStore(rasterBuffer, start, clip.y, dest, end - start);
5470 } // for (i -> line.count)
5471 map += mapStride;
5472 } // for (yp -> bottom)
5473 }
5474}
5475#endif
5476
5477static inline void alphamapblend_quint16(int coverage, quint16 *dest, int x, const quint16 srcColor)
5478{
5479 if (coverage == 0) {
5480 // nothing
5481 } else if (coverage == 255) {
5482 dest[x] = srcColor;
5483 } else {
5484 dest[x] = BYTE_MUL_RGB16(srcColor, coverage)
5485 + BYTE_MUL_RGB16(dest[x], 255 - coverage);
5486 }
5487}
5488
5490 int x, int y, const QRgba64 &color,
5491 const uchar *map,
5492 int mapWidth, int mapHeight, int mapStride,
5493 const QClipData *clip, bool useGammaCorrection)
5494{
5495 if (useGammaCorrection || !color.isOpaque()) {
5496 qt_alphamapblit_generic(rasterBuffer, x, y, color, map, mapWidth, mapHeight, mapStride, clip, useGammaCorrection);
5497 return;
5498 }
5499
5500 const quint16 c = color.toRgb16();
5501
5502 if (!clip) {
5503 quint16 *dest = reinterpret_cast<quint16*>(rasterBuffer->scanLine(y)) + x;
5504 const int destStride = rasterBuffer->stride<quint16>();
5505 while (--mapHeight >= 0) {
5506 for (int i = 0; i < mapWidth; ++i)
5507 alphamapblend_quint16(map[i], dest, i, c);
5508 dest += destStride;
5509 map += mapStride;
5510 }
5511 } else {
5512 int top = qMax(y, 0);
5513 int bottom = qMin(y + mapHeight, rasterBuffer->height());
5514 map += (top - y) * mapStride;
5515
5516 const_cast<QClipData *>(clip)->initialize();
5517 for (int yp = top; yp<bottom; ++yp) {
5518 const QClipData::ClipLine &line = clip->m_clipLines[yp];
5519
5520 quint16 *dest = reinterpret_cast<quint16*>(rasterBuffer->scanLine(yp));
5521
5522 for (int i=0; i<line.count; ++i) {
5523 const QT_FT_Span &clip = line.spans[i];
5524
5525 int start = qMax<int>(x, clip.x);
5526 int end = qMin<int>(x + mapWidth, clip.x + clip.len);
5527
5528 for (int xp=start; xp<end; ++xp)
5529 alphamapblend_quint16(map[xp - x], dest, xp, c);
5530 } // for (i -> line.count)
5531 map += mapStride;
5532 } // for (yp -> bottom)
5533 }
5534}
5535
5536static void qt_alphamapblit_argb32(QRasterBuffer *rasterBuffer,
5537 int x, int y, const QRgba64 &color,
5538 const uchar *map,
5539 int mapWidth, int mapHeight, int mapStride,
5540 const QClipData *clip, bool useGammaCorrection)
5541{
5542 const quint32 c = color.toArgb32();
5543 const int destStride = rasterBuffer->stride<quint32>();
5544
5545 if (color.isTransparent())
5546 return;
5547
5548 const QColorTrcLut *colorProfile = nullptr;
5549
5550 if (useGammaCorrection)
5551 colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA8Text();
5552
5553 QRgba64 srcColor = color;
5554 if (colorProfile && color.isOpaque())
5555 srcColor = colorProfile->toLinear(srcColor);
5556
5557 if (!clip) {
5558 quint32 *dest = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y)) + x;
5559 while (--mapHeight >= 0) {
5560 for (int i = 0; i < mapWidth; ++i) {
5561 const int coverage = map[i];
5562 alphamapblend_argb32(dest + i, coverage, srcColor, c, colorProfile);
5563 }
5564 dest += destStride;
5565 map += mapStride;
5566 }
5567 } else {
5568 int bottom = qMin(y + mapHeight, rasterBuffer->height());
5569
5570 int top = qMax(y, 0);
5571 map += (top - y) * mapStride;
5572
5573 const_cast<QClipData *>(clip)->initialize();
5574 for (int yp = top; yp<bottom; ++yp) {
5575 const QClipData::ClipLine &line = clip->m_clipLines[yp];
5576
5577 quint32 *dest = reinterpret_cast<quint32 *>(rasterBuffer->scanLine(yp));
5578
5579 for (int i=0; i<line.count; ++i) {
5580 const QT_FT_Span &clip = line.spans[i];
5581
5582 int start = qMax<int>(x, clip.x);
5583 int end = qMin<int>(x + mapWidth, clip.x + clip.len);
5584
5585 for (int xp=start; xp<end; ++xp) {
5586 const int coverage = map[xp - x];
5587 alphamapblend_argb32(dest + xp, coverage, srcColor, c, colorProfile);
5588 } // for (i -> line.count)
5589 } // for (yp -> bottom)
5590 map += mapStride;
5591 }
5592 }
5593}
5594
5595static inline int qRgbAvg(QRgb rgb)
5596{
5597 return (qRed(rgb) * 5 + qGreen(rgb) * 6 + qBlue(rgb) * 5) / 16;
5598}
5599
5600static inline void rgbBlendPixel(quint32 *dst, int coverage, QRgba64 slinear, const QColorTrcLut *colorProfile)
5601{
5602 // Do a gammacorrected RGB alphablend...
5603 const QRgba64 dlinear = colorProfile ? colorProfile->toLinear64(*dst) : QRgba64::fromArgb32(*dst);
5604
5605 QRgba64 blend = rgbBlend(dlinear, slinear, coverage);
5606
5607 *dst = colorProfile ? colorProfile->fromLinear64(blend) : toArgb32(blend);
5608}
5609
5610static inline QRgb rgbBlend(QRgb d, QRgb s, uint rgbAlpha)
5611{
5612#if defined(__SSE2__)
5613 __m128i vd = _mm_cvtsi32_si128(d);
5614 __m128i vs = _mm_cvtsi32_si128(s);
5615 __m128i va = _mm_cvtsi32_si128(rgbAlpha);
5616 const __m128i vz = _mm_setzero_si128();
5617 vd = _mm_unpacklo_epi8(vd, vz);
5618 vs = _mm_unpacklo_epi8(vs, vz);
5619 va = _mm_unpacklo_epi8(va, vz);
5620 __m128i vb = _mm_xor_si128(_mm_set1_epi16(255), va);
5621 vs = _mm_mullo_epi16(vs, va);
5622 vd = _mm_mullo_epi16(vd, vb);
5623 vd = _mm_add_epi16(vd, vs);
5624 vd = _mm_add_epi16(vd, _mm_srli_epi16(vd, 8));
5625 vd = _mm_add_epi16(vd, _mm_set1_epi16(0x80));
5626 vd = _mm_srli_epi16(vd, 8);
5627 vd = _mm_packus_epi16(vd, vd);
5628 return _mm_cvtsi128_si32(vd);
5629#else
5630 const int dr = qRed(d);
5631 const int dg = qGreen(d);
5632 const int db = qBlue(d);
5633
5634 const int sr = qRed(s);
5635 const int sg = qGreen(s);
5636 const int sb = qBlue(s);
5637
5638 const int mr = qRed(rgbAlpha);
5639 const int mg = qGreen(rgbAlpha);
5640 const int mb = qBlue(rgbAlpha);
5641
5642 const int nr = qt_div_255(sr * mr + dr * (255 - mr));
5643 const int ng = qt_div_255(sg * mg + dg * (255 - mg));
5644 const int nb = qt_div_255(sb * mb + db * (255 - mb));
5645
5646 return 0xff000000 | (nr << 16) | (ng << 8) | nb;
5647#endif
5648}
5649
5650static inline void alphargbblend_argb32(quint32 *dst, uint coverage, const QRgba64 &srcLinear, quint32 src, const QColorTrcLut *colorProfile)
5651{
5652 if (coverage == 0xff000000) {
5653 // nothing
5654 } else if (coverage == 0xffffffff && qAlpha(src) == 255) {
5655 blend_pixel(*dst, src);
5656 } else if (*dst < 0xff000000) {
5657 // Give up and do a naive gray alphablend. Needed to deal with ARGB32 and invalid ARGB32_premultiplied, see QTBUG-60571
5658 blend_pixel(*dst, src, qRgbAvg(coverage));
5659 } else if (!colorProfile) {
5660 // First do naive blend with text-color
5661 QRgb s = *dst;
5662 blend_pixel(s, src);
5663 // Then a naive blend with glyph shape
5664 *dst = rgbBlend(*dst, s, coverage);
5665 } else if (srcLinear.isOpaque()) {
5666 rgbBlendPixel(dst, coverage, srcLinear, colorProfile);
5667 } else {
5668 // First do naive blend with text-color
5669 QRgb s = *dst;
5670 blend_pixel(s, src);
5671 // Then gamma-corrected blend with glyph shape
5672 QRgba64 s64 = colorProfile ? colorProfile->toLinear64(s) : QRgba64::fromArgb32(s);
5673 rgbBlendPixel(dst, coverage, s64, colorProfile);
5674 }
5675}
5676
5677#if QT_CONFIG(raster_64bit)
5678static inline void rgbBlendPixel(QRgba64 &dst, int coverage, QRgba64 slinear, const QColorTrcLut *colorProfile)
5679{
5680 // Do a gammacorrected RGB alphablend...
5681 const QRgba64 dlinear = colorProfile ? colorProfile->toLinear(dst) : dst;
5682
5683 QRgba64 blend = rgbBlend(dlinear, slinear, coverage);
5684
5685 dst = colorProfile ? colorProfile->fromLinear(blend) : blend;
5686}
5687
5688static inline void alphargbblend_generic(uint coverage, QRgba64 *dest, int x, const QRgba64 &srcLinear, const QRgba64 &src, const QColorTrcLut *colorProfile)
5689{
5690 if (coverage == 0xff000000) {
5691 // nothing
5692 } else if (coverage == 0xffffffff) {
5693 blend_pixel(dest[x], src);
5694 } else if (!dest[x].isOpaque()) {
5695 // Do a gray alphablend.
5696 alphamapblend_generic(qRgbAvg(coverage), dest, x, srcLinear, src, colorProfile);
5697 } else if (src.isOpaque()) {
5698 rgbBlendPixel(dest[x], coverage, srcLinear, colorProfile);
5699 } else {
5700 // First do naive blend with text-color
5701 QRgba64 s = dest[x];
5702 blend_pixel(s, src);
5703 // Then gamma-corrected blend with glyph shape
5704 if (colorProfile)
5705 s = colorProfile->toLinear(s);
5706 rgbBlendPixel(dest[x], coverage, s, colorProfile);
5707 }
5708}
5709
5710static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer,
5711 int x, int y, const QRgba64 &color,
5712 const uint *src, int mapWidth, int mapHeight, int srcStride,
5713 const QClipData *clip, bool useGammaCorrection)
5714{
5715 if (color.isTransparent())
5716 return;
5717
5718 const QColorTrcLut *colorProfile = nullptr;
5719
5720 if (useGammaCorrection)
5721 colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA32Text();
5722
5723 QRgba64 srcColor = color;
5724 if (colorProfile && color.isOpaque())
5725 srcColor = colorProfile->toLinear(srcColor);
5726
5727 alignas(8) QRgba64 buffer[BufferSize];
5728 const DestFetchProc64 destFetch64 = destFetchProc64[rasterBuffer->format];
5729 const DestStoreProc64 destStore64 = destStoreProc64[rasterBuffer->format];
5730
5731 if (!clip) {
5732 for (int ly = 0; ly < mapHeight; ++ly) {
5733 int i = x;
5734 int length = mapWidth;
5735 while (length > 0) {
5736 int l = qMin(BufferSize, length);
5737 QRgba64 *dest = destFetch64(buffer, rasterBuffer, i, y + ly, l);
5738 for (int j=0; j < l; ++j) {
5739 const uint coverage = src[j + (i - x)];
5740 alphargbblend_generic(coverage, dest, j, srcColor, color, colorProfile);
5741 }
5742 if (destStore64)
5743 destStore64(rasterBuffer, i, y + ly, dest, l);
5744 length -= l;
5745 i += l;
5746 }
5747 src += srcStride;
5748 }
5749 } else {
5750 int bottom = qMin(y + mapHeight, rasterBuffer->height());
5751
5752 int top = qMax(y, 0);
5753 src += (top - y) * srcStride;
5754
5755 const_cast<QClipData *>(clip)->initialize();
5756 for (int yp = top; yp<bottom; ++yp) {
5757 const QClipData::ClipLine &line = clip->m_clipLines[yp];
5758
5759 for (int i=0; i<line.count; ++i) {
5760 const QT_FT_Span &clip = line.spans[i];
5761
5762 int start = qMax<int>(x, clip.x);
5763 int end = qMin<int>(x + mapWidth, clip.x + clip.len);
5764 if (end <= start)
5765 continue;
5767 QRgba64 *dest = destFetch64(buffer, rasterBuffer, start, clip.y, end - start);
5768
5769 for (int xp=start; xp<end; ++xp) {
5770 const uint coverage = src[xp - x];
5771 alphargbblend_generic(coverage, dest, xp - start, srcColor, color, colorProfile);
5772 }
5773 if (destStore64)
5774 destStore64(rasterBuffer, start, clip.y, dest, end - start);
5775 } // for (i -> line.count)
5776 src += srcStride;
5777 } // for (yp -> bottom)
5778 }
5779}
5780#else
5781static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer,
5782 int x, int y, const QRgba64 &color,
5783 const uint *src, int mapWidth, int mapHeight, int srcStride,
5784 const QClipData *clip, bool useGammaCorrection)
5785{
5786 if (color.isTransparent())
5787 return;
5788
5789 const quint32 c = color.toArgb32();
5790
5791 const QColorTrcLut *colorProfile = nullptr;
5792
5793 if (useGammaCorrection)
5794 colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA32Text();
5795
5796 QRgba64 srcColor = color;
5797 if (colorProfile && color.isOpaque())
5798 srcColor = colorProfile->toLinear(srcColor);
5799
5801 const DestFetchProc destFetch = destFetchProc[rasterBuffer->format];
5802 const DestStoreProc destStore = destStoreProc[rasterBuffer->format];
5803
5804 if (!clip) {
5805 for (int ly = 0; ly < mapHeight; ++ly) {
5806 int i = x;
5807 int length = mapWidth;
5808 while (length > 0) {
5809 int l = qMin(BufferSize, length);
5810 quint32 *dest = destFetch(buffer, rasterBuffer, i, y + ly, l);
5811 for (int j=0; j < l; ++j) {
5812 const uint coverage = src[j + (i - x)];
5813 alphargbblend_argb32(dest + j, coverage, srcColor, c, colorProfile);
5814 }
5815 if (destStore)
5816 destStore(rasterBuffer, i, y + ly, dest, l);
5817 length -= l;
5818 i += l;
5819 }
5820 src += srcStride;
5821 }
5822 } else {
5823 int bottom = qMin(y + mapHeight, rasterBuffer->height());
5824
5825 int top = qMax(y, 0);
5826 src += (top - y) * srcStride;
5827
5828 const_cast<QClipData *>(clip)->initialize();
5829 for (int yp = top; yp<bottom; ++yp) {
5830 const QClipData::ClipLine &line = clip->m_clipLines[yp];
5831
5832 for (int i=0; i<line.count; ++i) {
5833 const QT_FT_Span &clip = line.spans[i];
5834
5835 int start = qMax<int>(x, clip.x);
5836 int end = qMin<int>(x + mapWidth, clip.x + clip.len);
5837 if (end <= start)
5838 continue;
5840 quint32 *dest = destFetch(buffer, rasterBuffer, start, clip.y, end - start);
5841
5842 for (int xp=start; xp<end; ++xp) {
5843 const uint coverage = src[xp - x];
5844 alphargbblend_argb32(dest + xp - start, coverage, srcColor, c, colorProfile);
5845 }
5846 if (destStore)
5847 destStore(rasterBuffer, start, clip.y, dest, end - start);
5848 } // for (i -> line.count)
5849 src += srcStride;
5850 } // for (yp -> bottom)
5851 }
5852}
5853#endif
5854
5855static void qt_alphargbblit_argb32(QRasterBuffer *rasterBuffer,
5856 int x, int y, const QRgba64 &color,
5857 const uint *src, int mapWidth, int mapHeight, int srcStride,
5858 const QClipData *clip, bool useGammaCorrection)
5859{
5860 if (color.isTransparent())
5861 return;
5862
5863 const quint32 c = color.toArgb32();
5864
5865 const QColorTrcLut *colorProfile = nullptr;
5866
5867 if (useGammaCorrection)
5868 colorProfile = QGuiApplicationPrivate::instance()->colorProfileForA32Text();
5869
5870 QRgba64 srcColor = color;
5871 if (colorProfile && color.isOpaque())
5872 srcColor = colorProfile->toLinear(srcColor);
5873
5874 if (!clip) {
5875 quint32 *dst = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y)) + x;
5876 const int destStride = rasterBuffer->stride<quint32>();
5877 while (--mapHeight >= 0) {
5878 for (int i = 0; i < mapWidth; ++i) {
5879 const uint coverage = src[i];
5880 alphargbblend_argb32(dst + i, coverage, srcColor, c, colorProfile);
5881 }
5882
5883 dst += destStride;
5884 src += srcStride;
5885 }
5886 } else {
5887 int bottom = qMin(y + mapHeight, rasterBuffer->height());
5888
5889 int top = qMax(y, 0);
5890 src += (top - y) * srcStride;
5891
5892 const_cast<QClipData *>(clip)->initialize();
5893 for (int yp = top; yp<bottom; ++yp) {
5894 const QClipData::ClipLine &line = clip->m_clipLines[yp];
5895
5896 quint32 *dst = reinterpret_cast<quint32 *>(rasterBuffer->scanLine(yp));
5897
5898 for (int i=0; i<line.count; ++i) {
5899 const QT_FT_Span &clip = line.spans[i];
5900
5901 int start = qMax<int>(x, clip.x);
5902 int end = qMin<int>(x + mapWidth, clip.x + clip.len);
5903
5904 for (int xp=start; xp<end; ++xp) {
5905 const uint coverage = src[xp - x];
5906 alphargbblend_argb32(dst + xp, coverage, srcColor, c, colorProfile);
5907 }
5908 } // for (i -> line.count)
5909 src += srcStride;
5910 } // for (yp -> bottom)
5911
5912 }
5913}
5914
5915static void qt_rectfill_argb32(QRasterBuffer *rasterBuffer,
5916 int x, int y, int width, int height,
5917 const QRgba64 &color)
5918{
5919 qt_rectfill<quint32>(reinterpret_cast<quint32 *>(rasterBuffer->buffer()),
5920 color.toArgb32(), x, y, width, height, rasterBuffer->bytesPerLine());
5921}
5922
5923static void qt_rectfill_quint16(QRasterBuffer *rasterBuffer,
5924 int x, int y, int width, int height,
5925 const QRgba64 &color)
5926{
5927 const QPixelLayout &layout = qPixelLayouts[rasterBuffer->format];
5928 quint32 c32 = color.toArgb32();
5929 quint16 c16;
5930 layout.storeFromARGB32PM(reinterpret_cast<uchar *>(&c16), &c32, 0, 1, nullptr, nullptr);
5931 qt_rectfill<quint16>(reinterpret_cast<quint16 *>(rasterBuffer->buffer()),
5932 c16, x, y, width, height, rasterBuffer->bytesPerLine());
5933}
5934
5935static void qt_rectfill_quint24(QRasterBuffer *rasterBuffer,
5936 int x, int y, int width, int height,
5937 const QRgba64 &color)
5938{
5939 const QPixelLayout &layout = qPixelLayouts[rasterBuffer->format];
5940 quint32 c32 = color.toArgb32();
5941 quint24 c24;
5942 layout.storeFromARGB32PM(reinterpret_cast<uchar *>(&c24), &c32, 0, 1, nullptr, nullptr);
5943 qt_rectfill<quint24>(reinterpret_cast<quint24 *>(rasterBuffer->buffer()),
5944 c24, x, y, width, height, rasterBuffer->bytesPerLine());
5945}
5946
5948 int x, int y, int width, int height,
5949 const QRgba64 &color)
5950{
5951 qt_rectfill<quint32>(reinterpret_cast<quint32 *>(rasterBuffer->buffer()),
5952 color.unpremultiplied().toArgb32(), x, y, width, height, rasterBuffer->bytesPerLine());
5953}
5954
5955static void qt_rectfill_rgba(QRasterBuffer *rasterBuffer,
5956 int x, int y, int width, int height,
5957 const QRgba64 &color)
5958{
5959 qt_rectfill<quint32>(reinterpret_cast<quint32 *>(rasterBuffer->buffer()),
5960 ARGB2RGBA(color.toArgb32()), x, y, width, height, rasterBuffer->bytesPerLine());
5961}
5962
5964 int x, int y, int width, int height,
5965 const QRgba64 &color)
5966{
5967 qt_rectfill<quint32>(reinterpret_cast<quint32 *>(rasterBuffer->buffer()),
5968 ARGB2RGBA(color.unpremultiplied().toArgb32()), x, y, width, height, rasterBuffer->bytesPerLine());
5969}
5970
5971template<QtPixelOrder PixelOrder>
5972static void qt_rectfill_rgb30(QRasterBuffer *rasterBuffer,
5973 int x, int y, int width, int height,
5974 const QRgba64 &color)
5975{
5976 qt_rectfill<quint32>(reinterpret_cast<quint32 *>(rasterBuffer->buffer()),
5977 qConvertRgb64ToRgb30<PixelOrder>(color), x, y, width, height, rasterBuffer->bytesPerLine());
5978}
5979
5980static void qt_rectfill_alpha(QRasterBuffer *rasterBuffer,
5981 int x, int y, int width, int height,
5982 const QRgba64 &color)
5983{
5984 qt_rectfill<quint8>(reinterpret_cast<quint8 *>(rasterBuffer->buffer()),
5985 color.alpha() >> 8, x, y, width, height, rasterBuffer->bytesPerLine());
5986}
5987
5988static void qt_rectfill_gray(QRasterBuffer *rasterBuffer,
5989 int x, int y, int width, int height,
5990 const QRgba64 &color)
5991{
5992 qt_rectfill<quint8>(reinterpret_cast<quint8 *>(rasterBuffer->buffer()),
5993 qGray(color.toArgb32()), x, y, width, height, rasterBuffer->bytesPerLine());
5994}
5995
5996static void qt_rectfill_quint64(QRasterBuffer *rasterBuffer,
5997 int x, int y, int width, int height,
5998 const QRgba64 &color)
5999{
6000 const auto store = qStoreFromRGBA64PM[rasterBuffer->format];
6001 quint64 c64;
6002 store(reinterpret_cast<uchar *>(&c64), &color, 0, 1, nullptr, nullptr);
6003 qt_rectfill<quint64>(reinterpret_cast<quint64 *>(rasterBuffer->buffer()),
6004 c64, x, y, width, height, rasterBuffer->bytesPerLine());
6005}
6006
6007static void qt_rectfill_fp32x4(QRasterBuffer *rasterBuffer,
6008 int x, int y, int width, int height,
6009 const QRgba64 &color)
6010{
6011 const auto store = qStoreFromRGBA64PM[rasterBuffer->format];
6013 store(reinterpret_cast<uchar *>(&c), &color, 0, 1, nullptr, nullptr);
6014 qt_rectfill<QRgbaFloat32>(reinterpret_cast<QRgbaFloat32 *>(rasterBuffer->buffer()),
6015 c, x, y, width, height, rasterBuffer->bytesPerLine());
6016}
6017
6018// Map table for destination image format. Contains function pointers
6019// for blends of various types unto the destination
6020
6022{
6023 // Format_Invalid,
6024 { nullptr, nullptr, nullptr, nullptr, nullptr },
6025 // Format_Mono,
6026 {
6028 nullptr, nullptr, nullptr, nullptr
6029 },
6030 // Format_MonoLSB,
6031 {
6033 nullptr, nullptr, nullptr, nullptr
6034 },
6035 // Format_Indexed8,
6036 {
6038 nullptr, nullptr, nullptr, nullptr
6039 },
6040 // Format_RGB32,
6041 {
6047 },
6048 // Format_ARGB32,
6049 {
6055 },
6056 // Format_ARGB32_Premultiplied
6057 {
6063 },
6064 // Format_RGB16
6065 {
6071 },
6072 // Format_ARGB8565_Premultiplied
6073 {
6075 nullptr,
6079 },
6080 // Format_RGB666
6081 {
6083 nullptr,
6087 },
6088 // Format_ARGB6666_Premultiplied
6089 {
6091 nullptr,
6095 },
6096 // Format_RGB555
6097 {
6099 nullptr,
6103 },
6104 // Format_ARGB8555_Premultiplied
6105 {
6107 nullptr,
6111 },
6112 // Format_RGB888
6113 {
6115 nullptr,
6119 },
6120 // Format_RGB444
6121 {
6123 nullptr,
6127 },
6128 // Format_ARGB4444_Premultiplied
6129 {
6131 nullptr,
6135 },
6136 // Format_RGBX8888
6137 {
6143 },
6144 // Format_RGBA8888
6145 {
6151 },
6152 // Format_RGB8888_Premultiplied
6153 {
6159 },
6160 // Format_BGR30
6161 {
6163 qt_bitmapblit_rgb30<PixelOrderBGR>,
6166 qt_rectfill_rgb30<PixelOrderBGR>
6167 },
6168 // Format_A2BGR30_Premultiplied
6169 {
6171 qt_bitmapblit_rgb30<PixelOrderBGR>,
6174 qt_rectfill_rgb30<PixelOrderBGR>
6175 },
6176 // Format_RGB30
6177 {
6179 qt_bitmapblit_rgb30<PixelOrderRGB>,
6182 qt_rectfill_rgb30<PixelOrderRGB>
6183 },
6184 // Format_A2RGB30_Premultiplied
6185 {
6187 qt_bitmapblit_rgb30<PixelOrderRGB>,
6190 qt_rectfill_rgb30<PixelOrderRGB>
6191 },
6192 // Format_Alpha8
6193 {
6195 nullptr,
6199 },
6200 // Format_Grayscale8
6201 {
6203 nullptr,
6207 },
6208 // Format_RGBX64
6209 {
6211 nullptr,
6215 },
6216 // Format_RGBA64
6217 {
6219 nullptr,
6223 },
6224 // Format_RGBA64_Premultiplied
6225 {
6227 nullptr,
6231 },
6232 // Format_Grayscale16
6233 {
6235 nullptr,
6239 },
6240 // Format_BGR888
6241 {
6243 nullptr,
6247 },
6248 // Format_RGBX16FPx4
6249 {
6251 nullptr,
6255 },
6256 // Format_RGBA16FPx4
6257 {
6259 nullptr,
6263 },
6264 // Format_RGBA16FPx4_Premultiplied
6265 {
6267 nullptr,
6271 },
6272 // Format_RGBX32FPx4
6273 {
6275 nullptr,
6279 },
6280 // Format_RGBA32FPx4
6281 {
6283 nullptr,
6287 },
6288 // Format_RGBA32FPx4_Premultiplied
6289 {
6291 nullptr,
6295 },
6296};
6297
6298static_assert(std::size(qDrawHelper) == QImage::NImageFormats);
6299
6300#if !defined(Q_PROCESSOR_X86)
6302{
6303 qt_memfill_template<quint64>(dest, color, count);
6304}
6305#endif
6306
6307#if defined(QT_COMPILER_SUPPORTS_SSSE3) && defined(Q_CC_GNU) && !defined(Q_CC_CLANG)
6308__attribute__((optimize("no-tree-vectorize")))
6309#endif
6311{
6312# ifdef QT_COMPILER_SUPPORTS_SSSE3
6313 extern void qt_memfill24_ssse3(quint24 *, quint24, qsizetype);
6314 if (qCpuHasFeature(SSSE3))
6315 return qt_memfill24_ssse3(dest, color, count);
6316# endif
6317
6318 const quint32 v = color;
6319 quint24 *end = dest + count;
6320
6321 // prolog: align dest to 32bit
6322 while ((quintptr(dest) & 0x3) && dest < end) {
6323 *dest++ = v;
6324 }
6325 if (dest >= end)
6326 return;
6327
6328 const uint val1 = qFromBigEndian((v << 8) | (v >> 16));
6329 const uint val2 = qFromBigEndian((v << 16) | (v >> 8));
6330 const uint val3 = qFromBigEndian((v << 24) | (v >> 0));
6331
6332 for ( ; dest <= (end - 4); dest += 4) {
6333 quint32 *dst = reinterpret_cast<quint32 *>(dest);
6334 dst[0] = val1;
6335 dst[1] = val2;
6336 dst[2] = val3;
6337 }
6338
6339 // less than 4px left
6340 switch (end - dest) {
6341 case 3:
6342 *dest++ = v;
6343 Q_FALLTHROUGH();
6344 case 2:
6345 *dest++ = v;
6346 Q_FALLTHROUGH();
6347 case 1:
6348 *dest++ = v;
6349 }
6350}
6351
6353{
6354 const int align = quintptr(dest) & 0x3;
6355 if (align) {
6356 *dest++ = value;
6357 --count;
6358 }
6359
6360 if (count & 0x1)
6361 dest[count - 1] = value;
6362
6363 const quint32 value32 = (value << 16) | value;
6364 qt_memfill32(reinterpret_cast<quint32*>(dest), value32, count / 2);
6365}
6366
6367#if defined(Q_PROCESSOR_X86)
6368void (*qt_memfill32)(quint32 *dest, quint32 value, qsizetype count) = nullptr;
6369void (*qt_memfill64)(quint64 *dest, quint64 value, qsizetype count) = nullptr;
6370#elif !defined(__ARM_NEON__) && !defined(__MIPS_DSP__)
6372{
6373 qt_memfill_template<quint32>(dest, color, count);
6374}
6375#endif
6376
6377#ifdef QT_COMPILER_SUPPORTS_SSE4_1
6378template<QtPixelOrder> void QT_FASTCALL storeA2RGB30PMFromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6379#endif
6380
6381extern void qInitBlendFunctions();
6382
6384{
6385 // Set up basic blend function tables.
6387
6388#if defined(Q_PROCESSOR_X86) && !defined(__SSE2__)
6389 qt_memfill32 = qt_memfill_template<quint32>;
6390 qt_memfill64 = qt_memfill_template<quint64>;
6391#elif defined(__SSE2__)
6392# ifndef __haswell__
6393 qt_memfill32 = qt_memfill32_sse2;
6394 qt_memfill64 = qt_memfill64_sse2;
6395# endif
6396 qDrawHelper[QImage::Format_RGB32].bitmapBlit = qt_bitmapblit32_sse2;
6397 qDrawHelper[QImage::Format_ARGB32].bitmapBlit = qt_bitmapblit32_sse2;
6399 qDrawHelper[QImage::Format_RGB16].bitmapBlit = qt_bitmapblit16_sse2;
6400 qDrawHelper[QImage::Format_RGBX8888].bitmapBlit = qt_bitmapblit8888_sse2;
6401 qDrawHelper[QImage::Format_RGBA8888].bitmapBlit = qt_bitmapblit8888_sse2;
6403
6404 extern void qt_scale_image_argb32_on_argb32_sse2(uchar *destPixels, int dbpl,
6405 const uchar *srcPixels, int sbpl, int srch,
6406 const QRectF &targetRect,
6407 const QRectF &sourceRect,
6408 const QRect &clip,
6409 int const_alpha);
6411 qScaleFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_scale_image_argb32_on_argb32_sse2;
6413 qScaleFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_scale_image_argb32_on_argb32_sse2;
6414
6415 extern void qt_blend_rgb32_on_rgb32_sse2(uchar *destPixels, int dbpl,
6416 const uchar *srcPixels, int sbpl,
6417 int w, int h,
6418 int const_alpha);
6419 extern void qt_blend_argb32_on_argb32_sse2(uchar *destPixels, int dbpl,
6420 const uchar *srcPixels, int sbpl,
6421 int w, int h,
6422 int const_alpha);
6423
6424 qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse2;
6428 qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_sse2;
6432
6433 extern const uint * QT_FASTCALL qt_fetch_radial_gradient_sse2(uint *buffer, const Operator *op, const QSpanData *data,
6434 int y, int x, int length);
6435
6436 qt_fetch_radial_gradient = qt_fetch_radial_gradient_sse2;
6437
6438 extern void QT_FASTCALL comp_func_SourceOver_sse2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
6439 extern void QT_FASTCALL comp_func_solid_SourceOver_sse2(uint *destPixels, int length, uint color, uint const_alpha);
6440 extern void QT_FASTCALL comp_func_Source_sse2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
6441 extern void QT_FASTCALL comp_func_solid_Source_sse2(uint *destPixels, int length, uint color, uint const_alpha);
6442 extern void QT_FASTCALL comp_func_Plus_sse2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
6444 qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_sse2;
6446 qt_functionForModeSolid_C[QPainter::CompositionMode_Source] = comp_func_solid_Source_sse2;
6448
6449#ifdef QT_COMPILER_SUPPORTS_SSSE3
6450 if (qCpuHasFeature(SSSE3)) {
6451 extern void qt_blend_argb32_on_argb32_ssse3(uchar *destPixels, int dbpl,
6452 const uchar *srcPixels, int sbpl,
6453 int w, int h,
6454 int const_alpha);
6455
6456 extern const uint * QT_FASTCALL qt_fetchUntransformed_888_ssse3(uint *buffer, const Operator *, const QSpanData *data,
6457 int y, int x, int length);
6462 sourceFetchUntransformed[QImage::Format_RGB888] = qt_fetchUntransformed_888_ssse3;
6463 extern void QT_FASTCALL rbSwap_888_ssse3(uchar *dst, const uchar *src, int count);
6464 qPixelLayouts[QImage::Format_RGB888].rbSwap = rbSwap_888_ssse3;
6465 qPixelLayouts[QImage::Format_BGR888].rbSwap = rbSwap_888_ssse3;
6466 }
6467#endif // SSSE3
6468
6469#if defined(QT_COMPILER_SUPPORTS_SSE4_1)
6470 if (qCpuHasFeature(SSE4_1)) {
6471 extern void QT_FASTCALL convertARGB32ToARGB32PM_sse4(uint *buffer, int count, const QList<QRgb> *);
6472 extern void QT_FASTCALL convertRGBA8888ToARGB32PM_sse4(uint *buffer, int count, const QList<QRgb> *);
6473 extern const uint *QT_FASTCALL fetchARGB32ToARGB32PM_sse4(uint *buffer, const uchar *src, int index, int count,
6474 const QList<QRgb> *, QDitherInfo *);
6475 extern const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_sse4(uint *buffer, const uchar *src, int index, int count,
6476 const QList<QRgb> *, QDitherInfo *);
6477 extern const QRgba64 * QT_FASTCALL convertARGB32ToRGBA64PM_sse4(QRgba64 *buffer, const uint *src, int count,
6478 const QList<QRgb> *, QDitherInfo *);
6479 extern const QRgba64 * QT_FASTCALL convertRGBA8888ToRGBA64PM_sse4(QRgba64 *buffer, const uint *src, int count,
6480 const QList<QRgb> *, QDitherInfo *);
6481 extern const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM_sse4(QRgba64 *buffer, const uchar *src, int index, int count,
6482 const QList<QRgb> *, QDitherInfo *);
6483 extern const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM_sse4(QRgba64 *buffer, const uchar *src, int index, int count,
6484 const QList<QRgb> *, QDitherInfo *);
6485 extern void QT_FASTCALL storeARGB32FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count,
6486 const QList<QRgb> *, QDitherInfo *);
6487 extern void QT_FASTCALL storeRGBA8888FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count,
6488 const QList<QRgb> *, QDitherInfo *);
6489 extern void QT_FASTCALL storeRGBXFromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count,
6490 const QList<QRgb> *, QDitherInfo *);
6491 extern void QT_FASTCALL storeARGB32FromRGBA64PM_sse4(uchar *dest, const QRgba64 *src, int index, int count,
6492 const QList<QRgb> *, QDitherInfo *);
6493 extern void QT_FASTCALL storeRGBA8888FromRGBA64PM_sse4(uchar *dest, const QRgba64 *src, int index, int count,
6494 const QList<QRgb> *, QDitherInfo *);
6495 extern void QT_FASTCALL storeRGBA64FromRGBA64PM_sse4(uchar *, const QRgba64 *, int, int, const QList<QRgb> *, QDitherInfo *);
6496 extern void QT_FASTCALL storeRGBx64FromRGBA64PM_sse4(uchar *, const QRgba64 *, int, int, const QList<QRgb> *, QDitherInfo *);
6497 extern void QT_FASTCALL destStore64ARGB32_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length);
6498 extern void QT_FASTCALL destStore64RGBA8888_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length);
6499# ifndef __haswell__
6500 qPixelLayouts[QImage::Format_ARGB32].fetchToARGB32PM = fetchARGB32ToARGB32PM_sse4;
6501 qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_sse4;
6502 qPixelLayouts[QImage::Format_RGBA8888].fetchToARGB32PM = fetchRGBA8888ToARGB32PM_sse4;
6503 qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_sse4;
6504 qPixelLayouts[QImage::Format_ARGB32].fetchToRGBA64PM = fetchARGB32ToRGBA64PM_sse4;
6505 qPixelLayouts[QImage::Format_ARGB32].convertToRGBA64PM = convertARGB32ToRGBA64PM_sse4;
6506 qPixelLayouts[QImage::Format_RGBA8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_sse4;
6507 qPixelLayouts[QImage::Format_RGBA8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_sse4;
6508 qPixelLayouts[QImage::Format_RGBX8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_sse4;
6509 qPixelLayouts[QImage::Format_RGBX8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_sse4;
6510# endif
6511 qPixelLayouts[QImage::Format_ARGB32].storeFromARGB32PM = storeARGB32FromARGB32PM_sse4;
6512 qPixelLayouts[QImage::Format_RGBA8888].storeFromARGB32PM = storeRGBA8888FromARGB32PM_sse4;
6513 qPixelLayouts[QImage::Format_RGBX8888].storeFromARGB32PM = storeRGBXFromARGB32PM_sse4;
6514 qPixelLayouts[QImage::Format_A2BGR30_Premultiplied].storeFromARGB32PM = storeA2RGB30PMFromARGB32PM_sse4<PixelOrderBGR>;
6515 qPixelLayouts[QImage::Format_A2RGB30_Premultiplied].storeFromARGB32PM = storeA2RGB30PMFromARGB32PM_sse4<PixelOrderRGB>;
6516 qStoreFromRGBA64PM[QImage::Format_ARGB32] = storeARGB32FromRGBA64PM_sse4;
6517 qStoreFromRGBA64PM[QImage::Format_RGBA8888] = storeRGBA8888FromRGBA64PM_sse4;
6518 qStoreFromRGBA64PM[QImage::Format_RGBX64] = storeRGBx64FromRGBA64PM_sse4;
6519 qStoreFromRGBA64PM[QImage::Format_RGBA64] = storeRGBA64FromRGBA64PM_sse4;
6520#if QT_CONFIG(raster_64bit)
6521 destStoreProc64[QImage::Format_ARGB32] = destStore64ARGB32_sse4;
6522 destStoreProc64[QImage::Format_RGBA8888] = destStore64RGBA8888_sse4;
6523#endif
6524#if QT_CONFIG(raster_fp)
6525 extern const QRgbaFloat32 *QT_FASTCALL fetchRGBA32FToRGBA32F_sse4(QRgbaFloat32 *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6526 extern void QT_FASTCALL storeRGBX32FFromRGBA32F_sse4(uchar *dest, const QRgbaFloat32 *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6527 extern void QT_FASTCALL storeRGBA32FFromRGBA32F_sse4(uchar *dest, const QRgbaFloat32 *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6528 qFetchToRGBA32F[QImage::Format_RGBA32FPx4] = fetchRGBA32FToRGBA32F_sse4;
6529 qStoreFromRGBA32F[QImage::Format_RGBX32FPx4] = storeRGBX32FFromRGBA32F_sse4;
6530 qStoreFromRGBA32F[QImage::Format_RGBA32FPx4] = storeRGBA32FFromRGBA32F_sse4;
6531#endif // QT_CONFIG(raster_fp)
6532 }
6533#endif
6534
6535#if defined(QT_COMPILER_SUPPORTS_AVX2)
6536 if (qCpuHasFeature(ArchHaswell)) {
6537 qt_memfill32 = qt_memfill32_avx2;
6538 qt_memfill64 = qt_memfill64_avx2;
6539 extern void qt_blend_rgb32_on_rgb32_avx2(uchar *destPixels, int dbpl,
6540 const uchar *srcPixels, int sbpl,
6541 int w, int h, int const_alpha);
6542 extern void qt_blend_argb32_on_argb32_avx2(uchar *destPixels, int dbpl,
6543 const uchar *srcPixels, int sbpl,
6544 int w, int h, int const_alpha);
6545 qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_avx2;
6549 qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_avx2;
6553
6554 extern void QT_FASTCALL comp_func_Source_avx2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
6555 extern void QT_FASTCALL comp_func_SourceOver_avx2(uint *destPixels, const uint *srcPixels, int length, uint const_alpha);
6556 extern void QT_FASTCALL comp_func_solid_SourceOver_avx2(uint *destPixels, int length, uint color, uint const_alpha);
6559 qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_avx2;
6560#if QT_CONFIG(raster_64bit)
6561 extern void QT_FASTCALL comp_func_Source_rgb64_avx2(QRgba64 *destPixels, const QRgba64 *srcPixels, int length, uint const_alpha);
6562 extern void QT_FASTCALL comp_func_SourceOver_rgb64_avx2(QRgba64 *destPixels, const QRgba64 *srcPixels, int length, uint const_alpha);
6563 extern void QT_FASTCALL comp_func_solid_SourceOver_rgb64_avx2(QRgba64 *destPixels, int length, QRgba64 color, uint const_alpha);
6564 qt_functionForMode64_C[QPainter::CompositionMode_Source] = comp_func_Source_rgb64_avx2;
6565 qt_functionForMode64_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_rgb64_avx2;
6566 qt_functionForModeSolid64_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_rgb64_avx2;
6567#endif
6568#if QT_CONFIG(raster_fp)
6569 extern void QT_FASTCALL comp_func_Source_rgbafp_avx2(QRgbaFloat32 *destPixels, const QRgbaFloat32 *srcPixels, int length, uint const_alpha);
6570 extern void QT_FASTCALL comp_func_SourceOver_rgbafp_avx2(QRgbaFloat32 *destPixels, const QRgbaFloat32 *srcPixels, int length, uint const_alpha);
6571 extern void QT_FASTCALL comp_func_solid_Source_rgbafp_avx2(QRgbaFloat32 *destPixels, int length, QRgbaFloat32 color, uint const_alpha);
6572 extern void QT_FASTCALL comp_func_solid_SourceOver_rgbafp_avx2(QRgbaFloat32 *destPixels, int length, QRgbaFloat32 color, uint const_alpha);
6573 qt_functionForModeFP_C[QPainter::CompositionMode_Source] = comp_func_Source_rgbafp_avx2;
6574 qt_functionForModeFP_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_rgbafp_avx2;
6575 qt_functionForModeSolidFP_C[QPainter::CompositionMode_Source] = comp_func_solid_Source_rgbafp_avx2;
6576 qt_functionForModeSolidFP_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_rgbafp_avx2;
6577#endif
6578
6579 extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_scale_helper_avx2(uint *b, uint *end, const QTextureData &image,
6580 int &fx, int &fy, int fdx, int /*fdy*/);
6581 extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_downscale_helper_avx2(uint *b, uint *end, const QTextureData &image,
6582 int &fx, int &fy, int fdx, int /*fdy*/);
6583 extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_fast_rotate_helper_avx2(uint *b, uint *end, const QTextureData &image,
6584 int &fx, int &fy, int fdx, int fdy);
6585
6586 bilinearFastTransformHelperARGB32PM[0][SimpleScaleTransform] = fetchTransformedBilinearARGB32PM_simple_scale_helper_avx2;
6587 bilinearFastTransformHelperARGB32PM[0][DownscaleTransform] = fetchTransformedBilinearARGB32PM_downscale_helper_avx2;
6588 bilinearFastTransformHelperARGB32PM[0][FastRotateTransform] = fetchTransformedBilinearARGB32PM_fast_rotate_helper_avx2;
6589
6590 extern void QT_FASTCALL convertARGB32ToARGB32PM_avx2(uint *buffer, int count, const QList<QRgb> *);
6591 extern void QT_FASTCALL convertRGBA8888ToARGB32PM_avx2(uint *buffer, int count, const QList<QRgb> *);
6592 extern const uint *QT_FASTCALL fetchARGB32ToARGB32PM_avx2(uint *buffer, const uchar *src, int index, int count,
6593 const QList<QRgb> *, QDitherInfo *);
6594 extern const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_avx2(uint *buffer, const uchar *src, int index, int count,
6595 const QList<QRgb> *, QDitherInfo *);
6596 qPixelLayouts[QImage::Format_ARGB32].fetchToARGB32PM = fetchARGB32ToARGB32PM_avx2;
6597 qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_avx2;
6598 qPixelLayouts[QImage::Format_RGBA8888].fetchToARGB32PM = fetchRGBA8888ToARGB32PM_avx2;
6599 qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_avx2;
6600
6601 extern const QRgba64 *QT_FASTCALL convertARGB32ToRGBA64PM_avx2(QRgba64 *, const uint *, int, const QList<QRgb> *, QDitherInfo *);
6602 extern const QRgba64 *QT_FASTCALL convertRGBA8888ToRGBA64PM_avx2(QRgba64 *, const uint *, int count, const QList<QRgb> *, QDitherInfo *);
6603 extern const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM_avx2(QRgba64 *, const uchar *, int, int, const QList<QRgb> *, QDitherInfo *);
6604 extern const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM_avx2(QRgba64 *, const uchar *, int, int, const QList<QRgb> *, QDitherInfo *);
6605 extern const QRgba64 *QT_FASTCALL fetchRGBA64ToRGBA64PM_avx2(QRgba64 *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6606 qPixelLayouts[QImage::Format_ARGB32].convertToRGBA64PM = convertARGB32ToRGBA64PM_avx2;
6607 qPixelLayouts[QImage::Format_RGBX8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_avx2;
6608 qPixelLayouts[QImage::Format_ARGB32].fetchToRGBA64PM = fetchARGB32ToRGBA64PM_avx2;
6609 qPixelLayouts[QImage::Format_RGBX8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_avx2;
6610 qPixelLayouts[QImage::Format_RGBA64].fetchToRGBA64PM = fetchRGBA64ToRGBA64PM_avx2;
6611
6612 extern const uint *QT_FASTCALL fetchRGB16FToRGB32_avx2(uint *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6613 extern const uint *QT_FASTCALL fetchRGBA16FToARGB32PM_avx2(uint *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6614 extern const QRgba64 *QT_FASTCALL fetchRGBA16FPMToRGBA64PM_avx2(QRgba64 *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6615 extern const QRgba64 *QT_FASTCALL fetchRGBA16FToRGBA64PM_avx2(QRgba64 *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6616 extern void QT_FASTCALL storeRGB16FFromRGB32_avx2(uchar *dest, const uint *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6617 extern void QT_FASTCALL storeRGBA16FFromARGB32PM_avx2(uchar *dest, const uint *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6618 qPixelLayouts[QImage::Format_RGBX16FPx4].fetchToARGB32PM = fetchRGB16FToRGB32_avx2;
6619 qPixelLayouts[QImage::Format_RGBX16FPx4].fetchToRGBA64PM = fetchRGBA16FPMToRGBA64PM_avx2;
6620 qPixelLayouts[QImage::Format_RGBX16FPx4].storeFromARGB32PM = storeRGB16FFromRGB32_avx2;
6621 qPixelLayouts[QImage::Format_RGBX16FPx4].storeFromRGB32 = storeRGB16FFromRGB32_avx2;
6622 qPixelLayouts[QImage::Format_RGBA16FPx4].fetchToARGB32PM = fetchRGBA16FToARGB32PM_avx2;
6623 qPixelLayouts[QImage::Format_RGBA16FPx4].fetchToRGBA64PM = fetchRGBA16FToRGBA64PM_avx2;
6624 qPixelLayouts[QImage::Format_RGBA16FPx4].storeFromARGB32PM = storeRGBA16FFromARGB32PM_avx2;
6625 qPixelLayouts[QImage::Format_RGBA16FPx4].storeFromRGB32 = storeRGB16FFromRGB32_avx2;
6630#if QT_CONFIG(raster_fp)
6631 extern const QRgbaFloat32 *QT_FASTCALL fetchRGBA16FToRGBA32F_avx2(QRgbaFloat32 *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6632 extern void QT_FASTCALL storeRGBX16FFromRGBA32F_avx2(uchar *dest, const QRgbaFloat32 *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6633 extern void QT_FASTCALL storeRGBA16FFromRGBA32F_avx2(uchar *dest, const QRgbaFloat32 *src, int index, int count, const QList<QRgb> *, QDitherInfo *);
6634 qFetchToRGBA32F[QImage::Format_RGBA16FPx4] = fetchRGBA16FToRGBA32F_avx2;
6635 qStoreFromRGBA32F[QImage::Format_RGBX16FPx4] = storeRGBX16FFromRGBA32F_avx2;
6636 qStoreFromRGBA32F[QImage::Format_RGBA16FPx4] = storeRGBA16FFromRGBA32F_avx2;
6637#endif // QT_CONFIG(raster_fp)
6638 }
6639
6640#endif
6641
6642#endif // SSE2
6643
6644#if defined(__ARM_NEON__)
6645 qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_neon;
6649#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
6650 qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBX8888] = qt_blend_rgb32_on_rgb32_neon;
6654#endif
6655
6656 qt_functionForMode_C[QPainter::CompositionMode_SourceOver] = qt_blend_argb32_on_argb32_scanline_neon;
6657 qt_functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_neon;
6659
6660 extern const uint * QT_FASTCALL qt_fetch_radial_gradient_neon(uint *buffer, const Operator *op, const QSpanData *data,
6661 int y, int x, int length);
6662
6663 qt_fetch_radial_gradient = qt_fetch_radial_gradient_neon;
6664
6665 sourceFetchUntransformed[QImage::Format_RGB888] = qt_fetchUntransformed_888_neon;
6666
6667#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
6668 extern void QT_FASTCALL convertARGB32ToARGB32PM_neon(uint *buffer, int count, const QList<QRgb> *);
6669 extern void QT_FASTCALL convertRGBA8888ToARGB32PM_neon(uint *buffer, int count, const QList<QRgb> *);
6670 extern const uint *QT_FASTCALL fetchARGB32ToARGB32PM_neon(uint *buffer, const uchar *src, int index, int count,
6671 const QList<QRgb> *, QDitherInfo *);
6672 extern const uint *QT_FASTCALL fetchRGBA8888ToARGB32PM_neon(uint *buffer, const uchar *src, int index, int count,
6673 const QList<QRgb> *, QDitherInfo *);
6674 extern const QRgba64 * QT_FASTCALL convertARGB32ToRGBA64PM_neon(QRgba64 *buffer, const uint *src, int count,
6675 const QList<QRgb> *, QDitherInfo *);
6676 extern const QRgba64 * QT_FASTCALL convertRGBA8888ToRGBA64PM_neon(QRgba64 *buffer, const uint *src, int count,
6677 const QList<QRgb> *, QDitherInfo *);
6678 extern const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM_neon(QRgba64 *buffer, const uchar *src, int index, int count,
6679 const QList<QRgb> *, QDitherInfo *);
6680 extern const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM_neon(QRgba64 *buffer, const uchar *src, int index, int count,
6681 const QList<QRgb> *, QDitherInfo *);
6682 extern void QT_FASTCALL storeARGB32FromARGB32PM_neon(uchar *dest, const uint *src, int index, int count,
6683 const QList<QRgb> *, QDitherInfo *);
6684 extern void QT_FASTCALL storeRGBA8888FromARGB32PM_neon(uchar *dest, const uint *src, int index, int count,
6685 const QList<QRgb> *, QDitherInfo *);
6686 extern void QT_FASTCALL storeRGBXFromARGB32PM_neon(uchar *dest, const uint *src, int index, int count,
6687 const QList<QRgb> *, QDitherInfo *);
6688 qPixelLayouts[QImage::Format_ARGB32].fetchToARGB32PM = fetchARGB32ToARGB32PM_neon;
6689 qPixelLayouts[QImage::Format_ARGB32].convertToARGB32PM = convertARGB32ToARGB32PM_neon;
6690 qPixelLayouts[QImage::Format_ARGB32].storeFromARGB32PM = storeARGB32FromARGB32PM_neon;
6691 qPixelLayouts[QImage::Format_ARGB32].fetchToRGBA64PM = fetchARGB32ToRGBA64PM_neon;
6692 qPixelLayouts[QImage::Format_ARGB32].convertToRGBA64PM = convertARGB32ToRGBA64PM_neon;
6693 qPixelLayouts[QImage::Format_RGBA8888].fetchToARGB32PM = fetchRGBA8888ToARGB32PM_neon;
6694 qPixelLayouts[QImage::Format_RGBA8888].convertToARGB32PM = convertRGBA8888ToARGB32PM_neon;
6695 qPixelLayouts[QImage::Format_RGBA8888].storeFromARGB32PM = storeRGBA8888FromARGB32PM_neon;
6696 qPixelLayouts[QImage::Format_RGBA8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_neon;
6697 qPixelLayouts[QImage::Format_RGBA8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_neon;
6698 qPixelLayouts[QImage::Format_RGBX8888].storeFromARGB32PM = storeRGBXFromARGB32PM_neon;
6699 qPixelLayouts[QImage::Format_RGBX8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_neon;
6700 qPixelLayouts[QImage::Format_RGBX8888].convertToRGBA64PM = convertRGBA8888ToRGBA64PM_neon;
6701#endif
6702
6703#if defined(ENABLE_PIXMAN_DRAWHELPERS)
6704 // The RGB16 helpers are using Arm32 assemblythat has not been ported to AArch64
6707 qBlendFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_blend_rgb16_on_rgb16_neon;
6708
6709 qScaleFunctions[QImage::Format_RGB16][QImage::Format_ARGB32_Premultiplied] = qt_scale_image_argb32_on_rgb16_neon;
6710 qScaleFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_scale_image_rgb16_on_rgb16_neon;
6711
6712 qTransformFunctions[QImage::Format_RGB16][QImage::Format_ARGB32_Premultiplied] = qt_transform_image_argb32_on_rgb16_neon;
6713 qTransformFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_transform_image_rgb16_on_rgb16_neon;
6714
6715 qDrawHelper[QImage::Format_RGB16].alphamapBlit = qt_alphamapblit_quint16_neon;
6716
6717 destFetchProc[QImage::Format_RGB16] = qt_destFetchRGB16_neon;
6718 destStoreProc[QImage::Format_RGB16] = qt_destStoreRGB16_neon;
6719
6720 qMemRotateFunctions[QPixelLayout::BPP16][0] = qt_memrotate90_16_neon;
6721 qMemRotateFunctions[QPixelLayout::BPP16][2] = qt_memrotate270_16_neon;
6722#endif
6723#endif // defined(__ARM_NEON__)
6724
6725#if defined(__MIPS_DSP__)
6726 // Composition functions are all DSP r1
6727 qt_functionForMode_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_asm_mips_dsp;
6737
6746
6751
6753
6755
6759
6760#if defined(__MIPS_DSPR2__)
6761 qBlendFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_blend_rgb16_on_rgb16_mips_dspr2;
6762 sourceFetchUntransformed[QImage::Format_RGB16] = qt_fetchUntransformedRGB16_mips_dspr2;
6763#else
6765#endif // defined(__MIPS_DSPR2__)
6766#endif // defined(__MIPS_DSP__)
6767}
6768
6769// Ensure initialization if this object file is linked.
6771
struct capHdr __attribute__
const uint * fetch(int x, int y, int len, bool fetchDest)
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, int v)
static Type fetchSingle(const QGradientData &gradient, qreal v)
static void memfill(Type *buffer, Type fill, int length)
static Type null()
struct QClipData::ClipLine * m_clipLines
static const QColorSpacePrivate * get(const QColorSpace &colorSpace)
The QColorSpace class provides a color space abstraction.
Definition qcolorspace.h:21
bool isValid() const noexcept
Returns true if the color space is valid.
static QColorTransformPrivate * get(const QColorTransform &q)
The QColorTransform class is a transformation between color spaces.
static QGuiApplicationPrivate * instance()
Format
The following image formats are available in Qt.
Definition qimage.h:41
@ Format_Grayscale16
Definition qimage.h:70
@ Format_RGBA8888
Definition qimage.h:59
@ Format_RGB30
Definition qimage.h:63
@ Format_RGB888
Definition qimage.h:55
@ Format_RGBA16FPx4
Definition qimage.h:73
@ Format_RGBA32FPx4_Premultiplied
Definition qimage.h:77
@ Format_RGB32
Definition qimage.h:46
@ Format_Invalid
Definition qimage.h:42
@ Format_RGBX32FPx4
Definition qimage.h:75
@ Format_RGBA64_Premultiplied
Definition qimage.h:69
@ Format_RGB444
Definition qimage.h:56
@ Format_RGBA8888_Premultiplied
Definition qimage.h:60
@ Format_ARGB8565_Premultiplied
Definition qimage.h:50
@ Format_RGBA64
Definition qimage.h:68
@ Format_RGBA32FPx4
Definition qimage.h:76
@ Format_RGBA16FPx4_Premultiplied
Definition qimage.h:74
@ Format_RGBX64
Definition qimage.h:67
@ Format_A2BGR30_Premultiplied
Definition qimage.h:62
@ Format_RGBX16FPx4
Definition qimage.h:72
@ Format_BGR30
Definition qimage.h:61
@ NImageFormats
Definition qimage.h:80
@ Format_ARGB32_Premultiplied
Definition qimage.h:48
@ Format_A2RGB30_Premultiplied
Definition qimage.h:64
@ Format_RGB16
Definition qimage.h:49
@ Format_BGR888
Definition qimage.h:71
@ Format_ARGB32
Definition qimage.h:47
@ Format_RGBX8888
Definition qimage.h:58
CompositionMode
Defines the modes supported for digital image compositing.
Definition qpainter.h:97
@ CompositionMode_Xor
Definition qpainter.h:109
@ CompositionMode_DestinationAtop
Definition qpainter.h:108
@ CompositionMode_SourceOver
Definition qpainter.h:98
@ CompositionMode_DestinationOut
Definition qpainter.h:106
@ CompositionMode_SourceAtop
Definition qpainter.h:107
@ CompositionMode_Plus
Definition qpainter.h:112
@ CompositionMode_DestinationOver
Definition qpainter.h:99
@ CompositionMode_Source
Definition qpainter.h:101
@ CompositionMode_DestinationIn
Definition qpainter.h:104
@ CompositionMode_SourceOut
Definition qpainter.h:105
@ CompositionMode_SourceIn
Definition qpainter.h:103
QPainter::CompositionMode compositionMode
qsizetype bytesPerLine() const
QImage::Format format
uchar * scanLine(int y)
uchar * buffer() const
\inmodule QtCore\reentrant
Definition qrect.h:484
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr QRgba64 unpremultiplied() const
Definition qrgba64.h:130
static constexpr QRgba64 fromRgba64(quint64 c)
Definition qrgba64.h:36
constexpr uint toArgb32() const
Definition qrgba64.h:83
constexpr bool isOpaque() const
Definition qrgba64.h:61
static constexpr QRgba64 fromArgb32(uint rgb)
Definition qrgba64.h:56
constexpr bool isTransparent() const
Definition qrgba64.h:65
constexpr QRgba64 premultiplied() const
Definition qrgba64.h:108
static constexpr QRgbaFloat fromRgba64(quint16 red, quint16 green, quint16 blue, quint16 alpha)
Definition qrgbafloat.h:35
constexpr uint toArgb32() const
Definition qrgbafloat.h:82
qsizetype count(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4833
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
\keyword 16-bit Floating Point Support\inmodule QtCore \inheaderfile QFloat16
Definition qfloat16.h:47
QMap< QString, QString > map
[6]
Combined button and popup list for selecting options.
bool isSupported()
Definition image.cpp:4
void qInitBlendFunctions()
SrcOverTransformFunc qTransformFunctions[QImage::NImageFormats][QImage::NImageFormats]
SrcOverBlendFunc qBlendFunctions[QImage::NImageFormats][QImage::NImageFormats]
SrcOverScaleFunc qScaleFunctions[QImage::NImageFormats][QImage::NImageFormats]
#define rgb(r, g, b)
Definition qcolor.cpp:124
#define Q_FALLTHROUGH()
#define Q_UNLIKELY(x)
#define QT_FASTCALL
CompositionFunctionFP qt_functionForModeFP_C[]
CompositionFunctionSolid64 qt_functionForModeSolid64_C[]
CompositionFunctionSolid qt_functionForModeSolid_C[]
CompositionFunctionSolidFP qt_functionForModeSolidFP_C[]
CompositionFunction qt_functionForMode_C[]
CompositionFunction64 qt_functionForMode64_C[]
Q_CONSTRUCTOR_FUNCTION(qt_apple_check_os_version)
static bool initialize()
Definition qctf.cpp:94
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
constexpr int half_point
void(QT_FASTCALL * BilinearFastTransformHelper)(uint *b, uint *end, const QTextureData &image, int &fx, int &fy, int fdx, int fdy)
static const uint *QT_FASTCALL qt_fetch_linear_gradient(uint *buffer, const Operator *op, const QSpanData *data, int y, int x, int length)
static SourceFetchProc qt_fetch_radial_gradient
uint QT_FASTCALL fetch1Pixel< QPixelLayout::BPP1MSB >(const uchar *src, int index)
static int qRgbAvg(QRgb rgb)
static void qt_rectfill_quint16(QRasterBuffer *rasterBuffer, int x, int y, int width, int height, const QRgba64 &color)
void qt_memfill64(quint64 *dest, quint64 color, qsizetype count)
uint QT_FASTCALL fetch1Pixel< QPixelLayout::BPP24 >(const uchar *src, int index)
static uint interpolate_4_pixels_16(uint tl, uint tr, uint bl, uint br, uint distx, uint disty)
uint QT_FASTCALL fetch1Pixel< QPixelLayout::BPP8 >(const uchar *src, int index)
static uint *QT_FASTCALL destFetchMono(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
static void alphamapblend_quint16(int coverage, quint16 *dest, int x, const quint16 srcColor)
static uint *QT_FASTCALL destFetchARGB32P(uint *, QRasterBuffer *rasterBuffer, int x, int y, int)
static const uint *QT_FASTCALL fetchUntransformed(uint *buffer, const Operator *, const QSpanData *data, int y, int x, int length)
static void QT_FASTCALL intermediate_adder(uint *b, uint *end, const IntermediateBuffer &intermediate, int offset, int &fx, int fdx)
static SourceFetchProc sourceFetchAny32[]
static BilinearFastTransformHelper bilinearFastTransformHelperARGB32PM[2][NFastTransformTypes]
static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Operator *, const QSpanData *data, int y, int x, int length)
static uint qt_gradient_pixel_fixed(const QGradientData *data, int fixed_pos)
static DestStoreProc destStoreProc[]
static const uint *QT_FASTCALL fetchUntransformedRGB16(uint *buffer, const Operator *, const QSpanData *data, int y, int x, int length)
static const BlendType *QT_FASTCALL qt_fetch_linear_gradient_template(BlendType *buffer, const Operator *op, const QSpanData *data, int y, int x, int length)
static void QT_FASTCALL destStoreGray16(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
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)
static const ProcessSpans processTextureSpansRGB16[NBlendTypes]
uint QT_FASTCALL fetch1Pixel< QPixelLayout::BPP32FPx4 >(const uchar *src, int index)
uint QT_FASTCALL fetch1Pixel< QPixelLayout::BPP1LSB >(const uchar *src, int index)
FastTransformTypes
@ UpscaleTransform
@ RotateTransform
@ NFastTransformTypes
@ SimpleScaleTransform
@ FastRotateTransform
@ DownscaleTransform
static const BlendType *QT_FASTCALL qt_fetch_conical_gradient_template(BlendType *buffer, const QSpanData *data, int y, int x, int length)
uint QT_FASTCALL fetch1Pixel< QPixelLayout::BPP32 >(const uchar *src, int index)
static void qt_rectfill_quint64(QRasterBuffer *rasterBuffer, int x, int y, int width, int height, const QRgba64 &color)
TextureBlendType
@ BlendUntransformed
@ BlendTransformed
@ BlendTiled
@ BlendTransformedBilinearTiled
@ NBlendTypes
@ BlendTransformedBilinear
@ BlendTransformedTiled
static void QT_FASTCALL destStoreMonoLsb(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
void qt_memfill32(quint32 *dest, quint32 color, qsizetype count)
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 void QT_FASTCALL fetchTransformedBilinearARGB32PM_fast_rotate_helper(uint *b, uint *end, const QTextureData &image, int &fx, int &fy, int fdx, int fdy)
static TextureBlendType getBlendType(const QSpanData *data)
void fetchTransformedBilinear_pixelBounds< BlendTransformedBilinear >(int, int l1, int l2, int &v1, int &v2)
void qt_memfill16(quint16 *dest, quint16 value, qsizetype count)
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)
uint QT_FASTCALL fetch1Pixel< QPixelLayout::BPP16FPx4 >(const uchar *src, int index)
static void qInitDrawhelperFunctions()
static void blend_untransformed_rgb565(int count, const QT_FT_Span *spans, void *userData)
static SourceFetchProc sourceFetchARGB32PM[]
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 void blend_untransformed_argb(int count, const QT_FT_Span *spans, void *userData)
static const uint *QT_FASTCALL fetchUntransformedARGB32PM(uint *, const Operator *, const QSpanData *data, int y, int x, int)
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_FASTCALL fetchTransformedBilinear_simple_scale_helper(uint *b, uint *end, const QTextureData &image, int &fx, int &fy, int fdx, int)
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)
static DestFetchProc destFetchProc[]
static uint *QT_FASTCALL destFetchUndefined(uint *buffer, QRasterBuffer *, int, int, int)
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_FASTCALL destStoreMono(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
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 SourceFetchProc sourceFetchAny16[]
uint QT_FASTCALL fetch1Pixel< QPixelLayout::BPP16 >(const uchar *src, int index)
static const uint *QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, const Operator *, const QSpanData *data, int y, int x, int length)
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 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 void QT_FASTCALL fetchTransformed_fetcher(T *buffer, const QSpanData *data, int y, int x, int length)
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 const ProcessSpans processTextureSpansGeneric[NBlendTypes]
static const ProcessSpans processTextureSpansARGB32PM[NBlendTypes]
static void rgbBlendPixel(quint32 *dst, int coverage, QRgba64 slinear, const QColorTrcLut *colorProfile)
uint QT_FASTCALL fetch1Pixel< QPixelLayout::BPP64 >(const uchar *src, int index)
static void QT_FASTCALL fetchTransformedBilinear_fetcher(T *buf1, T *buf2, const int len, const QTextureData &image, int fx, int fy, const int fdx, const int fdy)
static void QT_FASTCALL destStore(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
static uint *QT_FASTCALL destFetchMonoLsb(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
static void QT_FASTCALL getRadialGradientValues(RadialGradientValues *v, const QSpanData *data)
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)
static uint *QT_FASTCALL destFetch(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
DrawHelper qDrawHelper[]
static void QT_FASTCALL fetchTransformedBilinearARGB32PM_rotate_helper(uint *b, uint *end, const QTextureData &image, int &fx, int &fy, int fdx, int fdy)
static const uint *QT_FASTCALL qt_fetch_conical_gradient(uint *buffer, const Operator *, const QSpanData *data, int y, int x, int length)
uint(QT_FASTCALL * Fetch1PixelFunc)(const uchar *src, int index)
#define QT_THREAD_PARALLEL_FILLS(function)
const uint *QT_FASTCALL qt_fetch_radial_gradient_plain(uint *buffer, const Operator *op, const QSpanData *data, int y, int x, int length)
static void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_scale_helper(uint *b, uint *end, const QTextureData &image, int &fx, int &fy, int fdx, int)
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)
static SourceFetchProc sourceFetchUntransformed[]
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 const CompositionFunction * functionForMode
static const uint *QT_FASTCALL fetchTransformed(uint *buffer, const Operator *, const QSpanData *data, int y, int x, int length)
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 uint *QT_FASTCALL destFetchRGB16(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
static void QT_FASTCALL destStoreGray8(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
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 QT_FASTCALL fetchTransformedBilinear_slow_fetcher(T *buf1, T *buf2, ushort *distxs, ushort *distys, const int len, const QTextureData &image, qreal &fx, qreal &fy, qreal &fw, const qreal fdx, const qreal fdy, const qreal fdw)
static void alphamapblend_argb32(quint32 *dst, int coverage, QRgba64 srcLinear, quint32 src, const QColorTrcLut *colorProfile)
static void QT_FASTCALL fetchTransformedBilinearARGB32PM_downscale_helper(uint *b, uint *end, const QTextureData &image, int &fx, int &fy, int fdx, int)
static const CompositionFunctionSolid * functionForModeSolid
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 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 qBlendGradient(int count, const QT_FT_Span *spans, void *userData)
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)
void qt_memfill24(quint24 *dest, quint24 color, qsizetype count)
static void QT_FASTCALL destStoreRGB16(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
#define FIXPT_BITS
static void QT_FASTCALL fetchTransformedBilinearARGB32PM_upscale_helper(uint *b, uint *end, const QTextureData &image, int &fx, int &fy, int fdx, int)
static void QT_FASTCALL getLinearGradientValues(LinearGradientValues *v, const QSpanData *data)
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 qBlendTexture(int count, const QT_FT_Span *spans, void *userData)
static uint QT_FASTCALL fetch1Pixel(const uchar *, int)
static const SourceFetchProc sourceFetchGeneric[]
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)
constexpr Fetch1PixelFunc fetch1PixelTable[QPixelLayout::BPPCount]
void QT_FASTCALL comp_func_solid_SourceOver_mips_dsp(uint *dest, int length, uint color, uint const_alpha)
void QT_FASTCALL comp_func_solid_SourceOut_mips_dsp(uint *dest, int length, uint color, uint const_alpha)
const uint *QT_FASTCALL qt_fetchUntransformed_argb8565_premultiplied_mips_dsp(uint *buffer, const Operator *, const QSpanData *data, int y, int x, int length)
void QT_FASTCALL comp_func_solid_DestinationAtop_mips_dsp(uint *dest, int length, uint color, uint const_alpha)
void qt_blend_argb32_on_argb32_mips_dsp(uchar *destPixels, int dbpl, const uchar *srcPixels, int sbpl, int w, int h, int const_alpha)
uint *QT_FASTCALL qt_destFetchARGB32_mips_dsp(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
void QT_FASTCALL comp_func_solid_SourceAtop_mips_dsp(uint *dest, int length, uint color, uint const_alpha)
void QT_FASTCALL comp_func_SourceOut_mips_dsp(uint *dest, const uint *src, int length, uint const_alpha)
void QT_FASTCALL comp_func_DestinationOut_mips_dsp(uint *dest, const uint *src, int length, uint const_alpha)
void QT_FASTCALL comp_func_XOR_mips_dsp(uint *dest, const uint *src, int length, uint const_alpha)
const uint *QT_FASTCALL qt_fetchUntransformed_888_mips_dsp(uint *buffer, const Operator *, const QSpanData *data, int y, int x, int length)
void QT_FASTCALL qt_destStoreARGB32_mips_dsp(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
void qt_blend_rgb16_on_rgb16_mips_dsp(uchar *destPixels, int dbpl, const uchar *srcPixels, int sbpl, int w, int h, int const_alpha)
void QT_FASTCALL comp_func_DestinationAtop_mips_dsp(uint *dest, const uint *src, int length, uint const_alpha)
void QT_FASTCALL comp_func_DestinationIn_mips_dsp(uint *dest, const uint *src, int length, uint const_alpha)
void QT_FASTCALL comp_func_solid_DestinationOver_mips_dsp(uint *dest, int length, uint color, uint const_alpha)
void QT_FASTCALL comp_func_SourceAtop_mips_dsp(uint *dest, const uint *src, int length, uint const_alpha)
void QT_FASTCALL comp_func_solid_XOR_mips_dsp(uint *dest, int length, uint color, uint const_alpha)
void comp_func_Source_mips_dsp(uint *dest, const uint *src, int length, uint const_alpha)
const uint *QT_FASTCALL qt_fetchUntransformed_444_mips_dsp(uint *buffer, const Operator *, const QSpanData *data, int y, int x, int length)
void qt_blend_rgb32_on_rgb32_mips_dsp(uchar *destPixels, int dbpl, const uchar *srcPixels, int sbpl, int w, int h, int const_alpha)
void QT_FASTCALL comp_func_solid_DestinationIn_mips_dsp(uint *dest, int length, uint color, uint const_alpha)
void QT_FASTCALL comp_func_SourceIn_mips_dsp(uint *dest, const uint *src, int length, uint const_alpha)
void QT_FASTCALL comp_func_solid_SourceIn_mips_dsp(uint *dest, int length, uint color, uint const_alpha)
void QT_FASTCALL comp_func_DestinationOver_mips_dsp(uint *dest, const uint *src, int length, uint const_alpha)
void(QT_FASTCALL * CompositionFunction64)(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha)
const QRgba64 *(QT_FASTCALL * SourceFetchProc64)(QRgba64 *buffer, const Operator *o, const QSpanData *data, int y, int x, int length)
void qt_memfill(T *dest, T value, qsizetype count)
void(QT_FASTCALL * CompositionFunctionSolid)(uint *dest, int length, uint color, uint const_alpha)
#define Q_DECL_RESTRICT
void(QT_FASTCALL * CompositionFunction)(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha)
const QRgbaFloat32 *(QT_FASTCALL * SourceFetchProcFP)(QRgbaFloat32 *buffer, const Operator *o, const QSpanData *data, int y, int x, int length)
static constexpr bool hasFastInterpolate4()
static constexpr int BufferSize
QRgba64 *(QT_FASTCALL * DestFetchProc64)(QRgba64 *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
const uint *(QT_FASTCALL * SourceFetchProc)(uint *buffer, const Operator *o, const QSpanData *data, int y, int x, int length)
static constexpr uint qt_div_257(uint x)
static uint INTERPOLATE_PIXEL_256(uint x, uint a, uint y, uint b)
ushort qConvertRgb32To16(uint c)
static void blend_pixel(quint32 &dst, const quint32 src)
static constexpr int qt_div_255(int x)
static uint interpolate_4_pixels(uint tl, uint tr, uint bl, uint br, uint distx, uint disty)
void(QT_FASTCALL * DestStoreProc)(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
QT_FT_SpanFunc ProcessSpans
static QRgba64 interpolate_4_pixels_rgb64(const QRgba64 t[], const QRgba64 b[], uint distx, uint disty)
static uint qt_gradient_pixel(const QGradientData *data, qreal pos)
void(QT_FASTCALL * CompositionFunctionFP)(QRgbaFloat32 *Q_DECL_RESTRICT dest, const QRgbaFloat32 *Q_DECL_RESTRICT src, int length, uint const_alpha)
static uint BYTE_MUL(uint x, uint a)
void(QT_FASTCALL * CompositionFunctionSolid64)(QRgba64 *dest, int length, QRgba64 color, uint const_alpha)
QRgbaFloat32 *(QT_FASTCALL * DestFetchProcFP)(QRgbaFloat32 *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
static uint qt_gradient_clamp(const QGradientData *data, int ipos)
uint *(QT_FASTCALL * DestFetchProc)(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
void(QT_FASTCALL * CompositionFunctionSolidFP)(QRgbaFloat32 *dest, int length, QRgbaFloat32 color, uint const_alpha)
#define GRADIENT_STOPTABLE_SIZE
void(QT_FASTCALL * DestStoreProc64)(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length)
const uint qt_bayer_matrix[16][16]
QRgb qConvertRgb16To32(uint c)
void qt_memfill_template(T *dest, T color, qsizetype count)
static uint BYTE_MUL_RGB16(uint x, uint a)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
constexpr T qFromBigEndian(T source)
Definition qendian.h:174
Q_CORE_EXPORT void qFloatFromFloat16(float *, const qfloat16 *, qsizetype length) noexcept
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:349
qfloat16 qSqrt(qfloat16 f)
Definition qfloat16.h:289
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:327
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
#define M_1_PI
Definition qmath.h:221
auto qAtan2(T1 y, T2 x)
Definition qmath.h:90
int qFloor(T v)
Definition qmath.h:42
MemRotateFunc qMemRotateFunctions[QPixelLayout::BPPCount][3]
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
GLint GLfloat GLfloat GLfloat v2
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * v
[13]
GLint GLint GLint GLint GLint x
[0]
GLenum mode
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint GLfloat GLfloat GLfloat GLfloat y1
GLuint index
[2]
GLboolean r
[2]
GLuint GLuint end
GLuint GLfloat GLfloat GLfloat x1
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat s1
GLenum GLuint GLenum GLsizei length
GLdouble GLdouble GLdouble GLdouble top
GLenum GLenum GLsizei count
GLdouble GLdouble right
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
GLenum src
GLenum GLuint buffer
GLint GLsizei width
GLuint color
[2]
GLenum GLenum dst
GLint GLint bottom
GLuint GLfloat x0
GLfloat angle
GLenum target
GLint GLfloat GLfloat v1
GLuint start
GLenum GLuint GLintptr offset
GLboolean GLboolean g
GLfloat n
GLint GLsizei GLsizei GLenum format
GLint y
GLfloat GLfloat GLfloat GLfloat h
GLdouble s
[6]
Definition qopenglext.h:235
GLbyte GLbyte blue
Definition qopenglext.h:385
GLenum func
Definition qopenglext.h:663
const GLubyte * c
GLfixed GLfixed GLfixed y2
GLint limit
GLfixed GLfixed x2
GLdouble GLdouble t
Definition qopenglext.h:243
GLuint in
GLuint64EXT * result
[6]
GLenum GLsizei len
GLfloat GLfloat GLfloat alpha
Definition qopenglext.h:418
GLbyte green
Definition qopenglext.h:385
GLbyte ty
QPixelLayout qPixelLayouts[]
ConvertAndStorePixelsFunc64 qStoreFromRGBA64PM[]
void(QT_FASTCALL * Convert64Func)(QRgba64 *buffer, int count)
const uint *(QT_FASTCALL * FetchAndConvertPixelsFunc)(uint *buffer, const uchar *src, int index, int count, const QList< QRgb > *clut, QDitherInfo *dither)
void(QT_FASTCALL * Convert64ToFPFunc)(QRgbaFloat32 *buffer, const quint64 *src, int count)
static quint32 ARGB2RGBA(quint32 x)
void(QT_FASTCALL * ConvertAndStorePixelsFunc)(uchar *dest, const uint *src, int index, int count, const QList< QRgb > *clut, QDitherInfo *dither)
static constexpr To convert(const std::array< Mapping, N > &mapping, From Mapping::*from, To Mapping::*to, From value, To defaultValue)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
constexpr bool qIsGray(QRgb rgb)
Definition qrgb.h:42
QT_BEGIN_NAMESPACE typedef unsigned int QRgb
Definition qrgb.h:13
constexpr int qRed(QRgb rgb)
Definition qrgb.h:18
constexpr int qGreen(QRgb rgb)
Definition qrgb.h:21
constexpr int qGray(int r, int g, int b)
Definition qrgb.h:36
constexpr int qBlue(QRgb rgb)
Definition qrgb.h:24
constexpr int qAlpha(QRgb rgb)
Definition qrgb.h:27
static uint toArgb32(QRgba64 rgba64)
Definition qrgba64_p.h:219
static QRgba64 interpolate255(QRgba64 x, uint alpha1, QRgba64 y, uint alpha2)
Definition qrgba64_p.h:115
#define qCpuHasFeature(feature)
Definition qsimd_p.h:387
#define s2
#define s1
#define tr(X)
unsigned int quint32
Definition qtypes.h:50
unsigned char uchar
Definition qtypes.h:32
unsigned short quint16
Definition qtypes.h:48
size_t quintptr
Definition qtypes.h:167
unsigned long long quint64
Definition qtypes.h:61
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
unsigned short ushort
Definition qtypes.h:33
double qreal
Definition qtypes.h:187
unsigned char quint8
Definition qtypes.h:46
QVBoxLayout * layout
QMimeDatabase db
[0]
ba fill(true)
p ry()++
p rx()++
QGraphicsSvgItem * red
AlphamapBlitFunc alphamapBlit
BitmapBlitFunc bitmapBlit
quint32 buffer_rb[BufferSize+2]
LinearGradientValues linear
CompositionFunction64 func64
DestStoreProcFP destStoreFP
CompositionFunctionSolidFP funcSolidFP
CompositionFunctionSolid funcSolid
DestStoreProc64 destStore64
SourceFetchProcFP srcFetchFP
DestStoreProc destStore
SourceFetchProc64 srcFetch64
QPainter::CompositionMode mode
CompositionFunction func
CompositionFunctionFP funcFP
DestFetchProcFP destFetchFP
DestFetchProc64 destFetch64
RadialGradientValues radial
SourceFetchProc srcFetch
CompositionFunctionSolid64 funcSolid64
DestFetchProc destFetch
const QSpanData * data
const Operator & op
ConvertFunc convertToARGB32PM
ConvertAndStorePixelsFunc storeFromARGB32PM
ConvertTo64Func convertToRGBA64PM
FetchAndConvertPixelsFunc64 fetchToRGBA64PM
FetchAndConvertPixelsFunc fetchToARGB32PM
ConvertAndStorePixelsFunc storeFromRGB32
RbSwapFunc rbSwap
QRasterBuffer * rasterBuffer
signed int txop
unsigned char coverage
Definition moc.h:23