1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#ifndef QDRAWHELPER_P_H
41#define QDRAWHELPER_P_H
42
43//
44// W A R N I N G
45// -------------
46//
47// This file is not part of the Qt API. It exists purely as an
48// implementation detail. This header file may change from version to
49// version without notice, or even be removed.
50//
51// We mean it.
52//
53
54#include <QtGui/private/qtguiglobal_p.h>
55#include "QtCore/qmath.h"
56#include "QtGui/qcolor.h"
57#include "QtGui/qpainter.h"
58#include "QtGui/qimage.h"
59#include "QtGui/qrgba64.h"
60#ifndef QT_FT_BEGIN_HEADER
61#define QT_FT_BEGIN_HEADER
62#define QT_FT_END_HEADER
63#endif
64#include "private/qrasterdefs_p.h"
65#include <private/qsimd_p.h>
66
67#include <QtCore/qsharedpointer.h>
68
69QT_BEGIN_NAMESPACE
70
71#if defined(Q_CC_GNU)
72# define Q_DECL_RESTRICT __restrict__
73# if defined(Q_PROCESSOR_X86_32) && defined(Q_CC_GNU) && !defined(Q_CC_CLANG) && !defined(Q_CC_INTEL)
74# define Q_DECL_VECTORCALL __attribute__((sseregparm,regparm(3)))
75# else
76# define Q_DECL_VECTORCALL
77# endif
78#elif defined(Q_CC_MSVC)
79# define Q_DECL_RESTRICT __restrict
80# define Q_DECL_VECTORCALL __vectorcall
81#else
82# define Q_DECL_RESTRICT
83# define Q_DECL_VECTORCALL
84#endif
85
86static const uint AMASK = 0xff000000;
87static const uint RMASK = 0x00ff0000;
88static const uint GMASK = 0x0000ff00;
89static const uint BMASK = 0x000000ff;
90
91/*******************************************************************************
92 * QSpan
93 *
94 * duplicate definition of FT_Span
95 */
96typedef QT_FT_Span QSpan;
97
98struct QSolidData;
99struct QTextureData;
100struct QGradientData;
101struct QLinearGradientData;
102struct QRadialGradientData;
103struct QConicalGradientData;
104struct QSpanData;
105class QGradient;
106class QRasterBuffer;
107class QClipData;
108class QRasterPaintEngineState;
109
110typedef QT_FT_SpanFunc ProcessSpans;
111typedef void (*BitmapBlitFunc)(QRasterBuffer *rasterBuffer,
112 int x, int y, const QRgba64 &color,
113 const uchar *bitmap,
114 int mapWidth, int mapHeight, int mapStride);
115
116typedef void (*AlphamapBlitFunc)(QRasterBuffer *rasterBuffer,
117 int x, int y, const QRgba64 &color,
118 const uchar *bitmap,
119 int mapWidth, int mapHeight, int mapStride,
120 const QClipData *clip, bool useGammaCorrection);
121
122typedef void (*AlphaRGBBlitFunc)(QRasterBuffer *rasterBuffer,
123 int x, int y, const QRgba64 &color,
124 const uint *rgbmask,
125 int mapWidth, int mapHeight, int mapStride,
126 const QClipData *clip, bool useGammaCorrection);
127
128typedef void (*RectFillFunc)(QRasterBuffer *rasterBuffer,
129 int x, int y, int width, int height,
130 const QRgba64 &color);
131
132typedef void (*SrcOverBlendFunc)(uchar *destPixels, int dbpl,
133 const uchar *src, int spbl,
134 int w, int h,
135 int const_alpha);
136
137typedef void (*SrcOverScaleFunc)(uchar *destPixels, int dbpl,
138 const uchar *src, int spbl, int srch,
139 const QRectF &targetRect,
140 const QRectF &sourceRect,
141 const QRect &clipRect,
142 int const_alpha);
143
144typedef void (*SrcOverTransformFunc)(uchar *destPixels, int dbpl,
145 const uchar *src, int spbl,
146 const QRectF &targetRect,
147 const QRectF &sourceRect,
148 const QRect &clipRect,
149 const QTransform &targetRectTransform,
150 int const_alpha);
151
152typedef void (*MemRotateFunc)(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl);
153
154struct DrawHelper {
155 ProcessSpans blendColor;
156 BitmapBlitFunc bitmapBlit;
157 AlphamapBlitFunc alphamapBlit;
158 AlphaRGBBlitFunc alphaRGBBlit;
159 RectFillFunc fillRect;
160};
161
162extern SrcOverBlendFunc qBlendFunctions[QImage::NImageFormats][QImage::NImageFormats];
163extern SrcOverScaleFunc qScaleFunctions[QImage::NImageFormats][QImage::NImageFormats];
164extern SrcOverTransformFunc qTransformFunctions[QImage::NImageFormats][QImage::NImageFormats];
165
166extern DrawHelper qDrawHelper[QImage::NImageFormats];
167
168struct quint24 {
169 quint24() = default;
170 quint24(uint value)
171 {
172 data[0] = uchar(value >> 16);
173 data[1] = uchar(value >> 8);
174 data[2] = uchar(value);
175 }
176 operator uint() const
177 {
178 return data[2] | (data[1] << 8) | (data[0] << 16);
179 }
180
181 uchar data[3];
182};
183
184void qBlendGradient(int count, const QSpan *spans, void *userData);
185void qBlendTexture(int count, const QSpan *spans, void *userData);
186#ifdef __SSE2__
187extern void (*qt_memfill64)(quint64 *dest, quint64 value, qsizetype count);
188extern void (*qt_memfill32)(quint32 *dest, quint32 value, qsizetype count);
189#else
190extern void qt_memfill64(quint64 *dest, quint64 value, qsizetype count);
191extern void qt_memfill32(quint32 *dest, quint32 value, qsizetype count);
192#endif
193extern void qt_memfill24(quint24 *dest, quint24 value, qsizetype count);
194extern void qt_memfill16(quint16 *dest, quint16 value, qsizetype count);
195
196typedef void (QT_FASTCALL *CompositionFunction)(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha);
197typedef void (QT_FASTCALL *CompositionFunction64)(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha);
198typedef void (QT_FASTCALL *CompositionFunctionSolid)(uint *dest, int length, uint color, uint const_alpha);
199typedef void (QT_FASTCALL *CompositionFunctionSolid64)(QRgba64 *dest, int length, QRgba64 color, uint const_alpha);
200
201struct LinearGradientValues
202{
203 qreal dx;
204 qreal dy;
205 qreal l;
206 qreal off;
207};
208
209struct RadialGradientValues
210{
211 qreal dx;
212 qreal dy;
213 qreal dr;
214 qreal sqrfr;
215 qreal a;
216 qreal inv2a;
217 bool extended;
218};
219
220struct Operator;
221typedef uint* (QT_FASTCALL *DestFetchProc)(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length);
222typedef QRgba64* (QT_FASTCALL *DestFetchProc64)(QRgba64 *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length);
223typedef void (QT_FASTCALL *DestStoreProc)(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length);
224typedef void (QT_FASTCALL *DestStoreProc64)(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length);
225typedef const uint* (QT_FASTCALL *SourceFetchProc)(uint *buffer, const Operator *o, const QSpanData *data, int y, int x, int length);
226typedef const QRgba64* (QT_FASTCALL *SourceFetchProc64)(QRgba64 *buffer, const Operator *o, const QSpanData *data, int y, int x, int length);
227
228struct Operator
229{
230 QPainter::CompositionMode mode;
231 DestFetchProc destFetch;
232 DestStoreProc destStore;
233 SourceFetchProc srcFetch;
234 CompositionFunctionSolid funcSolid;
235 CompositionFunction func;
236
237 DestFetchProc64 destFetch64;
238 DestStoreProc64 destStore64;
239 SourceFetchProc64 srcFetch64;
240 CompositionFunctionSolid64 funcSolid64;
241 CompositionFunction64 func64;
242
243 union {
244 LinearGradientValues linear;
245 RadialGradientValues radial;
246 };
247};
248
249class QRasterPaintEngine;
250
251struct QLinearGradientData
252{
253 struct {
254 qreal x;
255 qreal y;
256 } origin;
257 struct {
258 qreal x;
259 qreal y;
260 } end;
261};
262
263struct QRadialGradientData
264{
265 struct {
266 qreal x;
267 qreal y;
268 qreal radius;
269 } center;
270 struct {
271 qreal x;
272 qreal y;
273 qreal radius;
274 } focal;
275};
276
277struct QConicalGradientData
278{
279 struct {
280 qreal x;
281 qreal y;
282 } center;
283 qreal angle;
284};
285
286struct QGradientData
287{
288 QGradient::Spread spread;
289
290 union {
291 QLinearGradientData linear;
292 QRadialGradientData radial;
293 QConicalGradientData conical;
294 };
295
296#define GRADIENT_STOPTABLE_SIZE 1024
297#define GRADIENT_STOPTABLE_SIZE_SHIFT 10
298
299#if QT_CONFIG(raster_64bit)
300 const QRgba64 *colorTable64; //[GRADIENT_STOPTABLE_SIZE];
301#endif
302 const QRgb *colorTable32; //[GRADIENT_STOPTABLE_SIZE];
303
304 uint alphaColor : 1;
305};
306
307struct QTextureData
308{
309 const uchar *imageData;
310 const uchar *scanLine(int y) const { return imageData + y*bytesPerLine; }
311
312 int width;
313 int height;
314 // clip rect
315 int x1;
316 int y1;
317 int x2;
318 int y2;
319 qsizetype bytesPerLine;
320 QImage::Format format;
321 const QVector<QRgb> *colorTable;
322 bool hasAlpha;
323 enum Type {
324 Plain,
325 Tiled
326 };
327 Type type;
328 int const_alpha;
329};
330
331struct QSpanData
332{
333 QSpanData() : tempImage(nullptr) {}
334 ~QSpanData() { delete tempImage; }
335
336 QRasterBuffer *rasterBuffer;
337 ProcessSpans blend;
338 ProcessSpans unclipped_blend;
339 BitmapBlitFunc bitmapBlit;
340 AlphamapBlitFunc alphamapBlit;
341 AlphaRGBBlitFunc alphaRGBBlit;
342 RectFillFunc fillRect;
343 qreal m11, m12, m13, m21, m22, m23, m33, dx, dy; // inverse xform matrix
344 const QClipData *clip;
345 enum Type {
346 None,
347 Solid,
348 LinearGradient,
349 RadialGradient,
350 ConicalGradient,
351 Texture
352 } type : 8;
353 int txop : 8;
354 int fast_matrix : 1;
355 bool bilinear;
356 QImage *tempImage;
357 QRgba64 solidColor;
358 union {
359 QGradientData gradient;
360 QTextureData texture;
361 };
362 class Pinnable {
363 protected:
364 ~Pinnable() {}
365 }; // QSharedPointer<const void> is not supported
366 QSharedPointer<const Pinnable> cachedGradient;
367
368
369 void init(QRasterBuffer *rb, const QRasterPaintEngine *pe);
370 void setup(const QBrush &brush, int alpha, QPainter::CompositionMode compositionMode);
371 void setupMatrix(const QTransform &matrix, int bilinear);
372 void initTexture(const QImage *image, int alpha, QTextureData::Type = QTextureData::Plain, const QRect &sourceRect = QRect());
373 void adjustSpanMethods();
374};
375
376static inline uint qt_gradient_clamp(const QGradientData *data, int ipos)
377{
378 if (ipos < 0 || ipos >= GRADIENT_STOPTABLE_SIZE) {
379 if (data->spread == QGradient::RepeatSpread) {
380 ipos = ipos % GRADIENT_STOPTABLE_SIZE;
381 ipos = ipos < 0 ? GRADIENT_STOPTABLE_SIZE + ipos : ipos;
382 } else if (data->spread == QGradient::ReflectSpread) {
383 const int limit = GRADIENT_STOPTABLE_SIZE * 2;
384 ipos = ipos % limit;
385 ipos = ipos < 0 ? limit + ipos : ipos;
386 ipos = ipos >= GRADIENT_STOPTABLE_SIZE ? limit - 1 - ipos : ipos;
387 } else {
388 if (ipos < 0)
389 ipos = 0;
390 else if (ipos >= GRADIENT_STOPTABLE_SIZE)
391 ipos = GRADIENT_STOPTABLE_SIZE-1;
392 }
393 }
394
395 Q_ASSERT(ipos >= 0);
396 Q_ASSERT(ipos < GRADIENT_STOPTABLE_SIZE);
397
398 return ipos;
399}
400
401static inline uint qt_gradient_pixel(const QGradientData *data, qreal pos)
402{
403 int ipos = int(pos * (GRADIENT_STOPTABLE_SIZE - 1) + qreal(0.5));
404 return data->colorTable32[qt_gradient_clamp(data, ipos)];
405}
406
407#if QT_CONFIG(raster_64bit)
408static inline const QRgba64& qt_gradient_pixel64(const QGradientData *data, qreal pos)
409{
410 int ipos = int(pos * (GRADIENT_STOPTABLE_SIZE - 1) + qreal(0.5));
411 return data->colorTable64[qt_gradient_clamp(data, ipos)];
412}
413#endif
414
415static inline qreal qRadialDeterminant(qreal a, qreal b, qreal c)
416{
417 return (b * b) - (4 * a * c);
418}
419
420template <class RadialFetchFunc, typename BlendType> static
421const BlendType * QT_FASTCALL qt_fetch_radial_gradient_template(BlendType *buffer, const Operator *op,
422 const QSpanData *data, int y, int x, int length)
423{
424 // avoid division by zero
425 if (qFuzzyIsNull(op->radial.a)) {
426 RadialFetchFunc::memfill(buffer, RadialFetchFunc::null(), length);
427 return buffer;
428 }
429
430 const BlendType *b = buffer;
431 qreal rx = data->m21 * (y + qreal(0.5))
432 + data->dx + data->m11 * (x + qreal(0.5));
433 qreal ry = data->m22 * (y + qreal(0.5))
434 + data->dy + data->m12 * (x + qreal(0.5));
435 bool affine = !data->m13 && !data->m23;
436
437 BlendType *end = buffer + length;
438 if (affine) {
439 rx -= data->gradient.radial.focal.x;
440 ry -= data->gradient.radial.focal.y;
441
442 qreal inv_a = 1 / qreal(2 * op->radial.a);
443
444 const qreal delta_rx = data->m11;
445 const qreal delta_ry = data->m12;
446
447 qreal b = 2*(op->radial.dr*data->gradient.radial.focal.radius + rx * op->radial.dx + ry * op->radial.dy);
448 qreal delta_b = 2*(delta_rx * op->radial.dx + delta_ry * op->radial.dy);
449 const qreal b_delta_b = 2 * b * delta_b;
450 const qreal delta_b_delta_b = 2 * delta_b * delta_b;
451
452 const qreal bb = b * b;
453 const qreal delta_bb = delta_b * delta_b;
454
455 b *= inv_a;
456 delta_b *= inv_a;
457
458 const qreal rxrxryry = rx * rx + ry * ry;
459 const qreal delta_rxrxryry = delta_rx * delta_rx + delta_ry * delta_ry;
460 const qreal rx_plus_ry = 2*(rx * delta_rx + ry * delta_ry);
461 const qreal delta_rx_plus_ry = 2 * delta_rxrxryry;
462
463 inv_a *= inv_a;
464
465 qreal det = (bb - 4 * op->radial.a * (op->radial.sqrfr - rxrxryry)) * inv_a;
466 qreal delta_det = (b_delta_b + delta_bb + 4 * op->radial.a * (rx_plus_ry + delta_rxrxryry)) * inv_a;
467 const qreal delta_delta_det = (delta_b_delta_b + 4 * op->radial.a * delta_rx_plus_ry) * inv_a;
468
469 RadialFetchFunc::fetch(buffer, end, op, data, det, delta_det, delta_delta_det, b, delta_b);
470 } else {
471 qreal rw = data->m23 * (y + qreal(0.5))
472 + data->m33 + data->m13 * (x + qreal(0.5));
473
474 while (buffer < end) {
475 if (rw == 0) {
476 *buffer = 0;
477 } else {
478 qreal invRw = 1 / rw;
479 qreal gx = rx * invRw - data->gradient.radial.focal.x;
480 qreal gy = ry * invRw - data->gradient.radial.focal.y;
481 qreal b = 2*(op->radial.dr*data->gradient.radial.focal.radius + gx*op->radial.dx + gy*op->radial.dy);
482 qreal det = qRadialDeterminant(op->radial.a, b, op->radial.sqrfr - (gx*gx + gy*gy));
483
484 BlendType result = RadialFetchFunc::null();
485 if (det >= 0) {
486 qreal detSqrt = qSqrt(det);
487
488 qreal s0 = (-b - detSqrt) * op->radial.inv2a;
489 qreal s1 = (-b + detSqrt) * op->radial.inv2a;
490
491 qreal s = qMax(s0, s1);
492
493 if (data->gradient.radial.focal.radius + op->radial.dr * s >= 0)
494 result = RadialFetchFunc::fetchSingle(data->gradient, s);
495 }
496
497 *buffer = result;
498 }
499
500 rx += data->m11;
501 ry += data->m12;
502 rw += data->m13;
503
504 ++buffer;
505 }
506 }
507
508 return b;
509}
510
511template <class Simd>
512class QRadialFetchSimd
513{
514public:
515 static uint null() { return 0; }
516 static uint fetchSingle(const QGradientData& gradient, qreal v)
517 {
518 return qt_gradient_pixel(&gradient, v);
519 }
520 static void memfill(uint *buffer, uint fill, int length)
521 {
522 qt_memfill32(buffer, fill, length);
523 }
524 static void fetch(uint *buffer, uint *end, const Operator *op, const QSpanData *data, qreal det,
525 qreal delta_det, qreal delta_delta_det, qreal b, qreal delta_b)
526 {
527 typename Simd::Vect_buffer_f det_vec;
528 typename Simd::Vect_buffer_f delta_det4_vec;
529 typename Simd::Vect_buffer_f b_vec;
530
531 for (int i = 0; i < 4; ++i) {
532 det_vec.f[i] = det;
533 delta_det4_vec.f[i] = 4 * delta_det;
534 b_vec.f[i] = b;
535
536 det += delta_det;
537 delta_det += delta_delta_det;
538 b += delta_b;
539 }
540
541 const typename Simd::Float32x4 v_delta_delta_det16 = Simd::v_dup(16 * delta_delta_det);
542 const typename Simd::Float32x4 v_delta_delta_det6 = Simd::v_dup(6 * delta_delta_det);
543 const typename Simd::Float32x4 v_delta_b4 = Simd::v_dup(4 * delta_b);
544
545 const typename Simd::Float32x4 v_r0 = Simd::v_dup(data->gradient.radial.focal.radius);
546 const typename Simd::Float32x4 v_dr = Simd::v_dup(op->radial.dr);
547
548#if defined(__ARM_NEON__)
549 // NEON doesn't have SIMD sqrt, but uses rsqrt instead that can't be taken of 0.
550 const typename Simd::Float32x4 v_min = Simd::v_dup(std::numeric_limits<float>::epsilon());
551#else
552 const typename Simd::Float32x4 v_min = Simd::v_dup(0.0f);
553#endif
554 const typename Simd::Float32x4 v_max = Simd::v_dup(float(GRADIENT_STOPTABLE_SIZE-1));
555 const typename Simd::Float32x4 v_half = Simd::v_dup(0.5f);
556
557 const typename Simd::Int32x4 v_repeat_mask = Simd::v_dup(~(uint(0xffffff) << GRADIENT_STOPTABLE_SIZE_SHIFT));
558 const typename Simd::Int32x4 v_reflect_mask = Simd::v_dup(~(uint(0xffffff) << (GRADIENT_STOPTABLE_SIZE_SHIFT+1)));
559
560 const typename Simd::Int32x4 v_reflect_limit = Simd::v_dup(2 * GRADIENT_STOPTABLE_SIZE - 1);
561
562 const int extended_mask = op->radial.extended ? 0x0 : ~0x0;
563
564#define FETCH_RADIAL_LOOP_PROLOGUE \
565 while (buffer < end) { \
566 typename Simd::Vect_buffer_i v_buffer_mask; \
567 v_buffer_mask.v = Simd::v_greaterOrEqual(det_vec.v, v_min); \
568 const typename Simd::Float32x4 v_index_local = Simd::v_sub(Simd::v_sqrt(Simd::v_max(v_min, det_vec.v)), b_vec.v); \
569 const typename Simd::Float32x4 v_index = Simd::v_add(Simd::v_mul(v_index_local, v_max), v_half); \
570 v_buffer_mask.v = Simd::v_and(v_buffer_mask.v, Simd::v_greaterOrEqual(Simd::v_add(v_r0, Simd::v_mul(v_dr, v_index_local)), v_min)); \
571 typename Simd::Vect_buffer_i index_vec;
572#define FETCH_RADIAL_LOOP_CLAMP_REPEAT \
573 index_vec.v = Simd::v_and(v_repeat_mask, Simd::v_toInt(v_index));
574#define FETCH_RADIAL_LOOP_CLAMP_REFLECT \
575 const typename Simd::Int32x4 v_index_i = Simd::v_and(v_reflect_mask, Simd::v_toInt(v_index)); \
576 const typename Simd::Int32x4 v_index_i_inv = Simd::v_sub(v_reflect_limit, v_index_i); \
577 index_vec.v = Simd::v_min_16(v_index_i, v_index_i_inv);
578#define FETCH_RADIAL_LOOP_CLAMP_PAD \
579 index_vec.v = Simd::v_toInt(Simd::v_min(v_max, Simd::v_max(v_min, v_index)));
580#define FETCH_RADIAL_LOOP_EPILOGUE \
581 det_vec.v = Simd::v_add(Simd::v_add(det_vec.v, delta_det4_vec.v), v_delta_delta_det6); \
582 delta_det4_vec.v = Simd::v_add(delta_det4_vec.v, v_delta_delta_det16); \
583 b_vec.v = Simd::v_add(b_vec.v, v_delta_b4); \
584 for (int i = 0; i < 4; ++i) \
585 *buffer++ = (extended_mask | v_buffer_mask.i[i]) & data->gradient.colorTable32[index_vec.i[i]]; \
586 }
587
588#define FETCH_RADIAL_LOOP(FETCH_RADIAL_LOOP_CLAMP) \
589 FETCH_RADIAL_LOOP_PROLOGUE \
590 FETCH_RADIAL_LOOP_CLAMP \
591 FETCH_RADIAL_LOOP_EPILOGUE
592
593 switch (data->gradient.spread) {
594 case QGradient::RepeatSpread:
595 FETCH_RADIAL_LOOP(FETCH_RADIAL_LOOP_CLAMP_REPEAT)
596 break;
597 case QGradient::ReflectSpread:
598 FETCH_RADIAL_LOOP(FETCH_RADIAL_LOOP_CLAMP_REFLECT)
599 break;
600 case QGradient::PadSpread:
601 FETCH_RADIAL_LOOP(FETCH_RADIAL_LOOP_CLAMP_PAD)
602 break;
603 default:
604 Q_ASSERT(false);
605 }
606 }
607};
608
609static Q_ALWAYS_INLINE uint INTERPOLATE_PIXEL_255(uint x, uint a, uint y, uint b) {
610 uint t = (x & 0xff00ff) * a + (y & 0xff00ff) * b;
611 t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8;
612 t &= 0xff00ff;
613
614 x = ((x >> 8) & 0xff00ff) * a + ((y >> 8) & 0xff00ff) * b;
615 x = (x + ((x >> 8) & 0xff00ff) + 0x800080);
616 x &= 0xff00ff00;
617 x |= t;
618 return x;
619}
620
621#if Q_PROCESSOR_WORDSIZE == 8 // 64-bit versions
622
623static Q_ALWAYS_INLINE uint INTERPOLATE_PIXEL_256(uint x, uint a, uint y, uint b) {
624 quint64 t = (((quint64(x)) | ((quint64(x)) << 24)) & 0x00ff00ff00ff00ff) * a;
625 t += (((quint64(y)) | ((quint64(y)) << 24)) & 0x00ff00ff00ff00ff) * b;
626 t >>= 8;
627 t &= 0x00ff00ff00ff00ff;
628 return (uint(t)) | (uint(t >> 24));
629}
630
631static Q_ALWAYS_INLINE uint BYTE_MUL(uint x, uint a) {
632 quint64 t = (((quint64(x)) | ((quint64(x)) << 24)) & 0x00ff00ff00ff00ff) * a;
633 t = (t + ((t >> 8) & 0xff00ff00ff00ff) + 0x80008000800080) >> 8;
634 t &= 0x00ff00ff00ff00ff;
635 return (uint(t)) | (uint(t >> 24));
636}
637
638#else // 32-bit versions
639
640static Q_ALWAYS_INLINE uint INTERPOLATE_PIXEL_256(uint x, uint a, uint y, uint b) {
641 uint t = (x & 0xff00ff) * a + (y & 0xff00ff) * b;
642 t >>= 8;
643 t &= 0xff00ff;
644
645 x = ((x >> 8) & 0xff00ff) * a + ((y >> 8) & 0xff00ff) * b;
646 x &= 0xff00ff00;
647 x |= t;
648 return x;
649}
650
651static Q_ALWAYS_INLINE uint BYTE_MUL(uint x, uint a) {
652 uint t = (x & 0xff00ff) * a;
653 t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8;
654 t &= 0xff00ff;
655
656 x = ((x >> 8) & 0xff00ff) * a;
657 x = (x + ((x >> 8) & 0xff00ff) + 0x800080);
658 x &= 0xff00ff00;
659 x |= t;
660 return x;
661}
662#endif
663
664static Q_ALWAYS_INLINE void blend_pixel(quint32 &dst, const quint32 src)
665{
666 if (src >= 0xff000000)
667 dst = src;
668 else if (src != 0)
669 dst = src + BYTE_MUL(dst, qAlpha(~src));
670}
671
672static Q_ALWAYS_INLINE void blend_pixel(quint32 &dst, const quint32 src, const int const_alpha)
673{
674 if (src != 0) {
675 const quint32 s = BYTE_MUL(src, const_alpha);
676 dst = s + BYTE_MUL(dst, qAlpha(~s));
677 }
678}
679
680#if defined(__SSE2__)
681static Q_ALWAYS_INLINE uint interpolate_4_pixels_sse2(__m128i vt, __m128i vb, uint distx, uint disty)
682{
683 // First interpolate top and bottom pixels in parallel.
684 vt = _mm_unpacklo_epi8(vt, _mm_setzero_si128());
685 vb = _mm_unpacklo_epi8(vb, _mm_setzero_si128());
686 vt = _mm_mullo_epi16(vt, _mm_set1_epi16(256 - disty));
687 vb = _mm_mullo_epi16(vb, _mm_set1_epi16(disty));
688 __m128i vlr = _mm_add_epi16(vt, vb);
689 vlr = _mm_srli_epi16(vlr, 8);
690 // vlr now contains the result of the first two interpolate calls vlr = unpacked((xright << 64) | xleft)
691
692 // Now the last interpolate between left and right..
693 const __m128i vidistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(256 - distx), _MM_SHUFFLE(0, 0, 0, 0));
694 const __m128i vdistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(distx), _MM_SHUFFLE(0, 0, 0, 0));
695 const __m128i vmulx = _mm_unpacklo_epi16(vidistx, vdistx);
696 vlr = _mm_unpacklo_epi16(vlr, _mm_srli_si128(vlr, 8));
697 // vlr now contains the colors of left and right interleaved { la, ra, lr, rr, lg, rg, lb, rb }
698 vlr = _mm_madd_epi16(vlr, vmulx); // Multiply and horizontal add.
699 vlr = _mm_srli_epi32(vlr, 8);
700 vlr = _mm_packs_epi32(vlr, vlr);
701 vlr = _mm_packus_epi16(vlr, vlr);
702 return _mm_cvtsi128_si32(vlr);
703}
704
705static inline uint interpolate_4_pixels(uint tl, uint tr, uint bl, uint br, uint distx, uint disty)
706{
707 __m128i vt = _mm_unpacklo_epi32(_mm_cvtsi32_si128(tl), _mm_cvtsi32_si128(tr));
708 __m128i vb = _mm_unpacklo_epi32(_mm_cvtsi32_si128(bl), _mm_cvtsi32_si128(br));
709 return interpolate_4_pixels_sse2(vt, vb, distx, disty);
710}
711
712static inline uint interpolate_4_pixels(const uint t[], const uint b[], uint distx, uint disty)
713{
714 __m128i vt = _mm_loadl_epi64((const __m128i*)t);
715 __m128i vb = _mm_loadl_epi64((const __m128i*)b);
716 return interpolate_4_pixels_sse2(vt, vb, distx, disty);
717}
718
719static constexpr inline bool hasFastInterpolate4() { return true; }
720
721#elif defined(__ARM_NEON__)
722static Q_ALWAYS_INLINE uint interpolate_4_pixels_neon(uint32x2_t vt32, uint32x2_t vb32, uint distx, uint disty)
723{
724 uint16x8_t vt16 = vmovl_u8(vreinterpret_u8_u32(vt32));
725 uint16x8_t vb16 = vmovl_u8(vreinterpret_u8_u32(vb32));
726 vt16 = vmulq_n_u16(vt16, 256 - disty);
727 vt16 = vmlaq_n_u16(vt16, vb16, disty);
728 vt16 = vshrq_n_u16(vt16, 8);
729 uint16x4_t vl16 = vget_low_u16(vt16);
730 uint16x4_t vr16 = vget_high_u16(vt16);
731 vl16 = vmul_n_u16(vl16, 256 - distx);
732 vl16 = vmla_n_u16(vl16, vr16, distx);
733 vl16 = vshr_n_u16(vl16, 8);
734 uint8x8_t vr = vmovn_u16(vcombine_u16(vl16, vl16));
735 return vget_lane_u32(vreinterpret_u32_u8(vr), 0);
736}
737
738static inline uint interpolate_4_pixels(uint tl, uint tr, uint bl, uint br, uint distx, uint disty)
739{
740 uint32x2_t vt32 = vmov_n_u32(tl);
741 uint32x2_t vb32 = vmov_n_u32(bl);
742 vt32 = vset_lane_u32(tr, vt32, 1);
743 vb32 = vset_lane_u32(br, vb32, 1);
744 return interpolate_4_pixels_neon(vt32, vb32, distx, disty);
745}
746
747static inline uint interpolate_4_pixels(const uint t[], const uint b[], uint distx, uint disty)
748{
749 uint32x2_t vt32 = vld1_u32(t);
750 uint32x2_t vb32 = vld1_u32(b);
751 return interpolate_4_pixels_neon(vt32, vb32, distx, disty);
752}
753
754static constexpr inline bool hasFastInterpolate4() { return true; }
755
756#else
757static inline uint interpolate_4_pixels(uint tl, uint tr, uint bl, uint br, uint distx, uint disty)
758{
759 uint idistx = 256 - distx;
760 uint idisty = 256 - disty;
761 uint xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx);
762 uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx);
763 return INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty);
764}
765
766static inline uint interpolate_4_pixels(const uint t[], const uint b[], uint distx, uint disty)
767{
768 return interpolate_4_pixels(t[0], t[1], b[0], b[1], distx, disty);
769}
770
771static constexpr inline bool hasFastInterpolate4() { return false; }
772
773#endif
774
775static inline QRgba64 multiplyAlpha256(QRgba64 rgba64, uint alpha256)
776{
777 return QRgba64::fromRgba64((rgba64.red() * alpha256) >> 8,
778 (rgba64.green() * alpha256) >> 8,
779 (rgba64.blue() * alpha256) >> 8,
780 (rgba64.alpha() * alpha256) >> 8);
781}
782static inline QRgba64 interpolate256(QRgba64 x, uint alpha1, QRgba64 y, uint alpha2)
783{
784 return QRgba64::fromRgba64(multiplyAlpha256(x, alpha1) + multiplyAlpha256(y, alpha2));
785}
786
787#ifdef __SSE2__
788static inline QRgba64 interpolate_4_pixels_rgb64(const QRgba64 t[], const QRgba64 b[], uint distx, uint disty)
789{
790 __m128i vt = _mm_loadu_si128((const __m128i*)t);
791 if (disty) {
792 __m128i vb = _mm_loadu_si128((const __m128i*)b);
793 vt = _mm_mulhi_epu16(vt, _mm_set1_epi16(0x10000 - disty));
794 vb = _mm_mulhi_epu16(vb, _mm_set1_epi16(disty));
795 vt = _mm_add_epi16(vt, vb);
796 }
797 if (distx) {
798 const __m128i vdistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(distx), _MM_SHUFFLE(0, 0, 0, 0));
799 const __m128i vidistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(0x10000 - distx), _MM_SHUFFLE(0, 0, 0, 0));
800 vt = _mm_mulhi_epu16(vt, _mm_unpacklo_epi64(vidistx, vdistx));
801 vt = _mm_add_epi16(vt, _mm_srli_si128(vt, 8));
802 }
803#ifdef Q_PROCESSOR_X86_64
804 return QRgba64::fromRgba64(_mm_cvtsi128_si64(vt));
805#else
806 QRgba64 out;
807 _mm_storel_epi64((__m128i*)&out, vt);
808 return out;
809#endif // Q_PROCESSOR_X86_64
810}
811#elif defined(__ARM_NEON__)
812static inline QRgba64 interpolate_4_pixels_rgb64(const QRgba64 t[], const QRgba64 b[], uint distx, uint disty)
813{
814 uint64x1x2_t vt = vld2_u64(reinterpret_cast<const uint64_t *>(t));
815 if (disty) {
816 uint64x1x2_t vb = vld2_u64(reinterpret_cast<const uint64_t *>(b));
817 uint32x4_t vt0 = vmull_n_u16(vreinterpret_u16_u64(vt.val[0]), 0x10000 - disty);
818 uint32x4_t vt1 = vmull_n_u16(vreinterpret_u16_u64(vt.val[1]), 0x10000 - disty);
819 vt0 = vmlal_n_u16(vt0, vreinterpret_u16_u64(vb.val[0]), disty);
820 vt1 = vmlal_n_u16(vt1, vreinterpret_u16_u64(vb.val[1]), disty);
821 vt.val[0] = vreinterpret_u64_u16(vshrn_n_u32(vt0, 16));
822 vt.val[1] = vreinterpret_u64_u16(vshrn_n_u32(vt1, 16));
823 }
824 if (distx) {
825 uint32x4_t vt0 = vmull_n_u16(vreinterpret_u16_u64(vt.val[0]), 0x10000 - distx);
826 vt0 = vmlal_n_u16(vt0, vreinterpret_u16_u64(vt.val[1]), distx);
827 vt.val[0] = vreinterpret_u64_u16(vshrn_n_u32(vt0, 16));
828 }
829 QRgba64 out;
830 vst1_u64(reinterpret_cast<uint64_t *>(&out), vt.val[0]);
831 return out;
832}
833#else
834static inline QRgba64 interpolate_4_pixels_rgb64(const QRgba64 t[], const QRgba64 b[], uint distx, uint disty)
835{
836 const uint dx = distx>>8;
837 const uint dy = disty>>8;
838 const uint idx = 256 - dx;
839 const uint idy = 256 - dy;
840 QRgba64 xtop = interpolate256(t[0], idx, t[1], dx);
841 QRgba64 xbot = interpolate256(b[0], idx, b[1], dx);
842 return interpolate256(xtop, idy, xbot, dy);
843}
844#endif // __SSE2__
845
846#if Q_BYTE_ORDER == Q_BIG_ENDIAN
847static Q_ALWAYS_INLINE quint32 RGBA2ARGB(quint32 x) {
848 quint32 rgb = x >> 8;
849 quint32 a = x << 24;
850 return a | rgb;
851}
852
853static Q_ALWAYS_INLINE quint32 ARGB2RGBA(quint32 x) {
854 quint32 rgb = x << 8;
855 quint32 a = x >> 24;
856 return a | rgb;
857}
858#else
859static Q_ALWAYS_INLINE quint32 RGBA2ARGB(quint32 x) {
860 // RGBA8888 is ABGR32 on little endian.
861 quint32 ag = x & 0xff00ff00;
862 quint32 rg = x & 0x00ff00ff;
863 return ag | (rg << 16) | (rg >> 16);
864}
865
866static Q_ALWAYS_INLINE quint32 ARGB2RGBA(quint32 x) {
867 return RGBA2ARGB(x);
868}
869#endif
870
871static Q_ALWAYS_INLINE uint BYTE_MUL_RGB16(uint x, uint a) {
872 a += 1;
873 uint t = (((x & 0x07e0)*a) >> 8) & 0x07e0;
874 t |= (((x & 0xf81f)*(a>>2)) >> 6) & 0xf81f;
875 return t;
876}
877
878static Q_ALWAYS_INLINE uint BYTE_MUL_RGB16_32(uint x, uint a) {
879 uint t = (((x & 0xf81f07e0) >> 5)*a) & 0xf81f07e0;
880 t |= (((x & 0x07e0f81f)*a) >> 5) & 0x07e0f81f;
881 return t;
882}
883
884// qt_div_255 is a fast rounded division by 255 using an approximation that is accurate for all positive 16-bit integers
885static Q_DECL_CONSTEXPR Q_ALWAYS_INLINE int qt_div_255(int x) { return (x + (x>>8) + 0x80) >> 8; }
886static Q_DECL_CONSTEXPR Q_ALWAYS_INLINE uint qt_div_257_floor(uint x) { return (x - (x >> 8)) >> 8; }
887static Q_DECL_CONSTEXPR Q_ALWAYS_INLINE uint qt_div_257(uint x) { return qt_div_257_floor(x + 128); }
888static Q_DECL_CONSTEXPR Q_ALWAYS_INLINE uint qt_div_65535(uint x) { return (x + (x>>16) + 0x8000U) >> 16; }
889
890static Q_ALWAYS_INLINE uint qAlphaRgb30(uint c)
891{
892 uint a = c >> 30;
893 a |= a << 2;
894 a |= a << 4;
895 return a;
896}
897
898template <class T> inline void qt_memfill_template(T *dest, T color, qsizetype count)
899{
900 if (!count)
901 return;
902
903 qsizetype n = (count + 7) / 8;
904 switch (count & 0x07)
905 {
906 case 0: do { *dest++ = color; Q_FALLTHROUGH();
907 case 7: *dest++ = color; Q_FALLTHROUGH();
908 case 6: *dest++ = color; Q_FALLTHROUGH();
909 case 5: *dest++ = color; Q_FALLTHROUGH();
910 case 4: *dest++ = color; Q_FALLTHROUGH();
911 case 3: *dest++ = color; Q_FALLTHROUGH();
912 case 2: *dest++ = color; Q_FALLTHROUGH();
913 case 1: *dest++ = color;
914 } while (--n > 0);
915 }
916}
917
918template <class T> inline void qt_memfill(T *dest, T value, qsizetype count)
919{
920 qt_memfill_template(dest, value, count);
921}
922
923template<> inline void qt_memfill(quint64 *dest, quint64 color, qsizetype count)
924{
925 qt_memfill64(dest, color, count);
926}
927
928template<> inline void qt_memfill(quint32 *dest, quint32 color, qsizetype count)
929{
930 qt_memfill32(dest, color, count);
931}
932
933template<> inline void qt_memfill(quint24 *dest, quint24 color, qsizetype count)
934{
935 qt_memfill24(dest, color, count);
936}
937
938template<> inline void qt_memfill(quint16 *dest, quint16 color, qsizetype count)
939{
940 qt_memfill16(dest, color, count);
941}
942
943template<> inline void qt_memfill(quint8 *dest, quint8 color, qsizetype count)
944{
945 memset(dest, color, count);
946}
947
948template <class T> static
949inline void qt_rectfill(T *dest, T value,
950 int x, int y, int width, int height, qsizetype stride)
951{
952 char *d = reinterpret_cast<char*>(dest + x) + y * stride;
953 if (uint(stride) == (width * sizeof(T))) {
954 qt_memfill(reinterpret_cast<T*>(d), value, qsizetype(width) * height);
955 } else {
956 for (int j = 0; j < height; ++j) {
957 dest = reinterpret_cast<T*>(d);
958 qt_memfill(dest, value, width);
959 d += stride;
960 }
961 }
962}
963
964inline ushort qConvertRgb32To16(uint c)
965{
966 return (((c) >> 3) & 0x001f)
967 | (((c) >> 5) & 0x07e0)
968 | (((c) >> 8) & 0xf800);
969}
970
971inline QRgb qConvertRgb16To32(uint c)
972{
973 return 0xff000000
974 | ((((c) << 3) & 0xf8) | (((c) >> 2) & 0x7))
975 | ((((c) << 5) & 0xfc00) | (((c) >> 1) & 0x300))
976 | ((((c) << 8) & 0xf80000) | (((c) << 3) & 0x70000));
977}
978
979enum QtPixelOrder {
980 PixelOrderRGB,
981 PixelOrderBGR
982};
983
984template<enum QtPixelOrder> inline uint qConvertArgb32ToA2rgb30(QRgb);
985
986template<enum QtPixelOrder> inline uint qConvertRgb32ToRgb30(QRgb);
987
988template<enum QtPixelOrder> inline QRgb qConvertA2rgb30ToArgb32(uint c);
989
990// A combined unpremultiply and premultiply with new simplified alpha.
991// Needed when alpha loses precision relative to other colors during conversion (ARGB32 -> A2RGB30).
992template<unsigned int Shift>
993inline QRgb qRepremultiply(QRgb p)
994{
995 const uint alpha = qAlpha(p);
996 if (alpha == 255 || alpha == 0)
997 return p;
998 p = qUnpremultiply(p);
999 Q_CONSTEXPR uint mult = 255 / (255 >> Shift);
1000 const uint newAlpha = mult * (alpha >> Shift);
1001 p = (p & ~0xff000000) | (newAlpha<<24);
1002 return qPremultiply(p);
1003}
1004
1005template<unsigned int Shift>
1006inline QRgba64 qRepremultiply(QRgba64 p)
1007{
1008 const uint alpha = p.alpha();
1009 if (alpha == 65535 || alpha == 0)
1010 return p;
1011 p = p.unpremultiplied();
1012 Q_CONSTEXPR uint mult = 65535 / (65535 >> Shift);
1013 p.setAlpha(mult * (alpha >> Shift));
1014 return p.premultiplied();
1015}
1016
1017template<>
1018inline uint qConvertArgb32ToA2rgb30<PixelOrderBGR>(QRgb c)
1019{
1020 c = qRepremultiply<6>(c);
1021 return (c & 0xc0000000)
1022 | (((c << 22) & 0x3fc00000) | ((c << 14) & 0x00300000))
1023 | (((c << 4) & 0x000ff000) | ((c >> 4) & 0x00000c00))
1024 | (((c >> 14) & 0x000003fc) | ((c >> 22) & 0x00000003));
1025}
1026
1027template<>
1028inline uint qConvertArgb32ToA2rgb30<PixelOrderRGB>(QRgb c)
1029{
1030 c = qRepremultiply<6>(c);
1031 return (c & 0xc0000000)
1032 | (((c << 6) & 0x3fc00000) | ((c >> 2) & 0x00300000))
1033 | (((c << 4) & 0x000ff000) | ((c >> 4) & 0x00000c00))
1034 | (((c << 2) & 0x000003fc) | ((c >> 6) & 0x00000003));
1035}
1036
1037template<>
1038inline uint qConvertRgb32ToRgb30<PixelOrderBGR>(QRgb c)
1039{
1040 return 0xc0000000
1041 | (((c << 22) & 0x3fc00000) | ((c << 14) & 0x00300000))
1042 | (((c << 4) & 0x000ff000) | ((c >> 4) & 0x00000c00))
1043 | (((c >> 14) & 0x000003fc) | ((c >> 22) & 0x00000003));
1044}
1045
1046template<>
1047inline uint qConvertRgb32ToRgb30<PixelOrderRGB>(QRgb c)
1048{
1049 return 0xc0000000
1050 | (((c << 6) & 0x3fc00000) | ((c >> 2) & 0x00300000))
1051 | (((c << 4) & 0x000ff000) | ((c >> 4) & 0x00000c00))
1052 | (((c << 2) & 0x000003fc) | ((c >> 6) & 0x00000003));
1053}
1054
1055template<>
1056inline QRgb qConvertA2rgb30ToArgb32<PixelOrderBGR>(uint c)
1057{
1058 uint a = c >> 30;
1059 a |= a << 2;
1060 a |= a << 4;
1061 return (a << 24)
1062 | ((c << 14) & 0x00ff0000)
1063 | ((c >> 4) & 0x0000ff00)
1064 | ((c >> 22) & 0x000000ff);
1065}
1066
1067template<>
1068inline QRgb qConvertA2rgb30ToArgb32<PixelOrderRGB>(uint c)
1069{
1070 uint a = c >> 30;
1071 a |= a << 2;
1072 a |= a << 4;
1073 return (a << 24)
1074 | ((c >> 6) & 0x00ff0000)
1075 | ((c >> 4) & 0x0000ff00)
1076 | ((c >> 2) & 0x000000ff);
1077}
1078
1079template<enum QtPixelOrder> inline QRgba64 qConvertA2rgb30ToRgb64(uint rgb);
1080
1081template<>
1082inline QRgba64 qConvertA2rgb30ToRgb64<PixelOrderBGR>(uint rgb)
1083{
1084 quint16 alpha = rgb >> 30;
1085 quint16 blue = (rgb >> 20) & 0x3ff;
1086 quint16 green = (rgb >> 10) & 0x3ff;
1087 quint16 red = rgb & 0x3ff;
1088 // Expand the range.
1089 alpha |= (alpha << 2);
1090 alpha |= (alpha << 4);
1091 alpha |= (alpha << 8);
1092 red = (red << 6) | (red >> 4);
1093 green = (green << 6) | (green >> 4);
1094 blue = (blue << 6) | (blue >> 4);
1095 return qRgba64(red, green, blue, alpha);
1096}
1097
1098template<>
1099inline QRgba64 qConvertA2rgb30ToRgb64<PixelOrderRGB>(uint rgb)
1100{
1101 quint16 alpha = rgb >> 30;
1102 quint16 red = (rgb >> 20) & 0x3ff;
1103 quint16 green = (rgb >> 10) & 0x3ff;
1104 quint16 blue = rgb & 0x3ff;
1105 // Expand the range.
1106 alpha |= (alpha << 2);
1107 alpha |= (alpha << 4);
1108 alpha |= (alpha << 8);
1109 red = (red << 6) | (red >> 4);
1110 green = (green << 6) | (green >> 4);
1111 blue = (blue << 6) | (blue >> 4);
1112 return qRgba64(red, green, blue, alpha);
1113}
1114
1115template<enum QtPixelOrder> inline unsigned int qConvertRgb64ToRgb30(QRgba64);
1116
1117template<>
1118inline unsigned int qConvertRgb64ToRgb30<PixelOrderBGR>(QRgba64 c)
1119{
1120 c = qRepremultiply<14>(c);
1121 const uint a = c.alpha() >> 14;
1122 const uint r = c.red() >> 6;
1123 const uint g = c.green() >> 6;
1124 const uint b = c.blue() >> 6;
1125 return (a << 30) | (b << 20) | (g << 10) | r;
1126}
1127
1128template<>
1129inline unsigned int qConvertRgb64ToRgb30<PixelOrderRGB>(QRgba64 c)
1130{
1131 c = qRepremultiply<14>(c);
1132 const uint a = c.alpha() >> 14;
1133 const uint r = c.red() >> 6;
1134 const uint g = c.green() >> 6;
1135 const uint b = c.blue() >> 6;
1136 return (a << 30) | (r << 20) | (g << 10) | b;
1137}
1138
1139inline uint qRgbSwapRgb30(uint c)
1140{
1141 const uint ag = c & 0xc00ffc00;
1142 const uint rb = c & 0x3ff003ff;
1143 return ag | (rb << 20) | (rb >> 20);
1144}
1145
1146inline int qRed565(quint16 rgb) {
1147 const int r = (rgb & 0xf800);
1148 return (r >> 8) | (r >> 13);
1149}
1150
1151inline int qGreen565(quint16 rgb) {
1152 const int g = (rgb & 0x07e0);
1153 return (g >> 3) | (g >> 9);
1154}
1155
1156inline int qBlue565(quint16 rgb) {
1157 const int b = (rgb & 0x001f);
1158 return (b << 3) | (b >> 2);
1159}
1160
1161// We manually unalias the variables to make sure the compiler
1162// fully optimizes both aliased and unaliased cases.
1163#define UNALIASED_CONVERSION_LOOP(buffer, src, count, conversion) \
1164 if (src == buffer) { \
1165 for (int i = 0; i < count; ++i) \
1166 buffer[i] = conversion(buffer[i]); \
1167 } else { \
1168 for (int i = 0; i < count; ++i) \
1169 buffer[i] = conversion(src[i]); \
1170 }
1171
1172
1173static Q_ALWAYS_INLINE const uint *qt_convertARGB32ToARGB32PM(uint *buffer, const uint *src, int count)
1174{
1175 UNALIASED_CONVERSION_LOOP(buffer, src, count, qPremultiply);
1176 return buffer;
1177}
1178
1179static Q_ALWAYS_INLINE const uint *qt_convertRGBA8888ToARGB32PM(uint *buffer, const uint *src, int count)
1180{
1181 UNALIASED_CONVERSION_LOOP(buffer, src, count, [](uint s) { return qPremultiply(RGBA2ARGB(s));});
1182 return buffer;
1183}
1184
1185template<bool RGBA> void qt_convertRGBA64ToARGB32(uint *dst, const QRgba64 *src, int count);
1186
1187const uint qt_bayer_matrix[16][16] = {
1188 { 0x1, 0xc0, 0x30, 0xf0, 0xc, 0xcc, 0x3c, 0xfc,
1189 0x3, 0xc3, 0x33, 0xf3, 0xf, 0xcf, 0x3f, 0xff},
1190 { 0x80, 0x40, 0xb0, 0x70, 0x8c, 0x4c, 0xbc, 0x7c,
1191 0x83, 0x43, 0xb3, 0x73, 0x8f, 0x4f, 0xbf, 0x7f},
1192 { 0x20, 0xe0, 0x10, 0xd0, 0x2c, 0xec, 0x1c, 0xdc,
1193 0x23, 0xe3, 0x13, 0xd3, 0x2f, 0xef, 0x1f, 0xdf},
1194 { 0xa0, 0x60, 0x90, 0x50, 0xac, 0x6c, 0x9c, 0x5c,
1195 0xa3, 0x63, 0x93, 0x53, 0xaf, 0x6f, 0x9f, 0x5f},
1196 { 0x8, 0xc8, 0x38, 0xf8, 0x4, 0xc4, 0x34, 0xf4,
1197 0xb, 0xcb, 0x3b, 0xfb, 0x7, 0xc7, 0x37, 0xf7},
1198 { 0x88, 0x48, 0xb8, 0x78, 0x84, 0x44, 0xb4, 0x74,
1199 0x8b, 0x4b, 0xbb, 0x7b, 0x87, 0x47, 0xb7, 0x77},
1200 { 0x28, 0xe8, 0x18, 0xd8, 0x24, 0xe4, 0x14, 0xd4,
1201 0x2b, 0xeb, 0x1b, 0xdb, 0x27, 0xe7, 0x17, 0xd7},
1202 { 0xa8, 0x68, 0x98, 0x58, 0xa4, 0x64, 0x94, 0x54,
1203 0xab, 0x6b, 0x9b, 0x5b, 0xa7, 0x67, 0x97, 0x57},
1204 { 0x2, 0xc2, 0x32, 0xf2, 0xe, 0xce, 0x3e, 0xfe,
1205 0x1, 0xc1, 0x31, 0xf1, 0xd, 0xcd, 0x3d, 0xfd},
1206 { 0x82, 0x42, 0xb2, 0x72, 0x8e, 0x4e, 0xbe, 0x7e,
1207 0x81, 0x41, 0xb1, 0x71, 0x8d, 0x4d, 0xbd, 0x7d},
1208 { 0x22, 0xe2, 0x12, 0xd2, 0x2e, 0xee, 0x1e, 0xde,
1209 0x21, 0xe1, 0x11, 0xd1, 0x2d, 0xed, 0x1d, 0xdd},
1210 { 0xa2, 0x62, 0x92, 0x52, 0xae, 0x6e, 0x9e, 0x5e,
1211 0xa1, 0x61, 0x91, 0x51, 0xad, 0x6d, 0x9d, 0x5d},
1212 { 0xa, 0xca, 0x3a, 0xfa, 0x6, 0xc6, 0x36, 0xf6,
1213 0x9, 0xc9, 0x39, 0xf9, 0x5, 0xc5, 0x35, 0xf5},
1214 { 0x8a, 0x4a, 0xba, 0x7a, 0x86, 0x46, 0xb6, 0x76,
1215 0x89, 0x49, 0xb9, 0x79, 0x85, 0x45, 0xb5, 0x75},
1216 { 0x2a, 0xea, 0x1a, 0xda, 0x26, 0xe6, 0x16, 0xd6,
1217 0x29, 0xe9, 0x19, 0xd9, 0x25, 0xe5, 0x15, 0xd5},
1218 { 0xaa, 0x6a, 0x9a, 0x5a, 0xa6, 0x66, 0x96, 0x56,
1219 0xa9, 0x69, 0x99, 0x59, 0xa5, 0x65, 0x95, 0x55}
1220};
1221
1222#define ARGB_COMBINE_ALPHA(argb, alpha) \
1223 ((((argb >> 24) * alpha) >> 8) << 24) | (argb & 0x00ffffff)
1224
1225
1226#if Q_PROCESSOR_WORDSIZE == 8 // 64-bit versions
1227#define AMIX(mask) (qMin(((quint64(s)&mask) + (quint64(d)&mask)), quint64(mask)))
1228#define MIX(mask) (qMin(((quint64(s)&mask) + (quint64(d)&mask)), quint64(mask)))
1229#else // 32 bits
1230// The mask for alpha can overflow over 32 bits
1231#define AMIX(mask) quint32(qMin(((quint64(s)&mask) + (quint64(d)&mask)), quint64(mask)))
1232#define MIX(mask) (qMin(((quint32(s)&mask) + (quint32(d)&mask)), quint32(mask)))
1233#endif
1234
1235inline uint comp_func_Plus_one_pixel_const_alpha(uint d, const uint s, const uint const_alpha, const uint one_minus_const_alpha)
1236{
1237 const uint result = uint(AMIX(AMASK) | MIX(RMASK) | MIX(GMASK) | MIX(BMASK));
1238 return INTERPOLATE_PIXEL_255(result, const_alpha, d, one_minus_const_alpha);
1239}
1240
1241inline uint comp_func_Plus_one_pixel(uint d, const uint s)
1242{
1243 const uint result = uint(AMIX(AMASK) | MIX(RMASK) | MIX(GMASK) | MIX(BMASK));
1244 return result;
1245}
1246
1247#undef MIX
1248#undef AMIX
1249
1250// must be multiple of 4 for easier SIMD implementations
1251static Q_CONSTEXPR int BufferSize = 2048;
1252
1253// A buffer of intermediate results used by simple bilinear scaling.
1254struct IntermediateBuffer
1255{
1256 // The idea is first to do the interpolation between the row s1 and the row s2
1257 // into this intermediate buffer, then later interpolate between two pixel of this buffer.
1258 //
1259 // buffer_rb is a buffer of red-blue component of the pixel, in the form 0x00RR00BB
1260 // buffer_ag is the alpha-green component of the pixel, in the form 0x00AA00GG
1261 // +1 for the last pixel to interpolate with, and +1 for rounding errors.
1262 quint32 buffer_rb[BufferSize+2];
1263 quint32 buffer_ag[BufferSize+2];
1264};
1265
1266struct QDitherInfo {
1267 int x;
1268 int y;
1269};
1270
1271typedef const uint *(QT_FASTCALL *FetchAndConvertPixelsFunc)(uint *buffer, const uchar *src, int index, int count,
1272 const QVector<QRgb> *clut, QDitherInfo *dither);
1273typedef void (QT_FASTCALL *ConvertAndStorePixelsFunc)(uchar *dest, const uint *src, int index, int count,
1274 const QVector<QRgb> *clut, QDitherInfo *dither);
1275
1276typedef const QRgba64 *(QT_FASTCALL *FetchAndConvertPixelsFunc64)(QRgba64 *buffer, const uchar *src, int index, int count,
1277 const QVector<QRgb> *clut, QDitherInfo *dither);
1278typedef void (QT_FASTCALL *ConvertAndStorePixelsFunc64)(uchar *dest, const QRgba64 *src, int index, int count,
1279 const QVector<QRgb> *clut, QDitherInfo *dither);
1280
1281typedef void (QT_FASTCALL *ConvertFunc)(uint *buffer, int count, const QVector<QRgb> *clut);
1282typedef void (QT_FASTCALL *Convert64Func)(quint64 *buffer, int count, const QVector<QRgb> *clut);
1283typedef const QRgba64 *(QT_FASTCALL *ConvertTo64Func)(QRgba64 *buffer, const uint *src, int count,
1284 const QVector<QRgb> *clut, QDitherInfo *dither);
1285typedef void (QT_FASTCALL *RbSwapFunc)(uchar *dst, const uchar *src, int count);
1286
1287
1288struct QPixelLayout
1289{
1290 // Bits per pixel
1291 enum BPP {
1292 BPPNone,
1293 BPP1MSB,
1294 BPP1LSB,
1295 BPP8,
1296 BPP16,
1297 BPP24,
1298 BPP32,
1299 BPP64,
1300 BPPCount
1301 };
1302
1303 bool hasAlphaChannel;
1304 bool premultiplied;
1305 BPP bpp;
1306 RbSwapFunc rbSwap;
1307 ConvertFunc convertToARGB32PM;
1308 ConvertTo64Func convertToRGBA64PM;
1309 FetchAndConvertPixelsFunc fetchToARGB32PM;
1310 FetchAndConvertPixelsFunc64 fetchToRGBA64PM;
1311 ConvertAndStorePixelsFunc storeFromARGB32PM;
1312 ConvertAndStorePixelsFunc storeFromRGB32;
1313};
1314
1315extern ConvertAndStorePixelsFunc64 qStoreFromRGBA64PM[QImage::NImageFormats];
1316
1317extern QPixelLayout qPixelLayouts[QImage::NImageFormats];
1318
1319extern MemRotateFunc qMemRotateFunctions[QPixelLayout::BPPCount][3];
1320
1321QT_END_NAMESPACE
1322
1323#endif // QDRAWHELPER_P_H
1324