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 signed int txop : 8;
354 uint 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(d: 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(a: op->radial.a, b, c: op->radial.sqrfr - (gx*gx + gy*gy));
483
484 BlendType result = RadialFetchFunc::null();
485 if (det >= 0) {
486 qreal detSqrt = qSqrt(v: det);
487
488 qreal s0 = (-b - detSqrt) * op->radial.inv2a;
489 qreal s1 = (-b + detSqrt) * op->radial.inv2a;
490
491 qreal s = qMax(a: s0, b: 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(data: &gradient, pos: 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_UNREACHABLE();
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(x: dst, a: qAlpha(rgb: ~src));
670}
671
672static Q_ALWAYS_INLINE void blend_pixel(quint32 &dst, const quint32 src, const int const_alpha)
673{
674 if (const_alpha == 255)
675 return blend_pixel(dst, src);
676 if (src != 0) {
677 const quint32 s = BYTE_MUL(x: src, a: const_alpha);
678 dst = s + BYTE_MUL(x: dst, a: qAlpha(rgb: ~s));
679 }
680}
681
682#if defined(__SSE2__)
683static Q_ALWAYS_INLINE uint interpolate_4_pixels_sse2(__m128i vt, __m128i vb, uint distx, uint disty)
684{
685 // First interpolate top and bottom pixels in parallel.
686 vt = _mm_unpacklo_epi8(a: vt, b: _mm_setzero_si128());
687 vb = _mm_unpacklo_epi8(a: vb, b: _mm_setzero_si128());
688 vt = _mm_mullo_epi16(a: vt, b: _mm_set1_epi16(w: 256 - disty));
689 vb = _mm_mullo_epi16(a: vb, b: _mm_set1_epi16(w: disty));
690 __m128i vlr = _mm_add_epi16(a: vt, b: vb);
691 vlr = _mm_srli_epi16(a: vlr, count: 8);
692 // vlr now contains the result of the first two interpolate calls vlr = unpacked((xright << 64) | xleft)
693
694 // Now the last interpolate between left and right..
695 const __m128i vidistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(256 - distx), _MM_SHUFFLE(0, 0, 0, 0));
696 const __m128i vdistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(distx), _MM_SHUFFLE(0, 0, 0, 0));
697 const __m128i vmulx = _mm_unpacklo_epi16(a: vidistx, b: vdistx);
698 vlr = _mm_unpacklo_epi16(a: vlr, _mm_srli_si128(vlr, 8));
699 // vlr now contains the colors of left and right interleaved { la, ra, lr, rr, lg, rg, lb, rb }
700 vlr = _mm_madd_epi16(a: vlr, b: vmulx); // Multiply and horizontal add.
701 vlr = _mm_srli_epi32(a: vlr, count: 8);
702 vlr = _mm_packs_epi32(a: vlr, b: vlr);
703 vlr = _mm_packus_epi16(a: vlr, b: vlr);
704 return _mm_cvtsi128_si32(a: vlr);
705}
706
707static inline uint interpolate_4_pixels(uint tl, uint tr, uint bl, uint br, uint distx, uint disty)
708{
709 __m128i vt = _mm_unpacklo_epi32(a: _mm_cvtsi32_si128(a: tl), b: _mm_cvtsi32_si128(a: tr));
710 __m128i vb = _mm_unpacklo_epi32(a: _mm_cvtsi32_si128(a: bl), b: _mm_cvtsi32_si128(a: br));
711 return interpolate_4_pixels_sse2(vt, vb, distx, disty);
712}
713
714static inline uint interpolate_4_pixels(const uint t[], const uint b[], uint distx, uint disty)
715{
716 __m128i vt = _mm_loadl_epi64(p: (const __m128i*)t);
717 __m128i vb = _mm_loadl_epi64(p: (const __m128i*)b);
718 return interpolate_4_pixels_sse2(vt, vb, distx, disty);
719}
720
721static constexpr inline bool hasFastInterpolate4() { return true; }
722
723#elif defined(__ARM_NEON__)
724static Q_ALWAYS_INLINE uint interpolate_4_pixels_neon(uint32x2_t vt32, uint32x2_t vb32, uint distx, uint disty)
725{
726 uint16x8_t vt16 = vmovl_u8(vreinterpret_u8_u32(vt32));
727 uint16x8_t vb16 = vmovl_u8(vreinterpret_u8_u32(vb32));
728 vt16 = vmulq_n_u16(vt16, 256 - disty);
729 vt16 = vmlaq_n_u16(vt16, vb16, disty);
730 vt16 = vshrq_n_u16(vt16, 8);
731 uint16x4_t vl16 = vget_low_u16(vt16);
732 uint16x4_t vr16 = vget_high_u16(vt16);
733 vl16 = vmul_n_u16(vl16, 256 - distx);
734 vl16 = vmla_n_u16(vl16, vr16, distx);
735 vl16 = vshr_n_u16(vl16, 8);
736 uint8x8_t vr = vmovn_u16(vcombine_u16(vl16, vl16));
737 return vget_lane_u32(vreinterpret_u32_u8(vr), 0);
738}
739
740static inline uint interpolate_4_pixels(uint tl, uint tr, uint bl, uint br, uint distx, uint disty)
741{
742 uint32x2_t vt32 = vmov_n_u32(tl);
743 uint32x2_t vb32 = vmov_n_u32(bl);
744 vt32 = vset_lane_u32(tr, vt32, 1);
745 vb32 = vset_lane_u32(br, vb32, 1);
746 return interpolate_4_pixels_neon(vt32, vb32, distx, disty);
747}
748
749static inline uint interpolate_4_pixels(const uint t[], const uint b[], uint distx, uint disty)
750{
751 uint32x2_t vt32 = vld1_u32(t);
752 uint32x2_t vb32 = vld1_u32(b);
753 return interpolate_4_pixels_neon(vt32, vb32, distx, disty);
754}
755
756static constexpr inline bool hasFastInterpolate4() { return true; }
757
758#else
759static inline uint interpolate_4_pixels(uint tl, uint tr, uint bl, uint br, uint distx, uint disty)
760{
761 uint idistx = 256 - distx;
762 uint idisty = 256 - disty;
763 uint xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx);
764 uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx);
765 return INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty);
766}
767
768static inline uint interpolate_4_pixels(const uint t[], const uint b[], uint distx, uint disty)
769{
770 return interpolate_4_pixels(t[0], t[1], b[0], b[1], distx, disty);
771}
772
773static constexpr inline bool hasFastInterpolate4() { return false; }
774
775#endif
776
777static inline QRgba64 multiplyAlpha256(QRgba64 rgba64, uint alpha256)
778{
779 return QRgba64::fromRgba64(red: (rgba64.red() * alpha256) >> 8,
780 green: (rgba64.green() * alpha256) >> 8,
781 blue: (rgba64.blue() * alpha256) >> 8,
782 alpha: (rgba64.alpha() * alpha256) >> 8);
783}
784static inline QRgba64 interpolate256(QRgba64 x, uint alpha1, QRgba64 y, uint alpha2)
785{
786 return QRgba64::fromRgba64(c: multiplyAlpha256(rgba64: x, alpha256: alpha1) + multiplyAlpha256(rgba64: y, alpha256: alpha2));
787}
788
789#ifdef __SSE2__
790static inline QRgba64 interpolate_4_pixels_rgb64(const QRgba64 t[], const QRgba64 b[], uint distx, uint disty)
791{
792 __m128i vt = _mm_loadu_si128(p: (const __m128i*)t);
793 if (disty) {
794 __m128i vb = _mm_loadu_si128(p: (const __m128i*)b);
795 vt = _mm_mulhi_epu16(a: vt, b: _mm_set1_epi16(w: 0x10000 - disty));
796 vb = _mm_mulhi_epu16(a: vb, b: _mm_set1_epi16(w: disty));
797 vt = _mm_add_epi16(a: vt, b: vb);
798 }
799 if (distx) {
800 const __m128i vdistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(distx), _MM_SHUFFLE(0, 0, 0, 0));
801 const __m128i vidistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(0x10000 - distx), _MM_SHUFFLE(0, 0, 0, 0));
802 vt = _mm_mulhi_epu16(a: vt, b: _mm_unpacklo_epi64(a: vidistx, b: vdistx));
803 vt = _mm_add_epi16(a: vt, _mm_srli_si128(vt, 8));
804 }
805#ifdef Q_PROCESSOR_X86_64
806 return QRgba64::fromRgba64(c: _mm_cvtsi128_si64(a: vt));
807#else
808 QRgba64 out;
809 _mm_storel_epi64((__m128i*)&out, vt);
810 return out;
811#endif // Q_PROCESSOR_X86_64
812}
813#elif defined(__ARM_NEON__)
814static inline QRgba64 interpolate_4_pixels_rgb64(const QRgba64 t[], const QRgba64 b[], uint distx, uint disty)
815{
816 uint64x1x2_t vt = vld2_u64(reinterpret_cast<const uint64_t *>(t));
817 if (disty) {
818 uint64x1x2_t vb = vld2_u64(reinterpret_cast<const uint64_t *>(b));
819 uint32x4_t vt0 = vmull_n_u16(vreinterpret_u16_u64(vt.val[0]), 0x10000 - disty);
820 uint32x4_t vt1 = vmull_n_u16(vreinterpret_u16_u64(vt.val[1]), 0x10000 - disty);
821 vt0 = vmlal_n_u16(vt0, vreinterpret_u16_u64(vb.val[0]), disty);
822 vt1 = vmlal_n_u16(vt1, vreinterpret_u16_u64(vb.val[1]), disty);
823 vt.val[0] = vreinterpret_u64_u16(vshrn_n_u32(vt0, 16));
824 vt.val[1] = vreinterpret_u64_u16(vshrn_n_u32(vt1, 16));
825 }
826 if (distx) {
827 uint32x4_t vt0 = vmull_n_u16(vreinterpret_u16_u64(vt.val[0]), 0x10000 - distx);
828 vt0 = vmlal_n_u16(vt0, vreinterpret_u16_u64(vt.val[1]), distx);
829 vt.val[0] = vreinterpret_u64_u16(vshrn_n_u32(vt0, 16));
830 }
831 QRgba64 out;
832 vst1_u64(reinterpret_cast<uint64_t *>(&out), vt.val[0]);
833 return out;
834}
835#else
836static inline QRgba64 interpolate_4_pixels_rgb64(const QRgba64 t[], const QRgba64 b[], uint distx, uint disty)
837{
838 const uint dx = distx>>8;
839 const uint dy = disty>>8;
840 const uint idx = 256 - dx;
841 const uint idy = 256 - dy;
842 QRgba64 xtop = interpolate256(t[0], idx, t[1], dx);
843 QRgba64 xbot = interpolate256(b[0], idx, b[1], dx);
844 return interpolate256(xtop, idy, xbot, dy);
845}
846#endif // __SSE2__
847
848#if Q_BYTE_ORDER == Q_BIG_ENDIAN
849static Q_ALWAYS_INLINE quint32 RGBA2ARGB(quint32 x) {
850 quint32 rgb = x >> 8;
851 quint32 a = x << 24;
852 return a | rgb;
853}
854
855static Q_ALWAYS_INLINE quint32 ARGB2RGBA(quint32 x) {
856 quint32 rgb = x << 8;
857 quint32 a = x >> 24;
858 return a | rgb;
859}
860#else
861static Q_ALWAYS_INLINE quint32 RGBA2ARGB(quint32 x) {
862 // RGBA8888 is ABGR32 on little endian.
863 quint32 ag = x & 0xff00ff00;
864 quint32 rg = x & 0x00ff00ff;
865 return ag | (rg << 16) | (rg >> 16);
866}
867
868static Q_ALWAYS_INLINE quint32 ARGB2RGBA(quint32 x) {
869 return RGBA2ARGB(x);
870}
871#endif
872
873static Q_ALWAYS_INLINE uint BYTE_MUL_RGB16(uint x, uint a) {
874 a += 1;
875 uint t = (((x & 0x07e0)*a) >> 8) & 0x07e0;
876 t |= (((x & 0xf81f)*(a>>2)) >> 6) & 0xf81f;
877 return t;
878}
879
880static Q_ALWAYS_INLINE uint BYTE_MUL_RGB16_32(uint x, uint a) {
881 uint t = (((x & 0xf81f07e0) >> 5)*a) & 0xf81f07e0;
882 t |= (((x & 0x07e0f81f)*a) >> 5) & 0x07e0f81f;
883 return t;
884}
885
886// qt_div_255 is a fast rounded division by 255 using an approximation that is accurate for all positive 16-bit integers
887static Q_DECL_CONSTEXPR Q_ALWAYS_INLINE int qt_div_255(int x) { return (x + (x>>8) + 0x80) >> 8; }
888static Q_DECL_CONSTEXPR Q_ALWAYS_INLINE uint qt_div_257_floor(uint x) { return (x - (x >> 8)) >> 8; }
889static Q_DECL_CONSTEXPR Q_ALWAYS_INLINE uint qt_div_257(uint x) { return qt_div_257_floor(x: x + 128); }
890static Q_DECL_CONSTEXPR Q_ALWAYS_INLINE uint qt_div_65535(uint x) { return (x + (x>>16) + 0x8000U) >> 16; }
891
892static Q_ALWAYS_INLINE uint qAlphaRgb30(uint c)
893{
894 uint a = c >> 30;
895 a |= a << 2;
896 a |= a << 4;
897 return a;
898}
899
900template <class T> inline void qt_memfill_template(T *dest, T color, qsizetype count)
901{
902 if (!count)
903 return;
904
905 qsizetype n = (count + 7) / 8;
906 switch (count & 0x07)
907 {
908 case 0: do { *dest++ = color; Q_FALLTHROUGH();
909 case 7: *dest++ = color; Q_FALLTHROUGH();
910 case 6: *dest++ = color; Q_FALLTHROUGH();
911 case 5: *dest++ = color; Q_FALLTHROUGH();
912 case 4: *dest++ = color; Q_FALLTHROUGH();
913 case 3: *dest++ = color; Q_FALLTHROUGH();
914 case 2: *dest++ = color; Q_FALLTHROUGH();
915 case 1: *dest++ = color;
916 } while (--n > 0);
917 }
918}
919
920template <class T> inline void qt_memfill(T *dest, T value, qsizetype count)
921{
922 qt_memfill_template(dest, value, count);
923}
924
925template<> inline void qt_memfill(quint64 *dest, quint64 color, qsizetype count)
926{
927 qt_memfill64(dest, color, count);
928}
929
930template<> inline void qt_memfill(quint32 *dest, quint32 color, qsizetype count)
931{
932 qt_memfill32(dest, color, count);
933}
934
935template<> inline void qt_memfill(quint24 *dest, quint24 color, qsizetype count)
936{
937 qt_memfill24(dest, value: color, count);
938}
939
940template<> inline void qt_memfill(quint16 *dest, quint16 color, qsizetype count)
941{
942 qt_memfill16(dest, value: color, count);
943}
944
945template<> inline void qt_memfill(quint8 *dest, quint8 color, qsizetype count)
946{
947 memset(s: dest, c: color, n: count);
948}
949
950template <class T> static
951inline void qt_rectfill(T *dest, T value,
952 int x, int y, int width, int height, qsizetype stride)
953{
954 char *d = reinterpret_cast<char*>(dest + x) + y * stride;
955 if (uint(stride) == (width * sizeof(T))) {
956 qt_memfill(reinterpret_cast<T*>(d), value, qsizetype(width) * height);
957 } else {
958 for (int j = 0; j < height; ++j) {
959 dest = reinterpret_cast<T*>(d);
960 qt_memfill(dest, value, width);
961 d += stride;
962 }
963 }
964}
965
966inline ushort qConvertRgb32To16(uint c)
967{
968 return (((c) >> 3) & 0x001f)
969 | (((c) >> 5) & 0x07e0)
970 | (((c) >> 8) & 0xf800);
971}
972
973inline QRgb qConvertRgb16To32(uint c)
974{
975 return 0xff000000
976 | ((((c) << 3) & 0xf8) | (((c) >> 2) & 0x7))
977 | ((((c) << 5) & 0xfc00) | (((c) >> 1) & 0x300))
978 | ((((c) << 8) & 0xf80000) | (((c) << 3) & 0x70000));
979}
980
981enum QtPixelOrder {
982 PixelOrderRGB,
983 PixelOrderBGR
984};
985
986template<enum QtPixelOrder> inline uint qConvertArgb32ToA2rgb30(QRgb);
987
988template<enum QtPixelOrder> inline uint qConvertRgb32ToRgb30(QRgb);
989
990template<enum QtPixelOrder> inline QRgb qConvertA2rgb30ToArgb32(uint c);
991
992// A combined unpremultiply and premultiply with new simplified alpha.
993// Needed when alpha loses precision relative to other colors during conversion (ARGB32 -> A2RGB30).
994template<unsigned int Shift>
995inline QRgb qRepremultiply(QRgb p)
996{
997 const uint alpha = qAlpha(rgb: p);
998 if (alpha == 255 || alpha == 0)
999 return p;
1000 p = qUnpremultiply(p);
1001 Q_CONSTEXPR uint mult = 255 / (255 >> Shift);
1002 const uint newAlpha = mult * (alpha >> Shift);
1003 p = (p & ~0xff000000) | (newAlpha<<24);
1004 return qPremultiply(x: p);
1005}
1006
1007template<unsigned int Shift>
1008inline QRgba64 qRepremultiply(QRgba64 p)
1009{
1010 const uint alpha = p.alpha();
1011 if (alpha == 65535 || alpha == 0)
1012 return p;
1013 p = p.unpremultiplied();
1014 Q_CONSTEXPR uint mult = 65535 / (65535 >> Shift);
1015 p.setAlpha(mult * (alpha >> Shift));
1016 return p.premultiplied();
1017}
1018
1019template<>
1020inline uint qConvertArgb32ToA2rgb30<PixelOrderBGR>(QRgb c)
1021{
1022 c = qRepremultiply<6>(p: c);
1023 return (c & 0xc0000000)
1024 | (((c << 22) & 0x3fc00000) | ((c << 14) & 0x00300000))
1025 | (((c << 4) & 0x000ff000) | ((c >> 4) & 0x00000c00))
1026 | (((c >> 14) & 0x000003fc) | ((c >> 22) & 0x00000003));
1027}
1028
1029template<>
1030inline uint qConvertArgb32ToA2rgb30<PixelOrderRGB>(QRgb c)
1031{
1032 c = qRepremultiply<6>(p: c);
1033 return (c & 0xc0000000)
1034 | (((c << 6) & 0x3fc00000) | ((c >> 2) & 0x00300000))
1035 | (((c << 4) & 0x000ff000) | ((c >> 4) & 0x00000c00))
1036 | (((c << 2) & 0x000003fc) | ((c >> 6) & 0x00000003));
1037}
1038
1039template<>
1040inline uint qConvertRgb32ToRgb30<PixelOrderBGR>(QRgb c)
1041{
1042 return 0xc0000000
1043 | (((c << 22) & 0x3fc00000) | ((c << 14) & 0x00300000))
1044 | (((c << 4) & 0x000ff000) | ((c >> 4) & 0x00000c00))
1045 | (((c >> 14) & 0x000003fc) | ((c >> 22) & 0x00000003));
1046}
1047
1048template<>
1049inline uint qConvertRgb32ToRgb30<PixelOrderRGB>(QRgb c)
1050{
1051 return 0xc0000000
1052 | (((c << 6) & 0x3fc00000) | ((c >> 2) & 0x00300000))
1053 | (((c << 4) & 0x000ff000) | ((c >> 4) & 0x00000c00))
1054 | (((c << 2) & 0x000003fc) | ((c >> 6) & 0x00000003));
1055}
1056
1057template<>
1058inline QRgb qConvertA2rgb30ToArgb32<PixelOrderBGR>(uint c)
1059{
1060 uint a = c >> 30;
1061 a |= a << 2;
1062 a |= a << 4;
1063 return (a << 24)
1064 | ((c << 14) & 0x00ff0000)
1065 | ((c >> 4) & 0x0000ff00)
1066 | ((c >> 22) & 0x000000ff);
1067}
1068
1069template<>
1070inline QRgb qConvertA2rgb30ToArgb32<PixelOrderRGB>(uint c)
1071{
1072 uint a = c >> 30;
1073 a |= a << 2;
1074 a |= a << 4;
1075 return (a << 24)
1076 | ((c >> 6) & 0x00ff0000)
1077 | ((c >> 4) & 0x0000ff00)
1078 | ((c >> 2) & 0x000000ff);
1079}
1080
1081template<enum QtPixelOrder> inline QRgba64 qConvertA2rgb30ToRgb64(uint rgb);
1082
1083template<>
1084inline QRgba64 qConvertA2rgb30ToRgb64<PixelOrderBGR>(uint rgb)
1085{
1086 quint16 alpha = rgb >> 30;
1087 quint16 blue = (rgb >> 20) & 0x3ff;
1088 quint16 green = (rgb >> 10) & 0x3ff;
1089 quint16 red = rgb & 0x3ff;
1090 // Expand the range.
1091 alpha |= (alpha << 2);
1092 alpha |= (alpha << 4);
1093 alpha |= (alpha << 8);
1094 red = (red << 6) | (red >> 4);
1095 green = (green << 6) | (green >> 4);
1096 blue = (blue << 6) | (blue >> 4);
1097 return qRgba64(r: red, g: green, b: blue, a: alpha);
1098}
1099
1100template<>
1101inline QRgba64 qConvertA2rgb30ToRgb64<PixelOrderRGB>(uint rgb)
1102{
1103 quint16 alpha = rgb >> 30;
1104 quint16 red = (rgb >> 20) & 0x3ff;
1105 quint16 green = (rgb >> 10) & 0x3ff;
1106 quint16 blue = rgb & 0x3ff;
1107 // Expand the range.
1108 alpha |= (alpha << 2);
1109 alpha |= (alpha << 4);
1110 alpha |= (alpha << 8);
1111 red = (red << 6) | (red >> 4);
1112 green = (green << 6) | (green >> 4);
1113 blue = (blue << 6) | (blue >> 4);
1114 return qRgba64(r: red, g: green, b: blue, a: alpha);
1115}
1116
1117template<enum QtPixelOrder> inline unsigned int qConvertRgb64ToRgb30(QRgba64);
1118
1119template<>
1120inline unsigned int qConvertRgb64ToRgb30<PixelOrderBGR>(QRgba64 c)
1121{
1122 c = qRepremultiply<14>(p: c);
1123 const uint a = c.alpha() >> 14;
1124 const uint r = c.red() >> 6;
1125 const uint g = c.green() >> 6;
1126 const uint b = c.blue() >> 6;
1127 return (a << 30) | (b << 20) | (g << 10) | r;
1128}
1129
1130template<>
1131inline unsigned int qConvertRgb64ToRgb30<PixelOrderRGB>(QRgba64 c)
1132{
1133 c = qRepremultiply<14>(p: c);
1134 const uint a = c.alpha() >> 14;
1135 const uint r = c.red() >> 6;
1136 const uint g = c.green() >> 6;
1137 const uint b = c.blue() >> 6;
1138 return (a << 30) | (r << 20) | (g << 10) | b;
1139}
1140
1141inline uint qRgbSwapRgb30(uint c)
1142{
1143 const uint ag = c & 0xc00ffc00;
1144 const uint rb = c & 0x3ff003ff;
1145 return ag | (rb << 20) | (rb >> 20);
1146}
1147
1148inline int qRed565(quint16 rgb) {
1149 const int r = (rgb & 0xf800);
1150 return (r >> 8) | (r >> 13);
1151}
1152
1153inline int qGreen565(quint16 rgb) {
1154 const int g = (rgb & 0x07e0);
1155 return (g >> 3) | (g >> 9);
1156}
1157
1158inline int qBlue565(quint16 rgb) {
1159 const int b = (rgb & 0x001f);
1160 return (b << 3) | (b >> 2);
1161}
1162
1163// We manually unalias the variables to make sure the compiler
1164// fully optimizes both aliased and unaliased cases.
1165#define UNALIASED_CONVERSION_LOOP(buffer, src, count, conversion) \
1166 if (src == buffer) { \
1167 for (int i = 0; i < count; ++i) \
1168 buffer[i] = conversion(buffer[i]); \
1169 } else { \
1170 for (int i = 0; i < count; ++i) \
1171 buffer[i] = conversion(src[i]); \
1172 }
1173
1174
1175static Q_ALWAYS_INLINE const uint *qt_convertARGB32ToARGB32PM(uint *buffer, const uint *src, int count)
1176{
1177 UNALIASED_CONVERSION_LOOP(buffer, src, count, qPremultiply);
1178 return buffer;
1179}
1180
1181static Q_ALWAYS_INLINE const uint *qt_convertRGBA8888ToARGB32PM(uint *buffer, const uint *src, int count)
1182{
1183 UNALIASED_CONVERSION_LOOP(buffer, src, count, [](uint s) { return qPremultiply(RGBA2ARGB(s));});
1184 return buffer;
1185}
1186
1187template<bool RGBA> void qt_convertRGBA64ToARGB32(uint *dst, const QRgba64 *src, int count);
1188
1189const uint qt_bayer_matrix[16][16] = {
1190 { 0x1, 0xc0, 0x30, 0xf0, 0xc, 0xcc, 0x3c, 0xfc,
1191 0x3, 0xc3, 0x33, 0xf3, 0xf, 0xcf, 0x3f, 0xff},
1192 { 0x80, 0x40, 0xb0, 0x70, 0x8c, 0x4c, 0xbc, 0x7c,
1193 0x83, 0x43, 0xb3, 0x73, 0x8f, 0x4f, 0xbf, 0x7f},
1194 { 0x20, 0xe0, 0x10, 0xd0, 0x2c, 0xec, 0x1c, 0xdc,
1195 0x23, 0xe3, 0x13, 0xd3, 0x2f, 0xef, 0x1f, 0xdf},
1196 { 0xa0, 0x60, 0x90, 0x50, 0xac, 0x6c, 0x9c, 0x5c,
1197 0xa3, 0x63, 0x93, 0x53, 0xaf, 0x6f, 0x9f, 0x5f},
1198 { 0x8, 0xc8, 0x38, 0xf8, 0x4, 0xc4, 0x34, 0xf4,
1199 0xb, 0xcb, 0x3b, 0xfb, 0x7, 0xc7, 0x37, 0xf7},
1200 { 0x88, 0x48, 0xb8, 0x78, 0x84, 0x44, 0xb4, 0x74,
1201 0x8b, 0x4b, 0xbb, 0x7b, 0x87, 0x47, 0xb7, 0x77},
1202 { 0x28, 0xe8, 0x18, 0xd8, 0x24, 0xe4, 0x14, 0xd4,
1203 0x2b, 0xeb, 0x1b, 0xdb, 0x27, 0xe7, 0x17, 0xd7},
1204 { 0xa8, 0x68, 0x98, 0x58, 0xa4, 0x64, 0x94, 0x54,
1205 0xab, 0x6b, 0x9b, 0x5b, 0xa7, 0x67, 0x97, 0x57},
1206 { 0x2, 0xc2, 0x32, 0xf2, 0xe, 0xce, 0x3e, 0xfe,
1207 0x1, 0xc1, 0x31, 0xf1, 0xd, 0xcd, 0x3d, 0xfd},
1208 { 0x82, 0x42, 0xb2, 0x72, 0x8e, 0x4e, 0xbe, 0x7e,
1209 0x81, 0x41, 0xb1, 0x71, 0x8d, 0x4d, 0xbd, 0x7d},
1210 { 0x22, 0xe2, 0x12, 0xd2, 0x2e, 0xee, 0x1e, 0xde,
1211 0x21, 0xe1, 0x11, 0xd1, 0x2d, 0xed, 0x1d, 0xdd},
1212 { 0xa2, 0x62, 0x92, 0x52, 0xae, 0x6e, 0x9e, 0x5e,
1213 0xa1, 0x61, 0x91, 0x51, 0xad, 0x6d, 0x9d, 0x5d},
1214 { 0xa, 0xca, 0x3a, 0xfa, 0x6, 0xc6, 0x36, 0xf6,
1215 0x9, 0xc9, 0x39, 0xf9, 0x5, 0xc5, 0x35, 0xf5},
1216 { 0x8a, 0x4a, 0xba, 0x7a, 0x86, 0x46, 0xb6, 0x76,
1217 0x89, 0x49, 0xb9, 0x79, 0x85, 0x45, 0xb5, 0x75},
1218 { 0x2a, 0xea, 0x1a, 0xda, 0x26, 0xe6, 0x16, 0xd6,
1219 0x29, 0xe9, 0x19, 0xd9, 0x25, 0xe5, 0x15, 0xd5},
1220 { 0xaa, 0x6a, 0x9a, 0x5a, 0xa6, 0x66, 0x96, 0x56,
1221 0xa9, 0x69, 0x99, 0x59, 0xa5, 0x65, 0x95, 0x55}
1222};
1223
1224#define ARGB_COMBINE_ALPHA(argb, alpha) \
1225 ((((argb >> 24) * alpha) >> 8) << 24) | (argb & 0x00ffffff)
1226
1227
1228#if Q_PROCESSOR_WORDSIZE == 8 // 64-bit versions
1229#define AMIX(mask) (qMin(((quint64(s)&mask) + (quint64(d)&mask)), quint64(mask)))
1230#define MIX(mask) (qMin(((quint64(s)&mask) + (quint64(d)&mask)), quint64(mask)))
1231#else // 32 bits
1232// The mask for alpha can overflow over 32 bits
1233#define AMIX(mask) quint32(qMin(((quint64(s)&mask) + (quint64(d)&mask)), quint64(mask)))
1234#define MIX(mask) (qMin(((quint32(s)&mask) + (quint32(d)&mask)), quint32(mask)))
1235#endif
1236
1237inline uint comp_func_Plus_one_pixel_const_alpha(uint d, const uint s, const uint const_alpha, const uint one_minus_const_alpha)
1238{
1239 const uint result = uint(AMIX(AMASK) | MIX(RMASK) | MIX(GMASK) | MIX(BMASK));
1240 return INTERPOLATE_PIXEL_255(x: result, a: const_alpha, y: d, b: one_minus_const_alpha);
1241}
1242
1243inline uint comp_func_Plus_one_pixel(uint d, const uint s)
1244{
1245 const uint result = uint(AMIX(AMASK) | MIX(RMASK) | MIX(GMASK) | MIX(BMASK));
1246 return result;
1247}
1248
1249#undef MIX
1250#undef AMIX
1251
1252// must be multiple of 4 for easier SIMD implementations
1253static Q_CONSTEXPR int BufferSize = 2048;
1254
1255// A buffer of intermediate results used by simple bilinear scaling.
1256struct IntermediateBuffer
1257{
1258 // The idea is first to do the interpolation between the row s1 and the row s2
1259 // into this intermediate buffer, then later interpolate between two pixel of this buffer.
1260 //
1261 // buffer_rb is a buffer of red-blue component of the pixel, in the form 0x00RR00BB
1262 // buffer_ag is the alpha-green component of the pixel, in the form 0x00AA00GG
1263 // +1 for the last pixel to interpolate with, and +1 for rounding errors.
1264 quint32 buffer_rb[BufferSize+2];
1265 quint32 buffer_ag[BufferSize+2];
1266};
1267
1268struct QDitherInfo {
1269 int x;
1270 int y;
1271};
1272
1273typedef const uint *(QT_FASTCALL *FetchAndConvertPixelsFunc)(uint *buffer, const uchar *src, int index, int count,
1274 const QVector<QRgb> *clut, QDitherInfo *dither);
1275typedef void (QT_FASTCALL *ConvertAndStorePixelsFunc)(uchar *dest, const uint *src, int index, int count,
1276 const QVector<QRgb> *clut, QDitherInfo *dither);
1277
1278typedef const QRgba64 *(QT_FASTCALL *FetchAndConvertPixelsFunc64)(QRgba64 *buffer, const uchar *src, int index, int count,
1279 const QVector<QRgb> *clut, QDitherInfo *dither);
1280typedef void (QT_FASTCALL *ConvertAndStorePixelsFunc64)(uchar *dest, const QRgba64 *src, int index, int count,
1281 const QVector<QRgb> *clut, QDitherInfo *dither);
1282
1283typedef void (QT_FASTCALL *ConvertFunc)(uint *buffer, int count, const QVector<QRgb> *clut);
1284typedef void (QT_FASTCALL *Convert64Func)(quint64 *buffer, int count, const QVector<QRgb> *clut);
1285typedef const QRgba64 *(QT_FASTCALL *ConvertTo64Func)(QRgba64 *buffer, const uint *src, int count,
1286 const QVector<QRgb> *clut, QDitherInfo *dither);
1287typedef void (QT_FASTCALL *RbSwapFunc)(uchar *dst, const uchar *src, int count);
1288
1289
1290struct QPixelLayout
1291{
1292 // Bits per pixel
1293 enum BPP {
1294 BPPNone,
1295 BPP1MSB,
1296 BPP1LSB,
1297 BPP8,
1298 BPP16,
1299 BPP24,
1300 BPP32,
1301 BPP64,
1302 BPPCount
1303 };
1304
1305 bool hasAlphaChannel;
1306 bool premultiplied;
1307 BPP bpp;
1308 RbSwapFunc rbSwap;
1309 ConvertFunc convertToARGB32PM;
1310 ConvertTo64Func convertToRGBA64PM;
1311 FetchAndConvertPixelsFunc fetchToARGB32PM;
1312 FetchAndConvertPixelsFunc64 fetchToRGBA64PM;
1313 ConvertAndStorePixelsFunc storeFromARGB32PM;
1314 ConvertAndStorePixelsFunc storeFromRGB32;
1315};
1316
1317extern ConvertAndStorePixelsFunc64 qStoreFromRGBA64PM[QImage::NImageFormats];
1318
1319extern QPixelLayout qPixelLayouts[QImage::NImageFormats];
1320
1321extern MemRotateFunc qMemRotateFunctions[QPixelLayout::BPPCount][3];
1322
1323QT_END_NAMESPACE
1324
1325#endif // QDRAWHELPER_P_H
1326

source code of qtbase/src/gui/painting/qdrawhelper_p.h