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
qcolortransform.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qcolortransform.h"
5#include "qcolortransform_p.h"
6
7#include "qcmyk_p.h"
8#include "qcolorclut_p.h"
9#include "qcolormatrix_p.h"
10#include "qcolorspace_p.h"
11#include "qcolortrc_p.h"
12#include "qcolortrclut_p.h"
13
14#include <QtCore/qatomic.h>
15#include <QtCore/qmath.h>
16#include <QtGui/qcolor.h>
17#include <QtGui/qimage.h>
18#include <QtGui/qtransform.h>
19#include <QtCore/private/qsimd_p.h>
20
21#include <qdebug.h>
22
24
25std::shared_ptr<QColorTrcLut> lutFromTrc(const QColorTrc &trc)
26{
27 if (trc.m_type == QColorTrc::Type::Table)
29 if (trc.m_type == QColorTrc::Type::Function)
31 qWarning() << "TRC uninitialized";
32 return nullptr;
33}
34
36{
37 if (colorSpaceIn->lut.generated.loadAcquire())
38 return;
40 if (colorSpaceIn->lut.generated.loadRelaxed())
41 return;
42
43 for (int i = 0; i < 3; ++i) {
44 if (!colorSpaceIn->trc[i].isValid())
45 return;
46 }
47
48 if (colorSpaceIn->trc[0] == colorSpaceIn->trc[1] && colorSpaceIn->trc[0] == colorSpaceIn->trc[2]) {
52 } else {
53 for (int i = 0; i < 3; ++i)
55 }
56
57 colorSpaceIn->lut.generated.storeRelease(1);
58}
59
61{
62 if (colorSpaceOut->lut.generated.loadAcquire())
63 return;
65 if (colorSpaceOut->lut.generated.loadRelaxed())
66 return;
67 for (int i = 0; i < 3; ++i) {
68 if (!colorSpaceOut->trc[i].isValid())
69 return;
70 }
71
72 if (colorSpaceOut->trc[0] == colorSpaceOut->trc[1] && colorSpaceOut->trc[0] == colorSpaceOut->trc[2]) {
76 } else {
77 for (int i = 0; i < 3; ++i)
79 }
80
81 colorSpaceOut->lut.generated.storeRelease(1);
82}
83
103QColorTransform::QColorTransform(const QColorTransform &colorTransform) noexcept = default;
104
106
108
109
113bool QColorTransform::isIdentity() const noexcept
114{
115 return !d || d->isIdentity();
116}
117
132bool QColorTransform::compare(const QColorTransform &other) const
133{
134 if (d == other.d)
135 return true;
136 if (bool(d) != bool(other.d))
137 return d ? d->isIdentity() : other.d->isIdentity();
138 if (d->colorMatrix != other.d->colorMatrix)
139 return false;
140 if (bool(d->colorSpaceIn) != bool(other.d->colorSpaceIn))
141 return false;
142 if (bool(d->colorSpaceOut) != bool(other.d->colorSpaceOut))
143 return false;
144 if (d->colorSpaceIn) {
145 if (d->colorSpaceIn->transformModel != other.d->colorSpaceIn->transformModel)
146 return false;
148 for (int i = 0; i < 3; ++i) {
149 if (d->colorSpaceIn && d->colorSpaceIn->trc[i] != other.d->colorSpaceIn->trc[i])
150 return false;
151 }
152 } else {
153 if (!d->colorSpaceIn->equals(other.d->colorSpaceIn.constData()))
154 return false;
155 }
156 }
157 if (d->colorSpaceOut) {
158 if (d->colorSpaceOut->transformModel != other.d->colorSpaceOut->transformModel)
159 return false;
161 for (int i = 0; i < 3; ++i) {
162 if (d->colorSpaceOut && d->colorSpaceOut->trc[i] != other.d->colorSpaceOut->trc[i])
163 return false;
164 }
165 } else {
166 if (!d->colorSpaceOut->equals(other.d->colorSpaceOut.constData()))
167 return false;
168 }
169 }
170 return true;
171}
172
179{
180 if (!d)
181 return argb;
182 constexpr float f = 1.0f / 255.0f;
183 QColorVector c = { qRed(argb) * f, qGreen(argb) * f, qBlue(argb) * f };
184 c = d->map(c);
185 return qRgba(c.x * 255 + 0.5f, c.y * 255 + 0.5f, c.z * 255 + 0.5f, qAlpha(argb));
186}
187
194{
195 if (!d)
196 return rgba64;
197 constexpr float f = 1.0f / 65535.0f;
198 QColorVector c = { rgba64.red() * f, rgba64.green() * f, rgba64.blue() * f };
199 c = d->map(c);
200 return QRgba64::fromRgba64(c.x * 65535.f + 0.5f, c.y * 65535.f + 0.5f, c.z * 65535.f + 0.5f, rgba64.alpha());
201}
202
210{
211 if (!d)
212 return rgbafp16;
213 QColorVector c(rgbafp16.r, rgbafp16.g, rgbafp16.b);
214 c = d->mapExtended(c);
215 rgbafp16.r = qfloat16(c.x);
216 rgbafp16.g = qfloat16(c.y);
217 rgbafp16.b = qfloat16(c.z);
218 return rgbafp16;
219}
220
228{
229 if (!d)
230 return rgbafp32;
231 QColorVector c(rgbafp32.r, rgbafp32.g, rgbafp32.b);
232 c = d->mapExtended(c);
233 rgbafp32.r = c.x;
234 rgbafp32.g = c.y;
235 rgbafp32.b = c.z;
236 return rgbafp32;
237}
238
244{
245 if (!d)
246 return color;
247 QColor clr = color;
249 if (color.spec() != QColor::ExtendedRgb && color.spec() != QColor::Rgb)
250 clr = clr.toRgb();
252 if (color.spec() != QColor::Cmyk)
253 clr = clr.toCmyk();
254 }
255
257 (clr.spec() == QColor::Cmyk)
258 ? QColorVector(clr.cyanF(), clr.magentaF(), clr.yellowF(), clr.blackF())
259 : QColorVector(clr.redF(), clr.greenF(), clr.blueF());
260
261 c = d->mapExtended(c);
262
263 QColor out;
265 c.x = std::clamp(c.x, 0.f, 1.f);
266 c.y = std::clamp(c.y, 0.f, 1.f);
267 c.z = std::clamp(c.z, 0.f, 1.f);
268 c.w = std::clamp(c.w, 0.f, 1.f);
269 out.setCmykF(c.x, c.y, c.z, c.w, color.alphaF());
270 } else {
271 out.setRgbF(c.x, c.y, c.z, color.alphaF());
272 }
273 return out;
274}
275
276// Optimized sub-routines for fast block based conversion:
277
282
283template<ApplyMatrixForm doClamp = DoClamp>
284static void applyMatrix(QColorVector *buffer, const qsizetype len, const QColorMatrix &colorMatrix)
285{
286#if defined(__SSE2__)
287 const __m128 minV = _mm_set1_ps(0.0f);
288 const __m128 maxV = _mm_set1_ps(1.0f);
289 const __m128 xMat = _mm_loadu_ps(&colorMatrix.r.x);
290 const __m128 yMat = _mm_loadu_ps(&colorMatrix.g.x);
291 const __m128 zMat = _mm_loadu_ps(&colorMatrix.b.x);
292 for (qsizetype j = 0; j < len; ++j) {
293 __m128 c = _mm_loadu_ps(&buffer[j].x);
294 __m128 cx = _mm_shuffle_ps(c, c, _MM_SHUFFLE(0, 0, 0, 0));
295 __m128 cy = _mm_shuffle_ps(c, c, _MM_SHUFFLE(1, 1, 1, 1));
296 __m128 cz = _mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 2, 2, 2));
297 cx = _mm_mul_ps(cx, xMat);
298 cy = _mm_mul_ps(cy, yMat);
299 cz = _mm_mul_ps(cz, zMat);
300 cx = _mm_add_ps(cx, cy);
301 cx = _mm_add_ps(cx, cz);
302 // Clamp:
303 if (doClamp) {
304 cx = _mm_min_ps(cx, maxV);
305 cx = _mm_max_ps(cx, minV);
306 }
307 _mm_storeu_ps(&buffer[j].x, cx);
308 }
309#elif defined(__ARM_NEON__)
310 const float32x4_t minV = vdupq_n_f32(0.0f);
311 const float32x4_t maxV = vdupq_n_f32(1.0f);
312 const float32x4_t xMat = vld1q_f32(&colorMatrix.r.x);
313 const float32x4_t yMat = vld1q_f32(&colorMatrix.g.x);
314 const float32x4_t zMat = vld1q_f32(&colorMatrix.b.x);
315 for (qsizetype j = 0; j < len; ++j) {
316 float32x4_t c = vld1q_f32(&buffer[j].x);
317 float32x4_t cx = vmulq_n_f32(xMat, vgetq_lane_f32(c, 0));
318 float32x4_t cy = vmulq_n_f32(yMat, vgetq_lane_f32(c, 1));
319 float32x4_t cz = vmulq_n_f32(zMat, vgetq_lane_f32(c, 2));
320 cx = vaddq_f32(cx, cy);
321 cx = vaddq_f32(cx, cz);
322 // Clamp:
323 if (doClamp) {
324 cx = vminq_f32(cx, maxV);
325 cx = vmaxq_f32(cx, minV);
326 }
327 vst1q_f32(&buffer[j].x, cx);
328 }
329#else
330 for (qsizetype j = 0; j < len; ++j) {
331 const QColorVector cv = colorMatrix.map(buffer[j]);
332 if (doClamp) {
333 buffer[j].x = std::clamp(cv.x, 0.f, 1.f);
334 buffer[j].y = std::clamp(cv.y, 0.f, 1.f);
335 buffer[j].z = std::clamp(cv.z, 0.f, 1.f);
336 } else {
337 buffer[j] = cv;
338 }
339 }
340#endif
341}
342
343template<ApplyMatrixForm doClamp = DoClamp>
345{
346 if constexpr (doClamp != DoClamp)
347 return;
348#if defined(__SSE2__)
349 const __m128 minV = _mm_set1_ps(0.0f);
350 const __m128 maxV = _mm_set1_ps(1.0f);
351 for (qsizetype j = 0; j < len; ++j) {
352 __m128 c = _mm_loadu_ps(&buffer[j].x);
353 c = _mm_min_ps(c, maxV);
354 c = _mm_max_ps(c, minV);
355 _mm_storeu_ps(&buffer[j].x, c);
356 }
357#elif defined(__ARM_NEON__)
358 const float32x4_t minV = vdupq_n_f32(0.0f);
359 const float32x4_t maxV = vdupq_n_f32(1.0f);
360 for (qsizetype j = 0; j < len; ++j) {
361 float32x4_t c = vld1q_f32(&buffer[j].x);
362 c = vminq_f32(c, maxV);
363 c = vmaxq_f32(c, minV);
364 vst1q_f32(&buffer[j].x, c);
365 }
366#else
367 for (qsizetype j = 0; j < len; ++j) {
368 const QColorVector cv = buffer[j];
369 buffer[j].x = std::clamp(cv.x, 0.f, 1.f);
370 buffer[j].y = std::clamp(cv.y, 0.f, 1.f);
371 buffer[j].z = std::clamp(cv.z, 0.f, 1.f);
372 }
373#endif
374}
375
376#if defined(__SSE2__) || defined(__ARM_NEON__)
377template<typename T>
378static constexpr inline bool isArgb();
379template<>
380constexpr inline bool isArgb<QRgb>() { return true; }
381template<>
382constexpr inline bool isArgb<QRgba64>() { return false; }
383
384template<typename T>
385static inline int getAlpha(const T &p);
386template<>
387inline int getAlpha<QRgb>(const QRgb &p)
388{ return qAlpha(p); }
389template<>
390inline int getAlpha<QRgba64>(const QRgba64 &p)
391{ return p.alpha(); }
392
393#endif
394
395template<typename T>
396static float getAlphaF(const T &);
397template<> float getAlphaF(const QRgb &r)
398{
399 return qAlpha(r) * (1.f / 255.f);
400}
401template<> float getAlphaF(const QCmyk32 &)
402{
403 return 1.f;
404}
405template<> float getAlphaF(const QRgba64 &r)
406{
407 return r.alpha() * (1.f / 65535.f);
408}
409template<> float getAlphaF(const QRgbaFloat32 &r)
410{
411 return r.a;
412}
413
414template<typename T>
415static void loadPremultiplied(QColorVector *buffer, const T *src, const qsizetype len, const QColorTransformPrivate *d_ptr);
416template<typename T>
417static void loadUnpremultiplied(QColorVector *buffer, const T *src, const qsizetype len, const QColorTransformPrivate *d_ptr);
418
419#if defined(__SSE2__)
420// Load to [0-alpha] in 4x32 SIMD
421template<typename T>
422static inline void loadP(const T &p, __m128i &v);
423
424template<>
425inline void loadP<QRgb>(const QRgb &p, __m128i &v)
426{
427 v = _mm_cvtsi32_si128(p);
428#if defined(__SSE4_1__)
429 v = _mm_cvtepu8_epi32(v);
430#else
431 v = _mm_unpacklo_epi8(v, _mm_setzero_si128());
432 v = _mm_unpacklo_epi16(v, _mm_setzero_si128());
433#endif
434}
435
436template<>
437inline void loadP<QRgba64>(const QRgba64 &p, __m128i &v)
438{
439 v = _mm_loadl_epi64((const __m128i *)&p);
440#if defined(__SSE4_1__)
441 v = _mm_cvtepu16_epi32(v);
442#else
443 v = _mm_unpacklo_epi16(v, _mm_setzero_si128());
444#endif
445}
446
447template<typename T>
448static void loadPremultiplied(QColorVector *buffer, const T *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
449{
450 const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
451 const __m128 iFF00 = _mm_set1_ps(1.0f / (255 * 256));
452 constexpr bool isARGB = isArgb<T>();
453 for (qsizetype i = 0; i < len; ++i) {
454 __m128i v;
455 loadP<T>(src[i], v);
456 __m128 vf = _mm_cvtepi32_ps(v);
457 // Approximate 1/a:
458 __m128 va = _mm_shuffle_ps(vf, vf, _MM_SHUFFLE(3, 3, 3, 3));
459 __m128 via = _mm_rcp_ps(va);
460 via = _mm_sub_ps(_mm_add_ps(via, via), _mm_mul_ps(via, _mm_mul_ps(via, va)));
461 // v * (1/a)
462 vf = _mm_mul_ps(vf, via);
463
464 // Handle zero alpha
465 __m128 vAlphaMask = _mm_cmpeq_ps(va, _mm_set1_ps(0.0f));
466 vf = _mm_andnot_ps(vAlphaMask, vf);
467
468 // LUT
469 v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
470 const int ridx = isARGB ? _mm_extract_epi16(v, 4) : _mm_extract_epi16(v, 0);
471 const int gidx = _mm_extract_epi16(v, 2);
472 const int bidx = isARGB ? _mm_extract_epi16(v, 0) : _mm_extract_epi16(v, 4);
473 v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[0]->m_toLinear[ridx], 0);
474 v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[1]->m_toLinear[gidx], 2);
475 v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[2]->m_toLinear[bidx], 4);
476 vf = _mm_mul_ps(_mm_cvtepi32_ps(v), iFF00);
477
478 _mm_storeu_ps(&buffer[i].x, vf);
479 }
480}
481
482template<>
484{
485 const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
486 const __m128 viFF00 = _mm_set1_ps(1.0f / (255 * 256));
487 const __m128 vZero = _mm_set1_ps(0.0f);
488 const __m128 vOne = _mm_set1_ps(1.0f);
489 for (qsizetype i = 0; i < len; ++i) {
490 __m128 vf = _mm_loadu_ps(&src[i].r);
491 // Approximate 1/a:
492 __m128 va = _mm_shuffle_ps(vf, vf, _MM_SHUFFLE(3, 3, 3, 3));
493 __m128 via = _mm_rcp_ps(va);
494 via = _mm_sub_ps(_mm_add_ps(via, via), _mm_mul_ps(via, _mm_mul_ps(via, va)));
495 // v * (1/a)
496 vf = _mm_mul_ps(vf, via);
497
498 // Handle zero alpha
499 __m128 vAlphaMask = _mm_cmpeq_ps(va, vZero);
500 vf = _mm_andnot_ps(vAlphaMask, vf);
501
502 // LUT
503 const __m128 under = _mm_cmplt_ps(vf, vZero);
504 const __m128 over = _mm_cmpgt_ps(vf, vOne);
505 if (_mm_movemask_ps(_mm_or_ps(under, over)) == 0) {
506 // Within gamut
507 __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
508 const int ridx = _mm_extract_epi16(v, 0);
509 const int gidx = _mm_extract_epi16(v, 2);
510 const int bidx = _mm_extract_epi16(v, 4);
511 v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[0]->m_toLinear[ridx], 0);
512 v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[1]->m_toLinear[gidx], 2);
513 v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[2]->m_toLinear[bidx], 4);
514 vf = _mm_mul_ps(_mm_cvtepi32_ps(v), viFF00);
515 _mm_storeu_ps(&buffer[i].x, vf);
516 } else {
517 // Outside 0.0->1.0 gamut
518 _mm_storeu_ps(&buffer[i].x, vf);
519 buffer[i].x = d_ptr->colorSpaceIn->trc[0].applyExtended(buffer[i].x);
520 buffer[i].y = d_ptr->colorSpaceIn->trc[1].applyExtended(buffer[i].y);
521 buffer[i].z = d_ptr->colorSpaceIn->trc[2].applyExtended(buffer[i].z);
522 }
523 }
524}
525
526// Load to [0->TrcResolution] in 4x32 SIMD
527template<typename T>
528static inline void loadPU(const T &p, __m128i &v);
529
530template<>
531inline void loadPU<QRgb>(const QRgb &p, __m128i &v)
532{
533 v = _mm_cvtsi32_si128(p);
534#if defined(__SSE4_1__)
535 v = _mm_cvtepu8_epi32(v);
536#else
537 v = _mm_unpacklo_epi8(v, _mm_setzero_si128());
538 v = _mm_unpacklo_epi16(v, _mm_setzero_si128());
539#endif
540 v = _mm_slli_epi32(v, QColorTrcLut::ShiftUp);
541}
542
543template<>
544inline void loadPU<QRgba64>(const QRgba64 &p, __m128i &v)
545{
546 v = _mm_loadl_epi64((const __m128i *)&p);
547 v = _mm_sub_epi16(v, _mm_srli_epi16(v, 8));
548#if defined(__SSE4_1__)
549 v = _mm_cvtepu16_epi32(v);
550#else
551 v = _mm_unpacklo_epi16(v, _mm_setzero_si128());
552#endif
553 v = _mm_srli_epi32(v, QColorTrcLut::ShiftDown);
554}
555
556template<typename T>
558{
559 constexpr bool isARGB = isArgb<T>();
560 const __m128 iFF00 = _mm_set1_ps(1.0f / (255 * 256));
561 for (qsizetype i = 0; i < len; ++i) {
562 __m128i v;
563 loadPU<T>(src[i], v);
564 const int ridx = isARGB ? _mm_extract_epi16(v, 4) : _mm_extract_epi16(v, 0);
565 const int gidx = _mm_extract_epi16(v, 2);
566 const int bidx = isARGB ? _mm_extract_epi16(v, 0) : _mm_extract_epi16(v, 4);
567 v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[0]->m_toLinear[ridx], 0);
568 v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[1]->m_toLinear[gidx], 2);
569 v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[2]->m_toLinear[bidx], 4);
570 __m128 vf = _mm_mul_ps(_mm_cvtepi32_ps(v), iFF00);
571 _mm_storeu_ps(&buffer[i].x, vf);
572 }
573}
574
575template<>
577{
578 const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
579 const __m128 iFF00 = _mm_set1_ps(1.0f / (255 * 256));
580 const __m128 vZero = _mm_set1_ps(0.0f);
581 const __m128 vOne = _mm_set1_ps(1.0f);
582 for (qsizetype i = 0; i < len; ++i) {
583 __m128 vf = _mm_loadu_ps(&src[i].r);
584 const __m128 under = _mm_cmplt_ps(vf, vZero);
585 const __m128 over = _mm_cmpgt_ps(vf, vOne);
586 if (_mm_movemask_ps(_mm_or_ps(under, over)) == 0) {
587 // Within gamut
588 __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
589 const int ridx = _mm_extract_epi16(v, 0);
590 const int gidx = _mm_extract_epi16(v, 2);
591 const int bidx = _mm_extract_epi16(v, 4);
592 v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[0]->m_toLinear[ridx], 0);
593 v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[1]->m_toLinear[gidx], 2);
594 v = _mm_insert_epi16(v, d_ptr->colorSpaceIn->lut[2]->m_toLinear[bidx], 4);
595 vf = _mm_mul_ps(_mm_cvtepi32_ps(v), iFF00);
596 _mm_storeu_ps(&buffer[i].x, vf);
597 } else {
598 // Outside 0.0->1.0 gamut
599 buffer[i].x = d_ptr->colorSpaceIn->trc[0].applyExtended(src[i].r);
600 buffer[i].y = d_ptr->colorSpaceIn->trc[1].applyExtended(src[i].g);
601 buffer[i].z = d_ptr->colorSpaceIn->trc[2].applyExtended(src[i].b);
602 }
603 }
604}
605
606#elif defined(__ARM_NEON__)
607// Load to [0-alpha] in 4x32 SIMD
608template<typename T>
609static inline void loadP(const T &p, uint32x4_t &v);
610
611template<>
612inline void loadP<QRgb>(const QRgb &p, uint32x4_t &v)
613{
614 v = vmovl_u16(vget_low_u16(vmovl_u8(vreinterpret_u8_u32(vmov_n_u32(p)))));
615}
616
617template<>
618inline void loadP<QRgba64>(const QRgba64 &p, uint32x4_t &v)
619{
620 v = vmovl_u16(vreinterpret_u16_u64(vld1_u64(reinterpret_cast<const uint64_t *>(&p))));
621}
622
623template<typename T>
624static void loadPremultiplied(QColorVector *buffer, const T *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
625{
626 constexpr bool isARGB = isArgb<T>();
627 const float iFF00 = 1.0f / (255 * 256);
628 for (qsizetype i = 0; i < len; ++i) {
629 uint32x4_t v;
630 loadP<T>(src[i], v);
631 float32x4_t vf = vcvtq_f32_u32(v);
632 // Approximate 1/a:
633 float32x4_t va = vdupq_n_f32(vgetq_lane_f32(vf, 3));
634 float32x4_t via = vrecpeq_f32(va); // estimate 1/a
635 via = vmulq_f32(vrecpsq_f32(va, via), via);
636
637 // v * (1/a)
638 vf = vmulq_f32(vf, via);
639
640 // Handle zero alpha
641#if defined(Q_PROCESSOR_ARM_64)
642 uint32x4_t vAlphaMask = vceqzq_f32(va);
643#else
644 uint32x4_t vAlphaMask = vceqq_f32(va, vdupq_n_f32(0.0));
645#endif
646 vf = vreinterpretq_f32_u32(vbicq_u32(vreinterpretq_u32_f32(vf), vAlphaMask));
647
648 // LUT
649 v = vcvtq_u32_f32(vaddq_f32(vmulq_n_f32(vf, float(QColorTrcLut::Resolution)), vdupq_n_f32(0.5f)));
650 const int ridx = isARGB ? vgetq_lane_u32(v, 2) : vgetq_lane_u32(v, 0);
651 const int gidx = vgetq_lane_u32(v, 1);
652 const int bidx = isARGB ? vgetq_lane_u32(v, 0) : vgetq_lane_u32(v, 2);
653 v = vsetq_lane_u32(d_ptr->colorSpaceIn->lut[0]->m_toLinear[ridx], v, 0);
654 v = vsetq_lane_u32(d_ptr->colorSpaceIn->lut[1]->m_toLinear[gidx], v, 1);
655 v = vsetq_lane_u32(d_ptr->colorSpaceIn->lut[2]->m_toLinear[bidx], v, 2);
656 vf = vmulq_n_f32(vcvtq_f32_u32(v), iFF00);
657
658 vst1q_f32(&buffer[i].x, vf);
659 }
660}
661
662// Load to [0->TrcResultion] in 4x32 SIMD
663template<typename T>
664static inline void loadPU(const T &p, uint32x4_t &v);
665
666template<>
667inline void loadPU<QRgb>(const QRgb &p, uint32x4_t &v)
668{
669 v = vmovl_u16(vget_low_u16(vmovl_u8(vreinterpret_u8_u32(vmov_n_u32(p)))));
670 v = vshlq_n_u32(v, QColorTrcLut::ShiftUp);
671}
672
673template<>
674inline void loadPU<QRgba64>(const QRgba64 &p, uint32x4_t &v)
675{
676 uint16x4_t v16 = vreinterpret_u16_u64(vld1_u64(reinterpret_cast<const uint64_t *>(&p)));
677 v16 = vsub_u16(v16, vshr_n_u16(v16, 8));
678 v = vmovl_u16(v16);
679 v = vshrq_n_u32(v, QColorTrcLut::ShiftDown);
680}
681
682template<typename T>
684{
685 constexpr bool isARGB = isArgb<T>();
686 const float iFF00 = 1.0f / (255 * 256);
687 for (qsizetype i = 0; i < len; ++i) {
688 uint32x4_t v;
689 loadPU<T>(src[i], v);
690 const int ridx = isARGB ? vgetq_lane_u32(v, 2) : vgetq_lane_u32(v, 0);
691 const int gidx = vgetq_lane_u32(v, 1);
692 const int bidx = isARGB ? vgetq_lane_u32(v, 0) : vgetq_lane_u32(v, 2);
693 v = vsetq_lane_u32(d_ptr->colorSpaceIn->lut[0]->m_toLinear[ridx], v, 0);
694 v = vsetq_lane_u32(d_ptr->colorSpaceIn->lut[1]->m_toLinear[gidx], v, 1);
695 v = vsetq_lane_u32(d_ptr->colorSpaceIn->lut[2]->m_toLinear[bidx], v, 2);
696 float32x4_t vf = vmulq_n_f32(vcvtq_f32_u32(v), iFF00);
697 vst1q_f32(&buffer[i].x, vf);
698 }
699}
700#else
701template<>
703{
704 for (qsizetype i = 0; i < len; ++i) {
705 const uint p = src[i];
706 const int a = qAlpha(p);
707 if (a) {
708 const float ia = float(QColorTrcLut::Resolution) / a;
709 const int ridx = int(qRed(p) * ia + 0.5f);
710 const int gidx = int(qGreen(p) * ia + 0.5f);
711 const int bidx = int(qBlue(p) * ia + 0.5f);
712 buffer[i].x = d_ptr->colorSpaceIn->lut[0]->m_toLinear[ridx] * (1.0f / (255 * 256));
713 buffer[i].y = d_ptr->colorSpaceIn->lut[1]->m_toLinear[gidx] * (1.0f / (255 * 256));
714 buffer[i].z = d_ptr->colorSpaceIn->lut[2]->m_toLinear[bidx] * (1.0f / (255 * 256));
715 } else {
716 buffer[i].x = buffer[i].y = buffer[i].z = 0.0f;
717 }
718 }
719}
720
721template<>
723{
724 for (qsizetype i = 0; i < len; ++i) {
725 const QRgba64 &p = src[i];
726 const int a = p.alpha();
727 if (a) {
728 const float ia = float(QColorTrcLut::Resolution) / a;
729 const int ridx = int(p.red() * ia + 0.5f);
730 const int gidx = int(p.green() * ia + 0.5f);
731 const int bidx = int(p.blue() * ia + 0.5f);
732 buffer[i].x = d_ptr->colorSpaceIn->lut[0]->m_toLinear[ridx] * (1.0f / (255 * 256));
733 buffer[i].y = d_ptr->colorSpaceIn->lut[1]->m_toLinear[gidx] * (1.0f / (255 * 256));
734 buffer[i].z = d_ptr->colorSpaceIn->lut[2]->m_toLinear[bidx] * (1.0f / (255 * 256));
735 } else {
736 buffer[i].x = buffer[i].y = buffer[i].z = 0.0f;
737 }
738 }
739}
740
741template<>
743{
744 for (qsizetype i = 0; i < len; ++i) {
745 const uint p = src[i];
746 buffer[i].x = d_ptr->colorSpaceIn->lut[0]->u8ToLinearF32(qRed(p));
747 buffer[i].y = d_ptr->colorSpaceIn->lut[1]->u8ToLinearF32(qGreen(p));
748 buffer[i].z = d_ptr->colorSpaceIn->lut[2]->u8ToLinearF32(qBlue(p));
749 }
750}
751
752template<>
754{
755 for (qsizetype i = 0; i < len; ++i) {
756 const QRgba64 &p = src[i];
757 buffer[i].x = d_ptr->colorSpaceIn->lut[0]->u16ToLinearF32(p.red());
758 buffer[i].y = d_ptr->colorSpaceIn->lut[1]->u16ToLinearF32(p.green());
759 buffer[i].z = d_ptr->colorSpaceIn->lut[2]->u16ToLinearF32(p.blue());
760 }
761}
762#endif
763#if !defined(__SSE2__)
764template<>
766{
767 for (qsizetype i = 0; i < len; ++i) {
768 const QRgbaFloat32 &p = src[i];
769 const float a = p.a;
770 if (a) {
771 const float ia = 1.0f / a;
772 buffer[i].x = d_ptr->colorSpaceIn->trc[0].applyExtended(p.r * ia);
773 buffer[i].y = d_ptr->colorSpaceIn->trc[1].applyExtended(p.g * ia);
774 buffer[i].z = d_ptr->colorSpaceIn->trc[2].applyExtended(p.b * ia);
775 } else {
776 buffer[i].x = buffer[i].y = buffer[i].z = 0.0f;
777 }
778 }
779}
780
781template<>
783{
784 for (qsizetype i = 0; i < len; ++i) {
785 const QRgbaFloat32 &p = src[i];
786 buffer[i].x = d_ptr->colorSpaceIn->trc[0].applyExtended(p.r);
787 buffer[i].y = d_ptr->colorSpaceIn->trc[1].applyExtended(p.g);
788 buffer[i].z = d_ptr->colorSpaceIn->trc[2].applyExtended(p.b);
789 }
790}
791#endif
792
793#if defined(__SSE2__)
794template<typename T>
795static inline void storeP(T &p, __m128i &v, int a);
796template<>
797inline void storeP<QRgb>(QRgb &p, __m128i &v, int a)
798{
799 v = _mm_packs_epi32(v, v);
800 v = _mm_insert_epi16(v, a, 3);
801 p = _mm_cvtsi128_si32(_mm_packus_epi16(v, v));
802}
803template<>
804inline void storeP<QRgba64>(QRgba64 &p, __m128i &v, int a)
805{
806#if defined(__SSE4_1__)
807 v = _mm_packus_epi32(v, v);
808 v = _mm_insert_epi16(v, a, 3);
809 _mm_storel_epi64((__m128i *)&p, v);
810#else
811 const int r = _mm_extract_epi16(v, 0);
812 const int g = _mm_extract_epi16(v, 2);
813 const int b = _mm_extract_epi16(v, 4);
814 p = qRgba64(r, g, b, a);
815#endif
816}
817
818template<typename D, typename S,
819 typename = std::enable_if_t<!std::is_same_v<D, QRgbaFloat32>, void>>
820static void storePremultiplied(D *dst, const S *src, const QColorVector *buffer, const qsizetype len,
821 const QColorTransformPrivate *d_ptr)
822{
823 const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
824 const __m128 iFF00 = _mm_set1_ps(1.0f / (255 * 256));
825 constexpr bool isARGB = isArgb<D>();
826 for (qsizetype i = 0; i < len; ++i) {
827 const int a = getAlpha<S>(src[i]);
828 __m128 vf = _mm_loadu_ps(&buffer[i].x);
829 __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
830 __m128 va = _mm_mul_ps(_mm_set1_ps(a), iFF00);
831 const int ridx = _mm_extract_epi16(v, 0);
832 const int gidx = _mm_extract_epi16(v, 2);
833 const int bidx = _mm_extract_epi16(v, 4);
834 v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[0]->m_fromLinear[ridx], isARGB ? 4 : 0);
835 v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[1]->m_fromLinear[gidx], 2);
836 v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[2]->m_fromLinear[bidx], isARGB ? 0 : 4);
837 vf = _mm_cvtepi32_ps(v);
838 vf = _mm_mul_ps(vf, va);
839 v = _mm_cvtps_epi32(vf);
840 storeP<D>(dst[i], v, a);
841 }
842}
843
844template<typename S>
845static void storePremultiplied(QRgbaFloat32 *dst, const S *src,
846 const QColorVector *buffer, const qsizetype len,
847 const QColorTransformPrivate *d_ptr)
848{
849 const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
850 const __m128 vZero = _mm_set1_ps(0.0f);
851 const __m128 vOne = _mm_set1_ps(1.0f);
852 const __m128 viFF00 = _mm_set1_ps(1.0f / (255 * 256));
853 for (qsizetype i = 0; i < len; ++i) {
854 const float a = getAlphaF<S>(src[i]);
855 __m128 va = _mm_set1_ps(a);
856 __m128 vf = _mm_loadu_ps(&buffer[i].x);
857 const __m128 under = _mm_cmplt_ps(vf, vZero);
858 const __m128 over = _mm_cmpgt_ps(vf, vOne);
859 if (_mm_movemask_ps(_mm_or_ps(under, over)) == 0) {
860 // Within gamut
861 va = _mm_mul_ps(va, viFF00);
862 __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
863 const int ridx = _mm_extract_epi16(v, 0);
864 const int gidx = _mm_extract_epi16(v, 2);
865 const int bidx = _mm_extract_epi16(v, 4);
866 v = _mm_setzero_si128();
867 v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[0]->m_fromLinear[ridx], 0);
868 v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[1]->m_fromLinear[gidx], 2);
869 v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[2]->m_fromLinear[bidx], 4);
870 vf = _mm_mul_ps(_mm_cvtepi32_ps(v), va);
871 _mm_store_ps(&dst[i].r, vf);
872 } else {
876 vf = _mm_mul_ps(_mm_load_ps(&dst[i].r), va);
877 _mm_store_ps(&dst[i].r, vf);
878 }
879 dst[i].a = a;
880 }
881}
882
883template<typename T>
884static inline void storePU(T &p, __m128i &v, int a);
885template<>
886inline void storePU<QRgb>(QRgb &p, __m128i &v, int a)
887{
888 v = _mm_add_epi16(v, _mm_set1_epi16(0x80));
889 v = _mm_srli_epi16(v, 8);
890 v = _mm_insert_epi16(v, a, 3);
891 p = _mm_cvtsi128_si32(_mm_packus_epi16(v, v));
892}
893template<>
894inline void storePU<QRgba64>(QRgba64 &p, __m128i &v, int a)
895{
896 v = _mm_add_epi16(v, _mm_srli_epi16(v, 8));
897 v = _mm_insert_epi16(v, a, 3);
898 _mm_storel_epi64((__m128i *)&p, v);
899}
900
901template<typename D, typename S,
902 typename = std::enable_if_t<!std::is_same_v<D, QRgbaFloat32>, void>>
903static void storeUnpremultiplied(D *dst, const S *src, const QColorVector *buffer, const qsizetype len,
904 const QColorTransformPrivate *d_ptr)
905{
906 const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
907 constexpr bool isARGB = isArgb<D>();
908 for (qsizetype i = 0; i < len; ++i) {
909 const int a = getAlpha<S>(src[i]);
910 __m128 vf = _mm_loadu_ps(&buffer[i].x);
911 __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
912 const int ridx = _mm_extract_epi16(v, 0);
913 const int gidx = _mm_extract_epi16(v, 2);
914 const int bidx = _mm_extract_epi16(v, 4);
915 v = _mm_setzero_si128();
916 v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[0]->m_fromLinear[ridx], isARGB ? 2 : 0);
917 v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[1]->m_fromLinear[gidx], 1);
918 v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[2]->m_fromLinear[bidx], isARGB ? 0 : 2);
919 storePU<D>(dst[i], v, a);
920 }
921}
922
923template<typename S>
925 const QColorVector *buffer, const qsizetype len,
926 const QColorTransformPrivate *d_ptr)
927{
928 const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
929 const __m128 vZero = _mm_set1_ps(0.0f);
930 const __m128 vOne = _mm_set1_ps(1.0f);
931 const __m128 viFF00 = _mm_set1_ps(1.0f / (255 * 256));
932 for (qsizetype i = 0; i < len; ++i) {
933 const float a = getAlphaF<S>(src[i]);
934 __m128 vf = _mm_loadu_ps(&buffer[i].x);
935 const __m128 under = _mm_cmplt_ps(vf, vZero);
936 const __m128 over = _mm_cmpgt_ps(vf, vOne);
937 if (_mm_movemask_ps(_mm_or_ps(under, over)) == 0) {
938 // Within gamut
939 __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
940 const int ridx = _mm_extract_epi16(v, 0);
941 const int gidx = _mm_extract_epi16(v, 2);
942 const int bidx = _mm_extract_epi16(v, 4);
943 v = _mm_setzero_si128();
944 v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[0]->m_fromLinear[ridx], 0);
945 v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[1]->m_fromLinear[gidx], 2);
946 v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[2]->m_fromLinear[bidx], 4);
947 vf = _mm_mul_ps(_mm_cvtepi32_ps(v), viFF00);
948 _mm_storeu_ps(&dst[i].r, vf);
949 } else {
953 }
954 dst[i].a = a;
955 }
956}
957
958template<typename T>
959static void storeOpaque(T *dst, const QColorVector *buffer, const qsizetype len,
960 const QColorTransformPrivate *d_ptr)
961{
962 const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
963 constexpr bool isARGB = isArgb<T>();
964 for (qsizetype i = 0; i < len; ++i) {
965 __m128 vf = _mm_loadu_ps(&buffer[i].x);
966 __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
967 const int ridx = _mm_extract_epi16(v, 0);
968 const int gidx = _mm_extract_epi16(v, 2);
969 const int bidx = _mm_extract_epi16(v, 4);
970 v = _mm_setzero_si128();
971 v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[0]->m_fromLinear[ridx], isARGB ? 2 : 0);
972 v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[1]->m_fromLinear[gidx], 1);
973 v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[2]->m_fromLinear[bidx], isARGB ? 0 : 2);
974 storePU<T>(dst[i], v, isARGB ? 255 : 0xffff);
975 }
976}
977
978template<>
980 const QColorTransformPrivate *d_ptr)
981{
982 const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
983 const __m128 vZero = _mm_set1_ps(0.0f);
984 const __m128 vOne = _mm_set1_ps(1.0f);
985 const __m128 viFF00 = _mm_set1_ps(1.0f / (255 * 256));
986 for (qsizetype i = 0; i < len; ++i) {
987 __m128 vf = _mm_loadu_ps(&buffer[i].x);
988 const __m128 under = _mm_cmplt_ps(vf, vZero);
989 const __m128 over = _mm_cmpgt_ps(vf, vOne);
990 if (_mm_movemask_ps(_mm_or_ps(under, over)) == 0) {
991 // Within gamut
992 __m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
993 const int ridx = _mm_extract_epi16(v, 0);
994 const int gidx = _mm_extract_epi16(v, 2);
995 const int bidx = _mm_extract_epi16(v, 4);
996 v = _mm_setzero_si128();
997 v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[0]->m_fromLinear[ridx], 0);
998 v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[1]->m_fromLinear[gidx], 2);
999 v = _mm_insert_epi16(v, d_ptr->colorSpaceOut->lut[2]->m_fromLinear[bidx], 4);
1000 vf = _mm_mul_ps(_mm_cvtepi32_ps(v), viFF00);
1001 _mm_store_ps(&dst[i].r, vf);
1002 } else {
1003 dst[i].r = d_ptr->colorSpaceOut->trc[0].applyInverseExtended(buffer[i].x);
1004 dst[i].g = d_ptr->colorSpaceOut->trc[1].applyInverseExtended(buffer[i].y);
1005 dst[i].b = d_ptr->colorSpaceOut->trc[2].applyInverseExtended(buffer[i].z);
1006 }
1007 dst[i].a = 1.0f;
1008 }
1009}
1010
1011#elif defined(__ARM_NEON__)
1012template<typename T>
1013static inline void storeP(T &p, const uint16x4_t &v);
1014template<>
1015inline void storeP<QRgb>(QRgb &p, const uint16x4_t &v)
1016{
1017 p = vget_lane_u32(vreinterpret_u32_u8(vmovn_u16(vcombine_u16(v, v))), 0);
1018}
1019template<>
1020inline void storeP<QRgba64>(QRgba64 &p, const uint16x4_t &v)
1021{
1022 vst1_u16((uint16_t *)&p, v);
1023}
1024
1025template<typename D, typename S,
1026 typename = std::enable_if_t<!std::is_same_v<D, QRgbaFloat32>, void>>
1027static void storePremultiplied(D *dst, const S *src, const QColorVector *buffer, const qsizetype len,
1028 const QColorTransformPrivate *d_ptr)
1029{
1030 const float iFF00 = 1.0f / (255 * 256);
1031 constexpr bool isARGB = isArgb<D>();
1032 for (qsizetype i = 0; i < len; ++i) {
1033 const int a = getAlpha<S>(src[i]);
1034 float32x4_t vf = vld1q_f32(&buffer[i].x);
1035 uint32x4_t v = vcvtq_u32_f32(vaddq_f32(vmulq_n_f32(vf, float(QColorTrcLut::Resolution)), vdupq_n_f32(0.5f)));
1036 const int ridx = vgetq_lane_u32(v, 0);
1037 const int gidx = vgetq_lane_u32(v, 1);
1038 const int bidx = vgetq_lane_u32(v, 2);
1039 v = vsetq_lane_u32(d_ptr->colorSpaceOut->lut[0]->m_fromLinear[ridx], v, isARGB ? 2 : 0);
1040 v = vsetq_lane_u32(d_ptr->colorSpaceOut->lut[1]->m_fromLinear[gidx], v, 1);
1041 v = vsetq_lane_u32(d_ptr->colorSpaceOut->lut[2]->m_fromLinear[bidx], v, isARGB ? 0 : 2);
1042 vf = vcvtq_f32_u32(v);
1043 vf = vmulq_n_f32(vf, a * iFF00);
1044 vf = vaddq_f32(vf, vdupq_n_f32(0.5f));
1045 v = vcvtq_u32_f32(vf);
1046 uint16x4_t v16 = vmovn_u32(v);
1047 v16 = vset_lane_u16(a, v16, 3);
1048 storeP<D>(dst[i], v16);
1049 }
1050}
1051
1052template<typename T>
1053static inline void storePU(T &p, uint16x4_t &v, int a);
1054template<>
1055inline void storePU<QRgb>(QRgb &p, uint16x4_t &v, int a)
1056{
1057 v = vadd_u16(v, vdup_n_u16(0x80));
1058 v = vshr_n_u16(v, 8);
1059 v = vset_lane_u16(a, v, 3);
1060 p = vget_lane_u32(vreinterpret_u32_u8(vmovn_u16(vcombine_u16(v, v))), 0);
1061}
1062template<>
1063inline void storePU<QRgba64>(QRgba64 &p, uint16x4_t &v, int a)
1064{
1065 v = vadd_u16(v, vshr_n_u16(v, 8));
1066 v = vset_lane_u16(a, v, 3);
1067 vst1_u16((uint16_t *)&p, v);
1068}
1069
1070template<typename D, typename S,
1071 typename = std::enable_if_t<!std::is_same_v<D, QRgbaFloat32>, void>>
1072static void storeUnpremultiplied(D *dst, const S *src, const QColorVector *buffer, const qsizetype len,
1073 const QColorTransformPrivate *d_ptr)
1074{
1075 constexpr bool isARGB = isArgb<D>();
1076 for (qsizetype i = 0; i < len; ++i) {
1077 const int a = getAlpha<S>(src[i]);
1078 float32x4_t vf = vld1q_f32(&buffer[i].x);
1079 uint16x4_t v = vmovn_u32(vcvtq_u32_f32(vaddq_f32(vmulq_n_f32(vf, float(QColorTrcLut::Resolution)), vdupq_n_f32(0.5f))));
1080 const int ridx = vget_lane_u16(v, 0);
1081 const int gidx = vget_lane_u16(v, 1);
1082 const int bidx = vget_lane_u16(v, 2);
1083 v = vset_lane_u16(d_ptr->colorSpaceOut->lut[0]->m_fromLinear[ridx], v, isARGB ? 2 : 0);
1084 v = vset_lane_u16(d_ptr->colorSpaceOut->lut[1]->m_fromLinear[gidx], v, 1);
1085 v = vset_lane_u16(d_ptr->colorSpaceOut->lut[2]->m_fromLinear[bidx], v, isARGB ? 0 : 2);
1086 storePU<D>(dst[i], v, a);
1087 }
1088}
1089
1090template<typename T>
1091static void storeOpaque(T *dst, const QColorVector *buffer, const qsizetype len,
1092 const QColorTransformPrivate *d_ptr)
1093{
1094 constexpr bool isARGB = isArgb<T>();
1095 for (qsizetype i = 0; i < len; ++i) {
1096 float32x4_t vf = vld1q_f32(&buffer[i].x);
1097 uint16x4_t v = vmovn_u32(vcvtq_u32_f32(vaddq_f32(vmulq_n_f32(vf, float(QColorTrcLut::Resolution)), vdupq_n_f32(0.5f))));
1098 const int ridx = vget_lane_u16(v, 0);
1099 const int gidx = vget_lane_u16(v, 1);
1100 const int bidx = vget_lane_u16(v, 2);
1101 v = vset_lane_u16(d_ptr->colorSpaceOut->lut[0]->m_fromLinear[ridx], v, isARGB ? 2 : 0);
1102 v = vset_lane_u16(d_ptr->colorSpaceOut->lut[1]->m_fromLinear[gidx], v, 1);
1103 v = vset_lane_u16(d_ptr->colorSpaceOut->lut[2]->m_fromLinear[bidx], v, isARGB ? 0 : 2);
1104 storePU<T>(dst[i], v, isARGB ? 255 : 0xffff);
1105 }
1106}
1107#else
1108static void storePremultiplied(QRgb *dst, const QRgb *src, const QColorVector *buffer, const qsizetype len,
1109 const QColorTransformPrivate *d_ptr)
1110{
1111 for (qsizetype i = 0; i < len; ++i) {
1112 const int a = qAlpha(src[i]);
1113 const float fa = a / (255.0f * 256.0f);
1114 const float r = d_ptr->colorSpaceOut->lut[0]->m_fromLinear[int(buffer[i].x * float(QColorTrcLut::Resolution) + 0.5f)];
1115 const float g = d_ptr->colorSpaceOut->lut[1]->m_fromLinear[int(buffer[i].y * float(QColorTrcLut::Resolution) + 0.5f)];
1116 const float b = d_ptr->colorSpaceOut->lut[2]->m_fromLinear[int(buffer[i].z * float(QColorTrcLut::Resolution) + 0.5f)];
1117 dst[i] = qRgba(r * fa + 0.5f, g * fa + 0.5f, b * fa + 0.5f, a);
1118 }
1119}
1120
1122 const QColorTransformPrivate *d_ptr)
1123{
1124 for (qsizetype i = 0; i < len; ++i) {
1125 const int r = d_ptr->colorSpaceOut->lut[0]->u8FromLinearF32(buffer[i].x);
1126 const int g = d_ptr->colorSpaceOut->lut[1]->u8FromLinearF32(buffer[i].y);
1127 const int b = d_ptr->colorSpaceOut->lut[2]->u8FromLinearF32(buffer[i].z);
1128 dst[i] = (src[i] & 0xff000000) | (r << 16) | (g << 8) | (b << 0);
1129 }
1130}
1131
1133 const QColorTransformPrivate *d_ptr)
1134{
1135 for (qsizetype i = 0; i < len; ++i) {
1136 const int r = d_ptr->colorSpaceOut->lut[0]->u8FromLinearF32(buffer[i].x);
1137 const int g = d_ptr->colorSpaceOut->lut[1]->u8FromLinearF32(buffer[i].y);
1138 const int b = d_ptr->colorSpaceOut->lut[2]->u8FromLinearF32(buffer[i].z);
1139 dst[i] = 0xff000000 | (r << 16) | (g << 8) | (b << 0);
1140 }
1141}
1142
1143template<typename S>
1144static void storePremultiplied(QRgba64 *dst, const S *src, const QColorVector *buffer, const qsizetype len,
1145 const QColorTransformPrivate *d_ptr)
1146{
1147 for (qsizetype i = 0; i < len; ++i) {
1148 const int a = getAlphaF(src[i]) * 65535.f;
1149 const float fa = a / (255.0f * 256.0f);
1150 const float r = d_ptr->colorSpaceOut->lut[0]->m_fromLinear[int(buffer[i].x * float(QColorTrcLut::Resolution) + 0.5f)];
1151 const float g = d_ptr->colorSpaceOut->lut[1]->m_fromLinear[int(buffer[i].y * float(QColorTrcLut::Resolution) + 0.5f)];
1152 const float b = d_ptr->colorSpaceOut->lut[2]->m_fromLinear[int(buffer[i].z * float(QColorTrcLut::Resolution) + 0.5f)];
1153 dst[i] = qRgba64(r * fa + 0.5f, g * fa + 0.5f, b * fa + 0.5f, a);
1154 }
1155}
1156
1157template<typename S>
1158static void storeUnpremultiplied(QRgba64 *dst, const S *src, const QColorVector *buffer, const qsizetype len,
1159 const QColorTransformPrivate *d_ptr)
1160{
1161 for (qsizetype i = 0; i < len; ++i) {
1162 const int a = getAlphaF(src[i]) * 65535.f;
1163 const int r = d_ptr->colorSpaceOut->lut[0]->u16FromLinearF32(buffer[i].x);
1164 const int g = d_ptr->colorSpaceOut->lut[1]->u16FromLinearF32(buffer[i].y);
1165 const int b = d_ptr->colorSpaceOut->lut[2]->u16FromLinearF32(buffer[i].z);
1166 dst[i] = qRgba64(r, g, b, a);
1167 }
1168}
1169
1171 const QColorTransformPrivate *d_ptr)
1172{
1173 for (qsizetype i = 0; i < len; ++i) {
1174 const int r = d_ptr->colorSpaceOut->lut[0]->u16FromLinearF32(buffer[i].x);
1175 const int g = d_ptr->colorSpaceOut->lut[1]->u16FromLinearF32(buffer[i].y);
1176 const int b = d_ptr->colorSpaceOut->lut[2]->u16FromLinearF32(buffer[i].z);
1177 dst[i] = qRgba64(r, g, b, 0xFFFF);
1178 }
1179}
1180#endif
1181#if !defined(__SSE2__)
1182template<typename S>
1184 const qsizetype len, const QColorTransformPrivate *d_ptr)
1185{
1186 for (qsizetype i = 0; i < len; ++i) {
1187 const float a = getAlphaF(src[i]);
1188 dst[i].r = d_ptr->colorSpaceOut->trc[0].applyInverseExtended(buffer[i].x) * a;
1189 dst[i].g = d_ptr->colorSpaceOut->trc[1].applyInverseExtended(buffer[i].y) * a;
1190 dst[i].b = d_ptr->colorSpaceOut->trc[2].applyInverseExtended(buffer[i].z) * a;
1191 dst[i].a = a;
1192 }
1193}
1194
1195template<typename S>
1197 const qsizetype len, const QColorTransformPrivate *d_ptr)
1198{
1199 for (qsizetype i = 0; i < len; ++i) {
1200 const float a = getAlphaF(src[i]);
1201 dst[i].r = d_ptr->colorSpaceOut->trc[0].applyInverseExtended(buffer[i].x);
1202 dst[i].g = d_ptr->colorSpaceOut->trc[1].applyInverseExtended(buffer[i].y);
1203 dst[i].b = d_ptr->colorSpaceOut->trc[2].applyInverseExtended(buffer[i].z);
1204 dst[i].a = a;
1205 }
1206}
1207
1209 const QColorTransformPrivate *d_ptr)
1210{
1211 for (qsizetype i = 0; i < len; ++i) {
1212 dst[i].r = d_ptr->colorSpaceOut->trc[0].applyInverseExtended(buffer[i].x);
1213 dst[i].g = d_ptr->colorSpaceOut->trc[1].applyInverseExtended(buffer[i].y);
1214 dst[i].b = d_ptr->colorSpaceOut->trc[2].applyInverseExtended(buffer[i].z);
1215 dst[i].a = 1.0f;
1216 }
1217}
1218#endif
1219
1220static void loadGray(QColorVector *buffer, const quint8 *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
1221{
1222 for (qsizetype i = 0; i < len; ++i) {
1223 const float y = d_ptr->colorSpaceIn->lut[0]->u8ToLinearF32(src[i]);
1224 buffer[i] = d_ptr->colorSpaceIn->whitePoint * y;
1225 }
1226}
1227
1228static void loadGray(QColorVector *buffer, const quint16 *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
1229{
1230 for (qsizetype i = 0; i < len; ++i) {
1231 const float y = d_ptr->colorSpaceIn->lut[0]->u16ToLinearF32(src[i]);
1232 buffer[i] = d_ptr->colorSpaceIn->whitePoint * y;
1233 }
1234}
1235
1237 const QColorTransformPrivate *d_ptr)
1238{
1239 for (qsizetype i = 0; i < len; ++i)
1240 dst[i] = d_ptr->colorSpaceOut->lut[0]->u8FromLinearF32(buffer[i].y);
1241}
1242
1244 const QColorTransformPrivate *d_ptr)
1245{
1246 for (qsizetype i = 0; i < len; ++i)
1247 dst[i] = d_ptr->colorSpaceOut->lut[0]->u16FromLinearF32(buffer[i].y);
1248}
1249
1250static constexpr qsizetype WorkBlockSize = 256;
1251
1252template <typename T, int Count = 1>
1254{
1255public:
1256 operator T*() { return reinterpret_cast<T *>(this); }
1257private:
1258 alignas(T) char data[sizeof(T) * Count];
1259};
1260
1262{
1263 const float f = 1.0f / 255.f;
1264 for (qsizetype i = 0; i < len; ++i) {
1265 const uint p = src[i];
1266 buffer[i].x = qRed(p) * f;
1267 buffer[i].y = qGreen(p) * f;
1268 buffer[i].z = qBlue(p) * f;
1269 }
1270}
1271
1273{
1274 const float f = 1.0f / 255.f;
1275 for (qsizetype i = 0; i < len; ++i) {
1276 const QCmyk32 p = src[i];
1277 buffer[i].x = (p.cyan() * f);
1278 buffer[i].y = (p.magenta() * f);
1279 buffer[i].z = (p.yellow() * f);
1280 buffer[i].w = (p.black() * f);
1281 }
1282}
1283
1285{
1286 const float f = 1.0f / 65535.f;
1287 for (qsizetype i = 0; i < len; ++i) {
1288 buffer[i].x = src[i].red() * f;
1289 buffer[i].y = src[i].green() * f;
1290 buffer[i].z = src[i].blue() * f;
1291 }
1292}
1293
1295{
1296 for (qsizetype i = 0; i < len; ++i) {
1297 buffer[i].x = src[i].r;
1298 buffer[i].y = src[i].g;
1299 buffer[i].z = src[i].b;
1300 }
1301}
1302
1304{
1305 for (qsizetype i = 0; i < len; ++i) {
1306 const uint p = src[i];
1307 const float f = 1.0f / qAlpha(p);
1308 buffer[i].x = (qRed(p) * f);
1309 buffer[i].y = (qGreen(p) * f);
1310 buffer[i].z = (qBlue(p) * f);
1311 }
1312}
1313
1315{
1316 Q_UNREACHABLE();
1317}
1318
1320{
1321 for (qsizetype i = 0; i < len; ++i) {
1322 const float f = 1.0f / src[i].alpha();
1323 buffer[i].x = (src[i].red() * f);
1324 buffer[i].y = (src[i].green() * f);
1325 buffer[i].z = (src[i].blue() * f);
1326 }
1327}
1328
1330{
1331 for (qsizetype i = 0; i < len; ++i) {
1332 const float f = 1.0f / src[i].a;
1333 buffer[i].x = src[i].r * f;
1334 buffer[i].y = src[i].g * f;
1335 buffer[i].z = src[i].b * f;
1336 }
1337}
1338template<typename T>
1339static void storeUnpremultipliedLUT(QRgb *dst, const T *, const QColorVector *buffer, const qsizetype len)
1340{
1341 for (qsizetype i = 0; i < len; ++i) {
1342 const int r = buffer[i].x * 255.f;
1343 const int g = buffer[i].y * 255.f;
1344 const int b = buffer[i].z * 255.f;
1345 dst[i] = 0xff000000 | (r << 16) | (g << 8) | (b << 0);
1346 }
1347}
1348
1349template<>
1351{
1352 for (qsizetype i = 0; i < len; ++i) {
1353 const int r = buffer[i].x * 255.f;
1354 const int g = buffer[i].y * 255.f;
1355 const int b = buffer[i].z * 255.f;
1356 dst[i] = (src[i] & 0xff000000) | (r << 16) | (g << 8) | (b << 0);
1357 }
1358}
1359
1360
1361template<typename T>
1363{
1364 for (qsizetype i = 0; i < len; ++i) {
1365 const int c = buffer[i].x * 255.f;
1366 const int m = buffer[i].y * 255.f;
1367 const int y = buffer[i].z * 255.f;
1368 const int k = buffer[i].w * 255.f;
1369 dst[i] = QCmyk32(c, m, y, k);
1370 }
1371}
1372
1373template<typename T>
1374static void storeUnpremultipliedLUT(QRgba64 *dst, const T *,
1375 const QColorVector *buffer, const qsizetype len)
1376{
1377 for (qsizetype i = 0; i < len; ++i) {
1378 const int r = buffer[i].x * 65535.f;
1379 const int g = buffer[i].y * 65535.f;
1380 const int b = buffer[i].z * 65535.f;
1381 dst[i] = qRgba64(r, g, b, 65535);
1382 }
1383}
1384
1385template<>
1387 const QColorVector *buffer, const qsizetype len)
1388{
1389 for (qsizetype i = 0; i < len; ++i) {
1390 const int a = qAlpha(src[i]) * 257;
1391 const int r = buffer[i].x * 65535.f;
1392 const int g = buffer[i].y * 65535.f;
1393 const int b = buffer[i].z * 65535.f;
1394 dst[i] = qRgba64(r, g, b, a);
1395 }
1396}
1397
1398template<>
1400 const QColorVector *buffer, const qsizetype len)
1401{
1402 for (qsizetype i = 0; i < len; ++i) {
1403 const int r = buffer[i].x * 65535.f;
1404 const int g = buffer[i].y * 65535.f;
1405 const int b = buffer[i].z * 65535.f;
1406 dst[i] = qRgba64(r, g, b, src[i].alpha());
1407 }
1408}
1409
1410template<typename T>
1412 const QColorVector *buffer, const qsizetype len)
1413{
1414 for (qsizetype i = 0; i < len; ++i) {
1415 const float r = buffer[i].x;
1416 const float g = buffer[i].y;
1417 const float b = buffer[i].z;
1418 dst[i] = QRgbaFloat32{r, g, b, getAlphaF(src[i])};
1419 }
1420}
1421
1422template<typename T>
1423static void storePremultipliedLUT(QRgb *, const T *, const QColorVector *, const qsizetype);
1424
1425template<>
1427{
1428 for (qsizetype i = 0; i < len; ++i) {
1429 const int a = qAlpha(src[i]);
1430 const int r = buffer[i].x * a;
1431 const int g = buffer[i].y * a;
1432 const int b = buffer[i].z * a;
1433 dst[i] = (src[i] & 0xff000000) | (r << 16) | (g << 8) | (b << 0);
1434 }
1435}
1436
1437template<>
1439{
1440 for (qsizetype i = 0; i < len; ++i) {
1441 const int r = buffer[i].x * 255.f;
1442 const int g = buffer[i].y * 255.f;
1443 const int b = buffer[i].z * 255.f;
1444 dst[i] = 0xff000000 | (r << 16) | (g << 8) | (b << 0);
1445 }
1446}
1447
1448
1449template<typename T>
1450static void storePremultipliedLUT(QCmyk32 *dst, const T *src, const QColorVector *buffer, const qsizetype len)
1451{
1453}
1454
1455template<typename T>
1456static void storePremultipliedLUT(QRgba64 *, const T *, const QColorVector *, const qsizetype);
1457
1458template<>
1460{
1461 for (qsizetype i = 0; i < len; ++i) {
1462 const int a = qAlpha(src[i]) * 257;
1463 const int r = buffer[i].x * a;
1464 const int g = buffer[i].y * a;
1465 const int b = buffer[i].z * a;
1466 dst[i] = qRgba64(r, g, b, a);
1467 }
1468}
1469
1470template<>
1472{
1473 for (qsizetype i = 0; i < len; ++i) {
1474 const int r = buffer[i].x * 65535.f;
1475 const int g = buffer[i].y * 65535.f;
1476 const int b = buffer[i].z * 65535.f;
1477 dst[i] = qRgba64(r, g, b, 65535);
1478 }
1479}
1480
1481template<>
1483{
1484 for (qsizetype i = 0; i < len; ++i) {
1485 const int a = src[i].alpha();
1486 const int r = buffer[i].x * a;
1487 const int g = buffer[i].y * a;
1488 const int b = buffer[i].z * a;
1489 dst[i] = qRgba64(r, g, b, a);
1490 }
1491}
1492
1493template<typename T>
1495{
1496 for (qsizetype i = 0; i < len; ++i) {
1497 const float a = getAlphaF(src[i]);
1498 const float r = buffer[i].x * a;
1499 const float g = buffer[i].y * a;
1500 const float b = buffer[i].z * a;
1501 dst[i] = QRgbaFloat32{r, g, b, a};
1502 }
1503}
1504
1506{
1507 const bool doW = element.trc[3].isValid();
1508 for (qsizetype i = 0; i < len; ++i) {
1509 buffer[i].x = element.trc[0].apply(buffer[i].x);
1510 buffer[i].y = element.trc[1].apply(buffer[i].y);
1511 buffer[i].z = element.trc[2].apply(buffer[i].z);
1512 if (doW)
1513 buffer[i].w = element.trc[3].apply(buffer[i].w);
1514 }
1515}
1516
1517static void visitElement(const QColorMatrix &element, QColorVector *buffer, const qsizetype len)
1518{
1519 for (qsizetype i = 0; i < len; ++i)
1520 buffer[i] = element.map(buffer[i]);
1521}
1522
1524{
1525 for (qsizetype i = 0; i < len; ++i)
1526 buffer[i] += offset;
1527}
1528
1529static void visitElement(const QColorCLUT &element, QColorVector *buffer, const qsizetype len)
1530{
1531 if (element.isEmpty())
1532 return;
1533 for (qsizetype i = 0; i < len; ++i)
1534 buffer[i] = element.apply(buffer[i]);
1535}
1536
1541{
1543 if (colorSpaceIn->lut.generated.loadAcquire()) {
1544 c.x = colorSpaceIn->lut[0]->toLinear(c.x);
1545 c.y = colorSpaceIn->lut[1]->toLinear(c.y);
1546 c.z = colorSpaceIn->lut[2]->toLinear(c.z);
1547 } else {
1548 c.x = colorSpaceIn->trc[0].apply(c.x);
1549 c.y = colorSpaceIn->trc[1].apply(c.y);
1550 c.z = colorSpaceIn->trc[2].apply(c.z);
1551 }
1552 c = colorMatrix.map(c);
1553 } else {
1554 // Do element based conversion
1555 for (auto &&element : colorSpaceIn->mAB)
1556 std::visit([&c](auto &&elm) { visitElement(elm, &c, 1); }, element);
1557 }
1558 c.x = std::clamp(c.x, 0.0f, 1.0f);
1559 c.y = std::clamp(c.y, 0.0f, 1.0f);
1560 c.z = std::clamp(c.z, 0.0f, 1.0f);
1561
1562 // Match Profile Connection Spaces (PCS):
1564 c = c.xyzToLab();
1566 c = c.labToXyz();
1567
1570 c = colorMatrix.map(c);
1571 c.x = std::clamp(c.x, 0.0f, 1.0f);
1572 c.y = std::clamp(c.y, 0.0f, 1.0f);
1573 c.z = std::clamp(c.z, 0.0f, 1.0f);
1574 }
1575 if (colorSpaceOut->lut.generated.loadAcquire()) {
1576 c.x = colorSpaceOut->lut[0]->fromLinear(c.x);
1577 c.y = colorSpaceOut->lut[1]->fromLinear(c.y);
1578 c.z = colorSpaceOut->lut[2]->fromLinear(c.z);
1579 } else {
1580 c.x = colorSpaceOut->trc[0].applyInverse(c.x);
1581 c.y = colorSpaceOut->trc[1].applyInverse(c.y);
1582 c.z = colorSpaceOut->trc[2].applyInverse(c.z);
1583 }
1584 } else {
1585 // Do element based conversion
1586 for (auto &&element : colorSpaceOut->mBA)
1587 std::visit([&c](auto &&elm) { visitElement(elm, &c, 1); }, element);
1588 c.x = std::clamp(c.x, 0.0f, 1.0f);
1589 c.y = std::clamp(c.y, 0.0f, 1.0f);
1590 c.z = std::clamp(c.z, 0.0f, 1.0f);
1591 }
1592 return c;
1593}
1594
1599{
1601 c.x = colorSpaceIn->trc[0].applyExtended(c.x);
1602 c.y = colorSpaceIn->trc[1].applyExtended(c.y);
1603 c.z = colorSpaceIn->trc[2].applyExtended(c.z);
1604 c = colorMatrix.map(c);
1605 } else {
1606 // Do element based conversion
1607 for (auto &&element : colorSpaceIn->mAB)
1608 std::visit([&c](auto &&elm) { visitElement(elm, &c, 1); }, element);
1609 }
1610
1611 // Match Profile Connection Spaces (PCS):
1613 c = c.xyzToLab();
1615 c = c.labToXyz();
1616
1619 c = colorMatrix.map(c);
1623 } else {
1624 // Do element based conversion
1625 for (auto &&element : colorSpaceOut->mBA)
1626 std::visit([&c](auto &&elm) { visitElement(elm, &c, 1); }, element);
1627 }
1628 return c;
1629}
1630
1631template<typename S>
1632void QColorTransformPrivate::applyConvertIn(const S *src, QColorVector *buffer, qsizetype len, TransformFlags flags) const
1633{
1634 // Avoid compiling this part for S=QCmyk32:
1635 if constexpr (!std::is_same_v<S, QCmyk32>) {
1639 else
1641
1643 applyMatrix<DoClamp>(buffer, len, colorSpaceIn->toXyz);
1644 return;
1645 }
1646 }
1648
1651 else
1653
1654 if constexpr (std::is_same_v<S, QRgbaFloat16> || std::is_same_v<S, QRgbaFloat32>)
1655 clampIfNeeded<DoClamp>(buffer, len);
1656
1657 // Do element based conversion
1658 for (auto &&element : colorSpaceIn->mAB)
1659 std::visit([&buffer, len](auto &&elm) { visitElement(elm, buffer, len); }, element);
1660}
1661
1662template<typename D, typename S>
1663void QColorTransformPrivate::applyConvertOut(D *dst, const S *src, QColorVector *buffer, qsizetype len, TransformFlags flags) const
1664{
1665 constexpr ApplyMatrixForm doClamp = (std::is_same_v<D, QRgbaFloat16> || std::is_same_v<D, QRgbaFloat32>) ? DoNotClamp : DoClamp;
1666 // Avoid compiling this part for D=QCmyk32:
1667 if constexpr (!std::is_same_v<D, QCmyk32>) {
1669 applyMatrix<doClamp>(buffer, len, colorMatrix);
1670
1671 if constexpr (std::is_same_v<S, QCmyk32>) {
1672 storeOpaque(dst, buffer, len, this);
1673 } else {
1674 if (flags & InputOpaque)
1675 storeOpaque(dst, buffer, len, this);
1676 else if (flags & OutputPremultiplied)
1678 else
1680 }
1681 return;
1682 }
1683 }
1685
1686 // Do element based conversion
1687 for (auto &&element : colorSpaceOut->mBA)
1688 std::visit([&buffer, len](auto &&elm) { visitElement(elm, buffer, len); }, element);
1689
1690 clampIfNeeded<doClamp>(buffer, len);
1691
1694 else
1696}
1697
1702void QColorTransformPrivate::pcsAdapt(QColorVector *buffer, qsizetype count) const
1703{
1704 // Match Profile Connection Spaces (PCS):
1706 for (qsizetype j = 0; j < count; ++j)
1707 buffer[j] = buffer[j].xyzToLab();
1708 } else if (colorSpaceIn->isPcsLab && !colorSpaceOut->isPcsLab) {
1709 for (qsizetype j = 0; j < count; ++j)
1710 buffer[j] = buffer[j].labToXyz();
1711 }
1712}
1713
1723template<typename D, typename S>
1724void QColorTransformPrivate::apply(D *dst, const S *src, qsizetype count, TransformFlags flags) const
1725{
1727 updateLutsIn();
1729 updateLutsOut();
1730
1731 QUninitialized<QColorVector, WorkBlockSize> buffer;
1732 qsizetype i = 0;
1733 while (i < count) {
1734 const qsizetype len = qMin(count - i, WorkBlockSize);
1735
1736 applyConvertIn(src + i, buffer, len, flags);
1737
1738 pcsAdapt(buffer, len);
1739
1740 applyConvertOut(dst + i, src + i, buffer, len, flags);
1741
1742 i += len;
1743 }
1744}
1745
1751template<typename D, typename S>
1752void QColorTransformPrivate::applyReturnGray(D *dst, const S *src, qsizetype count, TransformFlags flags) const
1753{
1755 updateLutsOut();
1757 QUninitialized<QColorVector, WorkBlockSize> buffer;
1758
1759 qsizetype i = 0;
1760 while (i < count) {
1761 const qsizetype len = qMin(count - i, WorkBlockSize);
1762
1763 applyConvertIn(src, buffer, len, flags);
1764
1765 // Match Profile Connection Spaces (PCS):
1767 for (qsizetype j = 0; j < len; ++j)
1768 buffer[j] = buffer[j].xyzToLab();
1769 } else if (colorSpaceIn->isPcsLab && !colorSpaceOut->isPcsLab) {
1770 for (qsizetype j = 0; j < len; ++j)
1771 buffer[j] = buffer[j].labToXyz();
1772 }
1773
1774 applyMatrix<DoClamp>(buffer, len, colorMatrix);
1775 storeOpaque(dst + i, buffer, len, this);
1776
1777 i += len;
1778 }
1779 return;
1780 }
1781 if constexpr (!std::is_same_v<S, QCmyk32>) {
1782 if (!colorMatrix.isValid())
1783 return;
1784
1785 updateLutsIn();
1786
1787 QUninitialized<QColorVector, WorkBlockSize> buffer;
1788
1789 qsizetype i = 0;
1790 while (i < count) {
1791 const qsizetype len = qMin(count - i, WorkBlockSize);
1793 loadPremultiplied(buffer, src + i, len, this);
1794 else
1795 loadUnpremultiplied(buffer, src + i, len, this);
1796
1797 applyMatrix<DoClamp>(buffer, len, colorMatrix);
1798
1799 storeOpaque(dst + i, buffer, len, this);
1800
1801 i += len;
1802 }
1803 } else {
1804 Q_UNREACHABLE();
1805 }
1806}
1807
1811template<typename D, typename S>
1812void QColorTransformPrivate::applyGray(D *dst, const S *src, qsizetype count, TransformFlags) const
1813{
1815 updateLutsIn();
1816 if constexpr (std::is_same_v<D, QRgb> || std::is_same_v<D, QRgba64> || std::is_same_v<D, QRgbaFloat32> || std::is_same_v<D, QCmyk32>) {
1818 QUninitialized<QColorVector, WorkBlockSize> buffer;
1819
1820 qsizetype i = 0;
1821 while (i < count) {
1822 const qsizetype len = qMin(count - i, WorkBlockSize);
1823 loadGray(buffer, src + i, len, this);
1824
1825 applyMatrix<DoClamp>(buffer, len, colorMatrix);
1826
1827 // Match Profile Connection Spaces (PCS):
1829 for (qsizetype j = 0; j < len; ++j)
1830 buffer[j] = buffer[j].xyzToLab();
1831 } else if (colorSpaceIn->isPcsLab && !colorSpaceOut->isPcsLab) {
1832 for (qsizetype j = 0; j < len; ++j)
1833 buffer[j] = buffer[j].labToXyz();
1834 }
1835
1836 // Do element based conversion
1837 for (auto &&element : colorSpaceOut->mBA)
1838 std::visit([&buffer, len](auto &&elm) { visitElement(elm, buffer, len); }, element);
1839
1840 clampIfNeeded<DoClamp>(buffer, len);
1841
1842 storeUnpremultipliedLUT(dst, src, buffer, len); // input is always opaque
1843
1844 i += len;
1845 }
1846 return;
1847 }
1848 }
1850 if constexpr (!std::is_same_v<D, QCmyk32>) {
1851 if (!colorMatrix.isValid())
1852 return;
1853
1854 updateLutsOut();
1855
1856 QUninitialized<QColorVector, WorkBlockSize> buffer;
1857
1858 qsizetype i = 0;
1859 while (i < count) {
1860 const qsizetype len = qMin(count - i, WorkBlockSize);
1861 loadGray(buffer, src + i, len, this);
1862
1863 applyMatrix<DoClamp>(buffer, len, colorMatrix);
1864
1865 storeOpaque(dst + i, buffer, len, this);
1866 i += len;
1867 }
1868 } else {
1869 Q_UNREACHABLE();
1870 }
1871}
1872
1900
1901// Only allow versions increasing precision
1902template void QColorTransformPrivate::applyReturnGray<quint8, QRgb>(quint8 *dst, const QRgb *src, qsizetype count, TransformFlags flags) const;
1903template void QColorTransformPrivate::applyReturnGray<quint8, QCmyk32>(quint8 *dst, const QCmyk32 *src, qsizetype count, TransformFlags flags) const;
1904template void QColorTransformPrivate::applyReturnGray<quint16, QCmyk32>(quint16 *dst, const QCmyk32 *src, qsizetype count, TransformFlags flags) const;
1905template void QColorTransformPrivate::applyReturnGray<quint16, QRgba64>(quint16 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags) const;
1906template void QColorTransformPrivate::applyGray<quint8, quint8>(quint8 *dst, const quint8 *src, qsizetype count, TransformFlags flags) const;
1907template void QColorTransformPrivate::applyGray<quint16, quint8>(quint16 *dst, const quint8 *src, qsizetype count, TransformFlags flags) const;
1908template void QColorTransformPrivate::applyGray<quint16, quint16>(quint16 *dst, const quint16 *src, qsizetype count, TransformFlags flags) const;
1909template void QColorTransformPrivate::applyGray<QRgb, quint8>(QRgb *dst, const quint8 *src, qsizetype count, TransformFlags flags) const;
1910template void QColorTransformPrivate::applyGray<QCmyk32, quint8>(QCmyk32 *dst, const quint8 *src, qsizetype count, TransformFlags flags) const;
1911template void QColorTransformPrivate::applyGray<QCmyk32, quint16>(QCmyk32 *dst, const quint16 *src, qsizetype count, TransformFlags flags) const;
1912template void QColorTransformPrivate::applyGray<QRgba64, quint16>(QRgba64 *dst, const quint16 *src, qsizetype count, TransformFlags flags) const;
1913
1914template void QColorTransformPrivate::apply<QRgb, QRgb>(QRgb *dst, const QRgb *src, qsizetype count, TransformFlags flags) const;
1915template void QColorTransformPrivate::apply<QRgb, QCmyk32>(QRgb *dst, const QCmyk32 *src, qsizetype count, TransformFlags flags) const;
1916template void QColorTransformPrivate::apply<QCmyk32, QRgb>(QCmyk32 *dst, const QRgb *src, qsizetype count, TransformFlags flags) const;
1917template void QColorTransformPrivate::apply<QCmyk32, QCmyk32>(QCmyk32 *dst, const QCmyk32 *src, qsizetype count, TransformFlags flags) const;
1918template void QColorTransformPrivate::apply<QCmyk32, QRgba64>(QCmyk32 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags) const;
1919template void QColorTransformPrivate::apply<QCmyk32, QRgbaFloat32>(QCmyk32 *dst, const QRgbaFloat32 *src, qsizetype count, TransformFlags flags) const;
1920template void QColorTransformPrivate::apply<QRgba64, QRgb>(QRgba64 *dst, const QRgb *src, qsizetype count, TransformFlags flags) const;
1921template void QColorTransformPrivate::apply<QRgba64, QCmyk32>(QRgba64 *dst, const QCmyk32 *src, qsizetype count, TransformFlags flags) const;
1922template void QColorTransformPrivate::apply<QRgba64, QRgba64>(QRgba64 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags) const;
1923template void QColorTransformPrivate::apply<QRgbaFloat32, QRgb>(QRgbaFloat32 *dst, const QRgb *src, qsizetype count, TransformFlags flags) const;
1924template void QColorTransformPrivate::apply<QRgbaFloat32, QCmyk32>(QRgbaFloat32 *dst, const QCmyk32 *src, qsizetype count, TransformFlags flags) const;
1925template void QColorTransformPrivate::apply<QRgbaFloat32, QRgba64>(QRgbaFloat32 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags) const;
1926template void QColorTransformPrivate::apply<QRgbaFloat32, QRgbaFloat32>(QRgbaFloat32 *dst, const QRgbaFloat32 *src, qsizetype count, TransformFlags flags) const;
1927
1932{
1934 return true;
1935 if (!colorMatrix.isIdentity())
1936 return false;
1937 if (colorSpaceIn && colorSpaceOut) {
1939 return true;
1941 return false;
1943 return false;
1945 return colorSpaceIn->trc[0] == colorSpaceOut->trc[0]
1946 && colorSpaceIn->trc[1] == colorSpaceOut->trc[1]
1947 && colorSpaceIn->trc[2] == colorSpaceOut->trc[2];
1948 }
1949 } else {
1951 return false;
1953 return false;
1955 return false;
1957 return false;
1958 }
1959 return true;
1960}
1961
QColorVector apply(const QColorVector &v) const
bool isEmpty() const
bool isValid() const
QColorVector g
QColorVector b
QColorVector r
bool isIdentity() const noexcept
QColorVector map(const QColorVector &c) const
QColorVector whitePoint
QList< Element > mAB
QList< Element > mBA
QColorSpace::TransformModel transformModel
bool isThreeComponentMatrix() const
static Q_CONSTINIT QBasicMutex s_lutWriteLock
struct QColorSpacePrivate::LUT lut
QColorSpace::TransferFunction transferFunction
QColorSpace::ColorModel colorModel
bool equals(const QColorSpacePrivate *other) const
Q_GUI_EXPORT void prepare()
void applyReturnGray(D *dst, const S *src, qsizetype count, TransformFlags flags) const
QExplicitlySharedDataPointer< const QColorSpacePrivate > colorSpaceOut
QExplicitlySharedDataPointer< const QColorSpacePrivate > colorSpaceIn
void apply(D *dst, const S *src, qsizetype count, TransformFlags flags) const
QColorVector map(QColorVector color) const
QColorVector mapExtended(QColorVector color) const
void applyGray(D *dst, const S *src, qsizetype count, TransformFlags flags) const
The QColorTransform class is a transformation between color spaces.
Q_GUI_EXPORT QRgb map(QRgb argb) const
Applies the color transformation on the QRgb value argb.
QColorTransform() noexcept=default
Q_GUI_EXPORT bool isIdentity() const noexcept
Q_GUI_EXPORT ~QColorTransform()
static constexpr qsizetype Resolution
static std::shared_ptr< QColorTrcLut > fromTransferTable(const QColorTransferTable &transTable, Direction dir=BiLinear)
static constexpr uint32_t ShiftUp
static std::shared_ptr< QColorTrcLut > fromTransferFunction(const QColorTransferFunction &transFn, Direction dir=BiLinear)
static constexpr uint32_t ShiftDown
QColorTransferFunction m_fun
Definition qcolortrc_p.h:90
bool isValid() const
Definition qcolortrc_p.h:45
float applyInverseExtended(float x) const
Definition qcolortrc_p.h:75
Type m_type
Definition qcolortrc_p.h:89
float applyExtended(float x) const
Definition qcolortrc_p.h:57
float apply(float x) const
Definition qcolortrc_p.h:49
float applyInverse(float x) const
Definition qcolortrc_p.h:67
QColorTransferTable m_table
Definition qcolortrc_p.h:91
QColorVector labToXyz() const
QColorVector xyzToLab() const
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
@ Cmyk
Definition qcolor.h:35
@ ExtendedRgb
Definition qcolor.h:35
@ Rgb
Definition qcolor.h:35
QColor toRgb() const noexcept
Create and returns an RGB QColor based on this color.
Definition qcolor.cpp:2035
QColor toCmyk() const noexcept
Creates and returns a CMYK QColor based on this color.
Definition qcolor.cpp:2295
const T * constData() const noexcept
Returns a const pointer to the shared data object.
\inmodule QtCore
Definition qmutex.h:313
constexpr quint16 red() const
Definition qrgba64.h:70
constexpr quint16 alpha() const
Definition qrgba64.h:73
constexpr quint16 green() const
Definition qrgba64.h:71
constexpr quint16 blue() const
Definition qrgba64.h:72
static constexpr QRgba64 fromRgba64(quint64 c)
Definition qrgba64.h:36
\keyword 16-bit Floating Point Support\inmodule QtCore \inheaderfile QFloat16
Definition qfloat16.h:47
auto visit(Fn &&fn, QIODevice *socket, Args &&...args)
Combined button and popup list for selecting options.
void loadPremultiplied< QRgbaFloat32 >(QColorVector *buffer, const QRgbaFloat32 *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
static void visitElement(const QColorSpacePrivate::TransferElement &element, QColorVector *buffer, const qsizetype len)
void loadUnpremultipliedLUT(QColorVector *buffer, const QRgb *src, const qsizetype len)
static void applyMatrix(QColorVector *buffer, const qsizetype len, const QColorMatrix &colorMatrix)
static float getAlphaF(const T &)
void loadUnpremultiplied< QRgbaFloat32 >(QColorVector *buffer, const QRgbaFloat32 *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
static void storeUnpremultiplied(QRgb *dst, const QRgb *src, const QColorVector *buffer, const qsizetype len, const QColorTransformPrivate *d_ptr)
QT_BEGIN_NAMESPACE std::shared_ptr< QColorTrcLut > lutFromTrc(const QColorTrc &trc)
static void loadPremultiplied(QColorVector *buffer, const T *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
static void storeUnpremultipliedLUT(QRgb *dst, const T *, const QColorVector *buffer, const qsizetype len)
static void clampIfNeeded(QColorVector *buffer, const qsizetype len)
static void loadUnpremultiplied(QColorVector *buffer, const T *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
ApplyMatrixForm
@ DoNotClamp
@ DoClamp
static constexpr qsizetype WorkBlockSize
static void loadGray(QColorVector *buffer, const quint8 *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
static void storePremultiplied(QRgb *dst, const QRgb *src, const QColorVector *buffer, const qsizetype len, const QColorTransformPrivate *d_ptr)
void loadUnpremultiplied< QRgba64 >(QColorVector *buffer, const QRgba64 *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
void loadUnpremultiplied< QRgb >(QColorVector *buffer, const QRgb *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
static void storeOpaque(QRgb *dst, const QColorVector *buffer, const qsizetype len, const QColorTransformPrivate *d_ptr)
void loadPremultiplied< QRgba64 >(QColorVector *buffer, const QRgba64 *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
void loadPremultipliedLUT(QColorVector *buffer, const QRgb *src, const qsizetype len)
static void storePremultipliedLUT(QRgb *, const T *, const QColorVector *, const qsizetype)
void loadPremultiplied< QRgb >(QColorVector *buffer, const QRgb *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
#define qWarning
Definition qlogging.h:166
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * v
[13]
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat z
GLint GLint GLint GLint GLint x
[0]
const GLfloat * m
GLfloat GLfloat GLfloat w
[0]
GLboolean GLboolean GLboolean GLboolean a
[7]
GLboolean r
[2]
GLenum GLenum GLsizei count
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
GLenum src
GLenum GLuint buffer
GLuint color
[2]
GLenum GLenum dst
GLbitfield flags
GLenum GLuint GLintptr offset
GLboolean GLboolean g
GLint y
const GLubyte * c
GLfloat GLfloat p
[1]
GLenum GLsizei len
GLfloat GLfloat GLfloat alpha
Definition qopenglext.h:418
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
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 QRgb qRgba(int r, int g, int b, int a)
Definition qrgb.h:33
constexpr int qBlue(QRgb rgb)
Definition qrgb.h:24
constexpr int qAlpha(QRgb rgb)
Definition qrgb.h:27
constexpr QRgba64 qRgba64(quint16 r, quint16 g, quint16 b, quint16 a)
Definition qrgba64.h:180
#define QT_DEFINE_QESDP_SPECIALIZATION_DTOR(Class)
unsigned short quint16
Definition qtypes.h:48
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
unsigned char quint8
Definition qtypes.h:46
QTextStream out(stdout)
[7]
QReadWriteLock lock
[0]
QSharedPointer< T > other(t)
[5]