Make QColorTrcLut more flexible
Make it possible to generate one way QColorTrcLut tables, and make it easier test out different table size, Change-Id: I953c68d772699de87fdddbf15ce196e6ba8b9898 Reviewed-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>bb10
parent
05b8467304
commit
04e5b86f9e
|
|
@ -449,7 +449,7 @@ inline void loadP<QRgba64>(const QRgba64 &p, __m128i &v)
|
|||
template<typename T>
|
||||
static void loadPremultiplied(QColorVector *buffer, const T *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
|
||||
{
|
||||
const __m128 v4080 = _mm_set1_ps(4080.f);
|
||||
const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
|
||||
const __m128 iFF00 = _mm_set1_ps(1.0f / (255 * 256));
|
||||
constexpr bool isARGB = isArgb<T>();
|
||||
for (qsizetype i = 0; i < len; ++i) {
|
||||
|
|
@ -468,7 +468,7 @@ static void loadPremultiplied(QColorVector *buffer, const T *src, const qsizetyp
|
|||
vf = _mm_andnot_ps(vAlphaMask, vf);
|
||||
|
||||
// LUT
|
||||
v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080));
|
||||
v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
|
||||
const int ridx = isARGB ? _mm_extract_epi16(v, 4) : _mm_extract_epi16(v, 0);
|
||||
const int gidx = _mm_extract_epi16(v, 2);
|
||||
const int bidx = isARGB ? _mm_extract_epi16(v, 0) : _mm_extract_epi16(v, 4);
|
||||
|
|
@ -484,7 +484,7 @@ static void loadPremultiplied(QColorVector *buffer, const T *src, const qsizetyp
|
|||
template<>
|
||||
void loadPremultiplied<QRgbaFloat32>(QColorVector *buffer, const QRgbaFloat32 *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
|
||||
{
|
||||
const __m128 v4080 = _mm_set1_ps(4080.f);
|
||||
const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
|
||||
const __m128 viFF00 = _mm_set1_ps(1.0f / (255 * 256));
|
||||
const __m128 vZero = _mm_set1_ps(0.0f);
|
||||
const __m128 vOne = _mm_set1_ps(1.0f);
|
||||
|
|
@ -506,7 +506,7 @@ void loadPremultiplied<QRgbaFloat32>(QColorVector *buffer, const QRgbaFloat32 *s
|
|||
const __m128 over = _mm_cmpgt_ps(vf, vOne);
|
||||
if (_mm_movemask_ps(_mm_or_ps(under, over)) == 0) {
|
||||
// Within gamut
|
||||
__m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080));
|
||||
__m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
|
||||
const int ridx = _mm_extract_epi16(v, 0);
|
||||
const int gidx = _mm_extract_epi16(v, 2);
|
||||
const int bidx = _mm_extract_epi16(v, 4);
|
||||
|
|
@ -525,7 +525,7 @@ void loadPremultiplied<QRgbaFloat32>(QColorVector *buffer, const QRgbaFloat32 *s
|
|||
}
|
||||
}
|
||||
|
||||
// Load to [0-4080] in 4x32 SIMD
|
||||
// Load to [0->TrcResolution] in 4x32 SIMD
|
||||
template<typename T>
|
||||
static inline void loadPU(const T &p, __m128i &v);
|
||||
|
||||
|
|
@ -539,7 +539,7 @@ inline void loadPU<QRgb>(const QRgb &p, __m128i &v)
|
|||
v = _mm_unpacklo_epi8(v, _mm_setzero_si128());
|
||||
v = _mm_unpacklo_epi16(v, _mm_setzero_si128());
|
||||
#endif
|
||||
v = _mm_slli_epi32(v, 4);
|
||||
v = _mm_slli_epi32(v, QColorTrcLut::ShiftUp);
|
||||
}
|
||||
|
||||
template<>
|
||||
|
|
@ -552,7 +552,7 @@ inline void loadPU<QRgba64>(const QRgba64 &p, __m128i &v)
|
|||
#else
|
||||
v = _mm_unpacklo_epi16(v, _mm_setzero_si128());
|
||||
#endif
|
||||
v = _mm_srli_epi32(v, 4);
|
||||
v = _mm_srli_epi32(v, QColorTrcLut::ShiftDown);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
|
@ -577,7 +577,7 @@ void loadUnpremultiplied(QColorVector *buffer, const T *src, const qsizetype len
|
|||
template<>
|
||||
void loadUnpremultiplied<QRgbaFloat32>(QColorVector *buffer, const QRgbaFloat32 *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
|
||||
{
|
||||
const __m128 v4080 = _mm_set1_ps(4080.f);
|
||||
const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
|
||||
const __m128 iFF00 = _mm_set1_ps(1.0f / (255 * 256));
|
||||
const __m128 vZero = _mm_set1_ps(0.0f);
|
||||
const __m128 vOne = _mm_set1_ps(1.0f);
|
||||
|
|
@ -587,7 +587,7 @@ void loadUnpremultiplied<QRgbaFloat32>(QColorVector *buffer, const QRgbaFloat32
|
|||
const __m128 over = _mm_cmpgt_ps(vf, vOne);
|
||||
if (_mm_movemask_ps(_mm_or_ps(under, over)) == 0) {
|
||||
// Within gamut
|
||||
__m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080));
|
||||
__m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
|
||||
const int ridx = _mm_extract_epi16(v, 0);
|
||||
const int gidx = _mm_extract_epi16(v, 2);
|
||||
const int bidx = _mm_extract_epi16(v, 4);
|
||||
|
|
@ -648,7 +648,7 @@ static void loadPremultiplied(QColorVector *buffer, const T *src, const qsizetyp
|
|||
vf = vreinterpretq_f32_u32(vbicq_u32(vreinterpretq_u32_f32(vf), vAlphaMask));
|
||||
|
||||
// LUT
|
||||
v = vcvtq_u32_f32(vaddq_f32(vmulq_n_f32(vf, 4080.f), vdupq_n_f32(0.5f)));
|
||||
v = vcvtq_u32_f32(vaddq_f32(vmulq_n_f32(vf, float(QColorTrcLut::Resolution)), vdupq_n_f32(0.5f)));
|
||||
const int ridx = isARGB ? vgetq_lane_u32(v, 2) : vgetq_lane_u32(v, 0);
|
||||
const int gidx = vgetq_lane_u32(v, 1);
|
||||
const int bidx = isARGB ? vgetq_lane_u32(v, 0) : vgetq_lane_u32(v, 2);
|
||||
|
|
@ -661,7 +661,7 @@ static void loadPremultiplied(QColorVector *buffer, const T *src, const qsizetyp
|
|||
}
|
||||
}
|
||||
|
||||
// Load to [0-4080] in 4x32 SIMD
|
||||
// Load to [0->TrcResultion] in 4x32 SIMD
|
||||
template<typename T>
|
||||
static inline void loadPU(const T &p, uint32x4_t &v);
|
||||
|
||||
|
|
@ -669,7 +669,7 @@ template<>
|
|||
inline void loadPU<QRgb>(const QRgb &p, uint32x4_t &v)
|
||||
{
|
||||
v = vmovl_u16(vget_low_u16(vmovl_u8(vreinterpret_u8_u32(vmov_n_u32(p)))));
|
||||
v = vshlq_n_u32(v, 4);
|
||||
v = vshlq_n_u32(v, QColorTrcLut::ShiftUp);
|
||||
}
|
||||
|
||||
template<>
|
||||
|
|
@ -678,7 +678,7 @@ inline void loadPU<QRgba64>(const QRgba64 &p, uint32x4_t &v)
|
|||
uint16x4_t v16 = vreinterpret_u16_u64(vld1_u64(reinterpret_cast<const uint64_t *>(&p)));
|
||||
v16 = vsub_u16(v16, vshr_n_u16(v16, 8));
|
||||
v = vmovl_u16(v16);
|
||||
v = vshrq_n_u32(v, 4);
|
||||
v = vshrq_n_u32(v, QColorTrcLut::ShiftDown);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
|
@ -707,7 +707,7 @@ void loadPremultiplied<QRgb>(QColorVector *buffer, const QRgb *src, const qsizet
|
|||
const uint p = src[i];
|
||||
const int a = qAlpha(p);
|
||||
if (a) {
|
||||
const float ia = 4080.0f / a;
|
||||
const float ia = float(QColorTrcLut::Resolution) / a;
|
||||
const int ridx = int(qRed(p) * ia + 0.5f);
|
||||
const int gidx = int(qGreen(p) * ia + 0.5f);
|
||||
const int bidx = int(qBlue(p) * ia + 0.5f);
|
||||
|
|
@ -727,7 +727,7 @@ void loadPremultiplied<QRgba64>(QColorVector *buffer, const QRgba64 *src, const
|
|||
const QRgba64 &p = src[i];
|
||||
const int a = p.alpha();
|
||||
if (a) {
|
||||
const float ia = 4080.0f / a;
|
||||
const float ia = float(QColorTrcLut::Resolution) / a;
|
||||
const int ridx = int(p.red() * ia + 0.5f);
|
||||
const int gidx = int(p.green() * ia + 0.5f);
|
||||
const int bidx = int(p.blue() * ia + 0.5f);
|
||||
|
|
@ -822,13 +822,13 @@ template<typename D, typename S,
|
|||
static void storePremultiplied(D *dst, const S *src, const QColorVector *buffer, const qsizetype len,
|
||||
const QColorTransformPrivate *d_ptr)
|
||||
{
|
||||
const __m128 v4080 = _mm_set1_ps(4080.f);
|
||||
const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
|
||||
const __m128 iFF00 = _mm_set1_ps(1.0f / (255 * 256));
|
||||
constexpr bool isARGB = isArgb<D>();
|
||||
for (qsizetype i = 0; i < len; ++i) {
|
||||
const int a = getAlpha<S>(src[i]);
|
||||
__m128 vf = _mm_loadu_ps(&buffer[i].x);
|
||||
__m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080));
|
||||
__m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
|
||||
__m128 va = _mm_mul_ps(_mm_set1_ps(a), iFF00);
|
||||
const int ridx = _mm_extract_epi16(v, 0);
|
||||
const int gidx = _mm_extract_epi16(v, 2);
|
||||
|
|
@ -848,7 +848,7 @@ static void storePremultiplied(QRgbaFloat32 *dst, const S *src,
|
|||
const QColorVector *buffer, const qsizetype len,
|
||||
const QColorTransformPrivate *d_ptr)
|
||||
{
|
||||
const __m128 v4080 = _mm_set1_ps(4080.f);
|
||||
const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
|
||||
const __m128 vZero = _mm_set1_ps(0.0f);
|
||||
const __m128 vOne = _mm_set1_ps(1.0f);
|
||||
const __m128 viFF00 = _mm_set1_ps(1.0f / (255 * 256));
|
||||
|
|
@ -861,7 +861,7 @@ static void storePremultiplied(QRgbaFloat32 *dst, const S *src,
|
|||
if (_mm_movemask_ps(_mm_or_ps(under, over)) == 0) {
|
||||
// Within gamut
|
||||
va = _mm_mul_ps(va, viFF00);
|
||||
__m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080));
|
||||
__m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
|
||||
const int ridx = _mm_extract_epi16(v, 0);
|
||||
const int gidx = _mm_extract_epi16(v, 2);
|
||||
const int bidx = _mm_extract_epi16(v, 4);
|
||||
|
|
@ -905,12 +905,12 @@ template<typename D, typename S,
|
|||
static void storeUnpremultiplied(D *dst, const S *src, const QColorVector *buffer, const qsizetype len,
|
||||
const QColorTransformPrivate *d_ptr)
|
||||
{
|
||||
const __m128 v4080 = _mm_set1_ps(4080.f);
|
||||
const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
|
||||
constexpr bool isARGB = isArgb<D>();
|
||||
for (qsizetype i = 0; i < len; ++i) {
|
||||
const int a = getAlpha<S>(src[i]);
|
||||
__m128 vf = _mm_loadu_ps(&buffer[i].x);
|
||||
__m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080));
|
||||
__m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
|
||||
const int ridx = _mm_extract_epi16(v, 0);
|
||||
const int gidx = _mm_extract_epi16(v, 2);
|
||||
const int bidx = _mm_extract_epi16(v, 4);
|
||||
|
|
@ -927,7 +927,7 @@ void storeUnpremultiplied(QRgbaFloat32 *dst, const S *src,
|
|||
const QColorVector *buffer, const qsizetype len,
|
||||
const QColorTransformPrivate *d_ptr)
|
||||
{
|
||||
const __m128 v4080 = _mm_set1_ps(4080.f);
|
||||
const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
|
||||
const __m128 vZero = _mm_set1_ps(0.0f);
|
||||
const __m128 vOne = _mm_set1_ps(1.0f);
|
||||
const __m128 viFF00 = _mm_set1_ps(1.0f / (255 * 256));
|
||||
|
|
@ -938,7 +938,7 @@ void storeUnpremultiplied(QRgbaFloat32 *dst, const S *src,
|
|||
const __m128 over = _mm_cmpgt_ps(vf, vOne);
|
||||
if (_mm_movemask_ps(_mm_or_ps(under, over)) == 0) {
|
||||
// Within gamut
|
||||
__m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080));
|
||||
__m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
|
||||
const int ridx = _mm_extract_epi16(v, 0);
|
||||
const int gidx = _mm_extract_epi16(v, 2);
|
||||
const int bidx = _mm_extract_epi16(v, 4);
|
||||
|
|
@ -961,11 +961,11 @@ template<typename T>
|
|||
static void storeOpaque(T *dst, const QColorVector *buffer, const qsizetype len,
|
||||
const QColorTransformPrivate *d_ptr)
|
||||
{
|
||||
const __m128 v4080 = _mm_set1_ps(4080.f);
|
||||
const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
|
||||
constexpr bool isARGB = isArgb<T>();
|
||||
for (qsizetype i = 0; i < len; ++i) {
|
||||
__m128 vf = _mm_loadu_ps(&buffer[i].x);
|
||||
__m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080));
|
||||
__m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
|
||||
const int ridx = _mm_extract_epi16(v, 0);
|
||||
const int gidx = _mm_extract_epi16(v, 2);
|
||||
const int bidx = _mm_extract_epi16(v, 4);
|
||||
|
|
@ -982,7 +982,7 @@ void storeOpaque<QRgbaFloat32>(QRgbaFloat32 *dst,
|
|||
const QColorVector *buffer, const qsizetype len,
|
||||
const QColorTransformPrivate *d_ptr)
|
||||
{
|
||||
const __m128 v4080 = _mm_set1_ps(4080.f);
|
||||
const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
|
||||
const __m128 vZero = _mm_set1_ps(0.0f);
|
||||
const __m128 vOne = _mm_set1_ps(1.0f);
|
||||
const __m128 viFF00 = _mm_set1_ps(1.0f / (255 * 256));
|
||||
|
|
@ -992,7 +992,7 @@ void storeOpaque<QRgbaFloat32>(QRgbaFloat32 *dst,
|
|||
const __m128 over = _mm_cmpgt_ps(vf, vOne);
|
||||
if (_mm_movemask_ps(_mm_or_ps(under, over)) == 0) {
|
||||
// Within gamut
|
||||
__m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, v4080));
|
||||
__m128i v = _mm_cvtps_epi32(_mm_mul_ps(vf, vTrcRes));
|
||||
const int ridx = _mm_extract_epi16(v, 0);
|
||||
const int gidx = _mm_extract_epi16(v, 2);
|
||||
const int bidx = _mm_extract_epi16(v, 4);
|
||||
|
|
@ -1035,7 +1035,7 @@ static void storePremultiplied(D *dst, const S *src, const QColorVector *buffer,
|
|||
for (qsizetype i = 0; i < len; ++i) {
|
||||
const int a = getAlpha<S>(src[i]);
|
||||
float32x4_t vf = vld1q_f32(&buffer[i].x);
|
||||
uint32x4_t v = vcvtq_u32_f32(vaddq_f32(vmulq_n_f32(vf, 4080.f), vdupq_n_f32(0.5f)));
|
||||
uint32x4_t v = vcvtq_u32_f32(vaddq_f32(vmulq_n_f32(vf, float(QColorTrcLut::Resolution)), vdupq_n_f32(0.5f)));
|
||||
const int ridx = vgetq_lane_u32(v, 0);
|
||||
const int gidx = vgetq_lane_u32(v, 1);
|
||||
const int bidx = vgetq_lane_u32(v, 2);
|
||||
|
|
@ -1079,7 +1079,7 @@ static void storeUnpremultiplied(D *dst, const S *src, const QColorVector *buffe
|
|||
for (qsizetype i = 0; i < len; ++i) {
|
||||
const int a = getAlpha<S>(src[i]);
|
||||
float32x4_t vf = vld1q_f32(&buffer[i].x);
|
||||
uint16x4_t v = vmovn_u32(vcvtq_u32_f32(vaddq_f32(vmulq_n_f32(vf, 4080.f), vdupq_n_f32(0.5f))));
|
||||
uint16x4_t v = vmovn_u32(vcvtq_u32_f32(vaddq_f32(vmulq_n_f32(vf, float(QColorTrcLut::Resolution)), vdupq_n_f32(0.5f))));
|
||||
const int ridx = vget_lane_u16(v, 0);
|
||||
const int gidx = vget_lane_u16(v, 1);
|
||||
const int bidx = vget_lane_u16(v, 2);
|
||||
|
|
@ -1097,7 +1097,7 @@ static void storeOpaque(T *dst, const QColorVector *buffer, const qsizetype len,
|
|||
constexpr bool isARGB = isArgb<T>();
|
||||
for (qsizetype i = 0; i < len; ++i) {
|
||||
float32x4_t vf = vld1q_f32(&buffer[i].x);
|
||||
uint16x4_t v = vmovn_u32(vcvtq_u32_f32(vaddq_f32(vmulq_n_f32(vf, 4080.f), vdupq_n_f32(0.5f))));
|
||||
uint16x4_t v = vmovn_u32(vcvtq_u32_f32(vaddq_f32(vmulq_n_f32(vf, float(QColorTrcLut::Resolution)), vdupq_n_f32(0.5f))));
|
||||
const int ridx = vget_lane_u16(v, 0);
|
||||
const int gidx = vget_lane_u16(v, 1);
|
||||
const int bidx = vget_lane_u16(v, 2);
|
||||
|
|
@ -1114,9 +1114,9 @@ static void storePremultiplied(QRgb *dst, const QRgb *src, const QColorVector *b
|
|||
for (qsizetype i = 0; i < len; ++i) {
|
||||
const int a = qAlpha(src[i]);
|
||||
const float fa = a / (255.0f * 256.0f);
|
||||
const float r = d_ptr->colorSpaceOut->lut[0]->m_fromLinear[int(buffer[i].x * 4080.0f + 0.5f)];
|
||||
const float g = d_ptr->colorSpaceOut->lut[1]->m_fromLinear[int(buffer[i].y * 4080.0f + 0.5f)];
|
||||
const float b = d_ptr->colorSpaceOut->lut[2]->m_fromLinear[int(buffer[i].z * 4080.0f + 0.5f)];
|
||||
const float r = d_ptr->colorSpaceOut->lut[0]->m_fromLinear[int(buffer[i].x * float(QColorTrcLut::Resolution) + 0.5f)];
|
||||
const float g = d_ptr->colorSpaceOut->lut[1]->m_fromLinear[int(buffer[i].y * float(QColorTrcLut::Resolution) + 0.5f)];
|
||||
const float b = d_ptr->colorSpaceOut->lut[2]->m_fromLinear[int(buffer[i].z * float(QColorTrcLut::Resolution) + 0.5f)];
|
||||
dst[i] = qRgba(r * fa + 0.5f, g * fa + 0.5f, b * fa + 0.5f, a);
|
||||
}
|
||||
}
|
||||
|
|
@ -1150,9 +1150,9 @@ static void storePremultiplied(QRgba64 *dst, const S *src, const QColorVector *b
|
|||
for (qsizetype i = 0; i < len; ++i) {
|
||||
const int a = getAlphaF(src[i]) * 65535.f;
|
||||
const float fa = a / (255.0f * 256.0f);
|
||||
const float r = d_ptr->colorSpaceOut->lut[0]->m_fromLinear[int(buffer[i].x * 4080.0f + 0.5f)];
|
||||
const float g = d_ptr->colorSpaceOut->lut[1]->m_fromLinear[int(buffer[i].y * 4080.0f + 0.5f)];
|
||||
const float b = d_ptr->colorSpaceOut->lut[2]->m_fromLinear[int(buffer[i].z * 4080.0f + 0.5f)];
|
||||
const float r = d_ptr->colorSpaceOut->lut[0]->m_fromLinear[int(buffer[i].x * float(QColorTrcLut::Resolution) + 0.5f)];
|
||||
const float g = d_ptr->colorSpaceOut->lut[1]->m_fromLinear[int(buffer[i].y * float(QColorTrcLut::Resolution) + 0.5f)];
|
||||
const float b = d_ptr->colorSpaceOut->lut[2]->m_fromLinear[int(buffer[i].z * float(QColorTrcLut::Resolution) + 0.5f)];
|
||||
dst[i] = qRgba64(r * fa + 0.5f, g * fa + 0.5f, b * fa + 0.5f, a);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,43 +13,80 @@ std::shared_ptr<QColorTrcLut> QColorTrcLut::create()
|
|||
return std::make_shared<Access>();
|
||||
}
|
||||
|
||||
std::shared_ptr<QColorTrcLut> QColorTrcLut::fromGamma(qreal gamma)
|
||||
std::shared_ptr<QColorTrcLut> QColorTrcLut::fromGamma(qreal gamma, Direction dir)
|
||||
{
|
||||
auto cp = create();
|
||||
|
||||
for (int i = 0; i <= (255 * 16); ++i) {
|
||||
cp->m_toLinear[i] = ushort(qRound(qPow(i / qreal(255 * 16), gamma) * (255 * 256)));
|
||||
cp->m_fromLinear[i] = ushort(qRound(qPow(i / qreal(255 * 16), qreal(1) / gamma) * (255 * 256)));
|
||||
}
|
||||
|
||||
cp->setFromGamma(gamma, dir);
|
||||
return cp;
|
||||
}
|
||||
|
||||
std::shared_ptr<QColorTrcLut> QColorTrcLut::fromTransferFunction(const QColorTransferFunction &fun)
|
||||
std::shared_ptr<QColorTrcLut> QColorTrcLut::fromTransferFunction(const QColorTransferFunction &fun, Direction dir)
|
||||
{
|
||||
auto cp = create();
|
||||
QColorTransferFunction inv = fun.inverted();
|
||||
|
||||
for (int i = 0; i <= (255 * 16); ++i) {
|
||||
cp->m_toLinear[i] = ushort(qRound(fun.apply(i / qreal(255 * 16)) * (255 * 256)));
|
||||
cp->m_fromLinear[i] = ushort(qRound(inv.apply(i / qreal(255 * 16)) * (255 * 256)));
|
||||
}
|
||||
|
||||
cp->setFromTransferFunction(fun, dir);
|
||||
return cp;
|
||||
}
|
||||
|
||||
std::shared_ptr<QColorTrcLut> QColorTrcLut::fromTransferTable(const QColorTransferTable &table)
|
||||
std::shared_ptr<QColorTrcLut> QColorTrcLut::fromTransferTable(const QColorTransferTable &table, Direction dir)
|
||||
{
|
||||
auto cp = create();
|
||||
cp->setFromTransferTable(table, dir);
|
||||
return cp;
|
||||
}
|
||||
|
||||
float minInverse = 0.0f;
|
||||
for (int i = 0; i <= (255 * 16); ++i) {
|
||||
cp->m_toLinear[i] = ushort(qBound(0, qRound(table.apply(i / qreal(255 * 16)) * (255 * 256)), 65280));
|
||||
minInverse = table.applyInverse(i / qreal(255 * 16), minInverse);
|
||||
cp->m_fromLinear[i] = ushort(qBound(0, qRound(minInverse * (255 * 256)), 65280));
|
||||
void QColorTrcLut::setFromGamma(qreal gamma, Direction dir)
|
||||
{
|
||||
if (dir & ToLinear) {
|
||||
if (!m_toLinear)
|
||||
m_toLinear.reset(new ushort[Resolution + 1]);
|
||||
for (int i = 0; i <= Resolution; ++i)
|
||||
m_toLinear[i] = ushort(qRound(qPow(i / qreal(Resolution), gamma) * (255 * 256)));
|
||||
}
|
||||
|
||||
return cp;
|
||||
if (dir & FromLinear) {
|
||||
if (!m_fromLinear)
|
||||
m_fromLinear.reset(new ushort[Resolution + 1]);
|
||||
for (int i = 0; i <= Resolution; ++i)
|
||||
m_fromLinear[i] = ushort(qRound(qPow(i / qreal(Resolution), qreal(1) / gamma) * (255 * 256)));
|
||||
}
|
||||
}
|
||||
|
||||
void QColorTrcLut::setFromTransferFunction(const QColorTransferFunction &fun, Direction dir)
|
||||
{
|
||||
if (dir & ToLinear) {
|
||||
if (!m_toLinear)
|
||||
m_toLinear.reset(new ushort[Resolution + 1]);
|
||||
for (int i = 0; i <= Resolution; ++i)
|
||||
m_toLinear[i] = ushort(qRound(fun.apply(i / qreal(Resolution)) * (255 * 256)));
|
||||
}
|
||||
|
||||
if (dir & FromLinear) {
|
||||
if (!m_fromLinear)
|
||||
m_fromLinear.reset(new ushort[Resolution + 1]);
|
||||
QColorTransferFunction inv = fun.inverted();
|
||||
for (int i = 0; i <= Resolution; ++i)
|
||||
m_fromLinear[i] = ushort(qRound(inv.apply(i / qreal(Resolution)) * (255 * 256)));
|
||||
}
|
||||
}
|
||||
|
||||
void QColorTrcLut::setFromTransferTable(const QColorTransferTable &table, Direction dir)
|
||||
{
|
||||
if (dir & ToLinear) {
|
||||
if (!m_toLinear)
|
||||
m_toLinear.reset(new ushort[Resolution + 1]);
|
||||
for (int i = 0; i <= Resolution; ++i)
|
||||
m_toLinear[i] = ushort(qBound(0, qRound(table.apply(i / qreal(Resolution)) * (255 * 256)), 65280));
|
||||
}
|
||||
|
||||
if (dir & FromLinear) {
|
||||
if (!m_fromLinear)
|
||||
m_fromLinear.reset(new ushort[Resolution + 1]);
|
||||
float minInverse = 0.0f;
|
||||
for (int i = 0; i <= Resolution; ++i) {
|
||||
minInverse = table.applyInverse(i / qreal(Resolution), minInverse);
|
||||
m_fromLinear[i] = ushort(qBound(0, qRound(minInverse * (255 * 256)), 65280));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
|
|||
|
|
@ -36,9 +36,22 @@ class QColorTransferTable;
|
|||
class Q_GUI_EXPORT QColorTrcLut
|
||||
{
|
||||
public:
|
||||
static std::shared_ptr<QColorTrcLut> fromGamma(qreal gamma);
|
||||
static std::shared_ptr<QColorTrcLut> fromTransferFunction(const QColorTransferFunction &transfn);
|
||||
static std::shared_ptr<QColorTrcLut> fromTransferTable(const QColorTransferTable &transTable);
|
||||
static constexpr uint32_t ShiftUp = 4; // Amount to shift up from 1->255
|
||||
static constexpr uint32_t ShiftDown = (8 - ShiftUp); // Amount to shift down from 1->65280
|
||||
static constexpr qsizetype Resolution = (1 << ShiftUp) * 255; // Number of entries in table
|
||||
|
||||
enum Direction {
|
||||
ToLinear = 1,
|
||||
FromLinear = 2,
|
||||
BiLinear = ToLinear | FromLinear
|
||||
};
|
||||
|
||||
static std::shared_ptr<QColorTrcLut> fromGamma(qreal gamma, Direction dir = BiLinear);
|
||||
static std::shared_ptr<QColorTrcLut> fromTransferFunction(const QColorTransferFunction &transFn, Direction dir = BiLinear);
|
||||
static std::shared_ptr<QColorTrcLut> fromTransferTable(const QColorTransferTable &transTable, Direction dir = BiLinear);
|
||||
void setFromGamma(qreal gamma, Direction dir = BiLinear);
|
||||
void setFromTransferFunction(const QColorTransferFunction &transFn, Direction dir = BiLinear);
|
||||
void setFromTransferTable(const QColorTransferTable &transTable, Direction dir = BiLinear);
|
||||
|
||||
// The following methods all convert opaque or unpremultiplied colors:
|
||||
|
||||
|
|
@ -47,7 +60,7 @@ public:
|
|||
#if defined(__SSE2__)
|
||||
__m128i v = _mm_cvtsi32_si128(rgb32);
|
||||
v = _mm_unpacklo_epi8(v, _mm_setzero_si128());
|
||||
const __m128i vidx = _mm_slli_epi16(v, 4);
|
||||
const __m128i vidx = _mm_slli_epi16(v, ShiftUp);
|
||||
const int ridx = _mm_extract_epi16(vidx, 2);
|
||||
const int gidx = _mm_extract_epi16(vidx, 1);
|
||||
const int bidx = _mm_extract_epi16(vidx, 0);
|
||||
|
|
@ -62,7 +75,7 @@ public:
|
|||
#elif (defined(__ARM_NEON__) || defined(__ARM_NEON)) && Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
||||
uint8x8_t v8 = vreinterpret_u8_u32(vmov_n_u32(rgb32));
|
||||
uint16x4_t v16 = vget_low_u16(vmovl_u8(v8));
|
||||
const uint16x4_t vidx = vshl_n_u16(v16, 4);
|
||||
const uint16x4_t vidx = vshl_n_u16(v16, ShiftUp);
|
||||
const int ridx = vget_lane_u16(vidx, 2);
|
||||
const int gidx = vget_lane_u16(vidx, 1);
|
||||
const int bidx = vget_lane_u16(vidx, 0);
|
||||
|
|
@ -73,9 +86,9 @@ public:
|
|||
v16 = vadd_u16(v16, vshr_n_u16(v16, 8));
|
||||
return QRgba64::fromRgba64(vget_lane_u64(vreinterpret_u64_u16(v16), 0));
|
||||
#else
|
||||
uint r = m_toLinear[qRed(rgb32) << 4];
|
||||
uint g = m_toLinear[qGreen(rgb32) << 4];
|
||||
uint b = m_toLinear[qBlue(rgb32) << 4];
|
||||
uint r = m_toLinear[qRed(rgb32) << ShiftUp];
|
||||
uint g = m_toLinear[qGreen(rgb32) << ShiftUp];
|
||||
uint b = m_toLinear[qBlue(rgb32) << ShiftUp];
|
||||
r = r + (r >> 8);
|
||||
g = g + (g >> 8);
|
||||
b = b + (b >> 8);
|
||||
|
|
@ -86,30 +99,30 @@ public:
|
|||
|
||||
QRgb toLinear(QRgb rgb32) const
|
||||
{
|
||||
return convertWithTable(rgb32, m_toLinear);
|
||||
return convertWithTable(rgb32, m_toLinear.get());
|
||||
}
|
||||
|
||||
QRgba64 toLinear(QRgba64 rgb64) const
|
||||
{
|
||||
return convertWithTable(rgb64, m_toLinear);
|
||||
return convertWithTable(rgb64, m_toLinear.get());
|
||||
}
|
||||
|
||||
float u8ToLinearF32(int c) const
|
||||
{
|
||||
ushort v = m_toLinear[c << 4];
|
||||
ushort v = m_toLinear[c << ShiftUp];
|
||||
return v * (1.0f / (255*256));
|
||||
}
|
||||
|
||||
float u16ToLinearF32(int c) const
|
||||
{
|
||||
c -= (c >> 8);
|
||||
ushort v = m_toLinear[c >> 4];
|
||||
ushort v = m_toLinear[c >> ShiftDown];
|
||||
return v * (1.0f / (255*256));
|
||||
}
|
||||
|
||||
float toLinear(float f) const
|
||||
{
|
||||
ushort v = m_toLinear[(int)(f * (255 * 16) + 0.5f)];
|
||||
ushort v = m_toLinear[(int)(f * Resolution + 0.5f)];
|
||||
return v * (1.0f / (255*256));
|
||||
}
|
||||
|
||||
|
|
@ -118,7 +131,7 @@ public:
|
|||
#if defined(__SSE2__)
|
||||
__m128i v = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(&rgb64));
|
||||
v = _mm_sub_epi16(v, _mm_srli_epi16(v, 8));
|
||||
const __m128i vidx = _mm_srli_epi16(v, 4);
|
||||
const __m128i vidx = _mm_srli_epi16(v, ShiftDown);
|
||||
const int ridx = _mm_extract_epi16(vidx, 0);
|
||||
const int gidx = _mm_extract_epi16(vidx, 1);
|
||||
const int bidx = _mm_extract_epi16(vidx, 2);
|
||||
|
|
@ -132,7 +145,7 @@ public:
|
|||
#elif (defined(__ARM_NEON__) || defined(__ARM_NEON)) && Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
||||
uint16x4_t v = vreinterpret_u16_u64(vmov_n_u64(rgb64));
|
||||
v = vsub_u16(v, vshr_n_u16(v, 8));
|
||||
const uint16x4_t vidx = vshr_n_u16(v, 4);
|
||||
const uint16x4_t vidx = vshr_n_u16(v, ShiftDown);
|
||||
const int ridx = vget_lane_u16(vidx, 0);
|
||||
const int gidx = vget_lane_u16(vidx, 1);
|
||||
const int bidx = vget_lane_u16(vidx, 2);
|
||||
|
|
@ -151,56 +164,56 @@ public:
|
|||
g = g - (g >> 8);
|
||||
b = b - (b >> 8);
|
||||
a = (a + 0x80) >> 8;
|
||||
r = (m_fromLinear[r >> 4] + 0x80) >> 8;
|
||||
g = (m_fromLinear[g >> 4] + 0x80) >> 8;
|
||||
b = (m_fromLinear[b >> 4] + 0x80) >> 8;
|
||||
r = (m_fromLinear[r >> ShiftDown] + 0x80) >> 8;
|
||||
g = (m_fromLinear[g >> ShiftDown] + 0x80) >> 8;
|
||||
b = (m_fromLinear[b >> ShiftDown] + 0x80) >> 8;
|
||||
return (a << 24) | (r << 16) | (g << 8) | b;
|
||||
#endif
|
||||
}
|
||||
|
||||
QRgb fromLinear(QRgb rgb32) const
|
||||
{
|
||||
return convertWithTable(rgb32, m_fromLinear);
|
||||
return convertWithTable(rgb32, m_fromLinear.get());
|
||||
}
|
||||
|
||||
QRgba64 fromLinear(QRgba64 rgb64) const
|
||||
{
|
||||
return convertWithTable(rgb64, m_fromLinear);
|
||||
return convertWithTable(rgb64, m_fromLinear.get());
|
||||
}
|
||||
|
||||
int u8FromLinearF32(float f) const
|
||||
{
|
||||
ushort v = m_fromLinear[(int)(f * (255 * 16) + 0.5f)];
|
||||
ushort v = m_fromLinear[(int)(f * Resolution + 0.5f)];
|
||||
return (v + 0x80) >> 8;
|
||||
}
|
||||
int u16FromLinearF32(float f) const
|
||||
{
|
||||
ushort v = m_fromLinear[(int)(f * (255 * 16) + 0.5f)];
|
||||
ushort v = m_fromLinear[(int)(f * Resolution + 0.5f)];
|
||||
return v + (v >> 8);
|
||||
}
|
||||
float fromLinear(float f) const
|
||||
{
|
||||
ushort v = m_fromLinear[(int)(f * (255 * 16) + 0.5f)];
|
||||
ushort v = m_fromLinear[(int)(f * Resolution + 0.5f)];
|
||||
return v * (1.0f / (255*256));
|
||||
}
|
||||
|
||||
// We translate to 0-65280 (255*256) instead to 0-65535 to make simple
|
||||
// shifting an accurate conversion.
|
||||
// We translate from 0-4080 (255*16) for the same speed up, and to keep
|
||||
// the tables small enough to fit in most inner caches.
|
||||
ushort m_toLinear[(255 * 16) + 1]; // [0-4080] -> [0-65280]
|
||||
ushort m_fromLinear[(255 * 16) + 1]; // [0-4080] -> [0-65280]
|
||||
// We translate from 0->Resolution (4080 = 255*16) for the same speed up,
|
||||
// and to keep the tables small enough to fit in most inner caches.
|
||||
std::unique_ptr<ushort[]> m_toLinear; // [0->Resolution] -> [0-65280]
|
||||
std::unique_ptr<ushort[]> m_fromLinear; // [0->Resolution] -> [0-65280]
|
||||
|
||||
private:
|
||||
QColorTrcLut() { } // force uninitialized members
|
||||
QColorTrcLut() = default;
|
||||
|
||||
static std::shared_ptr<QColorTrcLut> create();
|
||||
|
||||
Q_ALWAYS_INLINE static QRgb convertWithTable(QRgb rgb32, const ushort *table)
|
||||
{
|
||||
const int r = (table[qRed(rgb32) << 4] + 0x80) >> 8;
|
||||
const int g = (table[qGreen(rgb32) << 4] + 0x80) >> 8;
|
||||
const int b = (table[qBlue(rgb32) << 4] + 0x80) >> 8;
|
||||
const int r = (table[qRed(rgb32) << ShiftUp] + 0x80) >> 8;
|
||||
const int g = (table[qGreen(rgb32) << ShiftUp] + 0x80) >> 8;
|
||||
const int b = (table[qBlue(rgb32) << ShiftUp] + 0x80) >> 8;
|
||||
return (rgb32 & 0xff000000) | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
Q_ALWAYS_INLINE static QRgba64 convertWithTable(QRgba64 rgb64, const ushort *table)
|
||||
|
|
@ -208,7 +221,7 @@ private:
|
|||
#if defined(__SSE2__)
|
||||
__m128i v = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(&rgb64));
|
||||
v = _mm_sub_epi16(v, _mm_srli_epi16(v, 8));
|
||||
const __m128i vidx = _mm_srli_epi16(v, 4);
|
||||
const __m128i vidx = _mm_srli_epi16(v, ShiftDown);
|
||||
const int ridx = _mm_extract_epi16(vidx, 2);
|
||||
const int gidx = _mm_extract_epi16(vidx, 1);
|
||||
const int bidx = _mm_extract_epi16(vidx, 0);
|
||||
|
|
@ -222,7 +235,7 @@ private:
|
|||
#elif (defined(__ARM_NEON__) || defined(__ARM_NEON)) && Q_BYTE_ORDER == Q_LITTLE_ENDIAN
|
||||
uint16x4_t v = vreinterpret_u16_u64(vmov_n_u64(rgb64));
|
||||
v = vsub_u16(v, vshr_n_u16(v, 8));
|
||||
const uint16x4_t vidx = vshr_n_u16(v, 4);
|
||||
const uint16x4_t vidx = vshr_n_u16(v, ShiftDown);
|
||||
const int ridx = vget_lane_u16(vidx, 2);
|
||||
const int gidx = vget_lane_u16(vidx, 1);
|
||||
const int bidx = vget_lane_u16(vidx, 0);
|
||||
|
|
@ -238,9 +251,9 @@ private:
|
|||
r = r - (r >> 8);
|
||||
g = g - (g >> 8);
|
||||
b = b - (b >> 8);
|
||||
r = table[r >> 4];
|
||||
g = table[g >> 4];
|
||||
b = table[b >> 4];
|
||||
r = table[r >> ShiftDown];
|
||||
g = table[g >> ShiftDown];
|
||||
b = table[b >> ShiftDown];
|
||||
r = r + (r >> 8);
|
||||
g = g + (g >> 8);
|
||||
b = b + (b >> 8);
|
||||
|
|
|
|||
|
|
@ -238,9 +238,10 @@ void tst_QColorSpace::imageConversion_data()
|
|||
|
||||
QTest::newRow("sRGB -> Display-P3") << QColorSpace::SRgb << QColorSpace::DisplayP3 << 0;
|
||||
QTest::newRow("sRGB -> Adobe RGB") << QColorSpace::SRgb << QColorSpace::AdobeRgb << 2;
|
||||
QTest::newRow("Display-P3 -> sRGB") << QColorSpace::DisplayP3 << QColorSpace::SRgb << 0;
|
||||
QTest::newRow("Adobe RGB -> sRGB") << QColorSpace::AdobeRgb << QColorSpace::SRgb << 2;
|
||||
QTest::newRow("Adobe RGB -> Display-P3") << QColorSpace::AdobeRgb << QColorSpace::DisplayP3 << 2;
|
||||
QTest::newRow("Display-P3 -> Adobe RGB") << QColorSpace::DisplayP3 << QColorSpace::AdobeRgb << 2;
|
||||
QTest::newRow("Display-P3 -> sRGB") << QColorSpace::DisplayP3 << QColorSpace::SRgb << 0;
|
||||
QTest::newRow("sRGB -> sRGB Linear") << QColorSpace::SRgb << QColorSpace::SRgbLinear << 0;
|
||||
QTest::newRow("sRGB Linear -> sRGB") << QColorSpace::SRgbLinear << QColorSpace::SRgb << 0;
|
||||
}
|
||||
|
|
@ -282,8 +283,9 @@ void tst_QColorSpace::imageConversion()
|
|||
QCOMPARE(testImage.colorSpace(), QColorSpace(fromColorSpace));
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
QRgb p = testImage.pixel(i, 0);
|
||||
QVERIFY(qAbs(qRed(p) - qGreen(p)) <= tolerance);
|
||||
QVERIFY(qAbs(qRed(p) - qBlue(p)) <= tolerance);
|
||||
QVERIFY(qAbs(qRed(p) - qGreen(p)) <= tolerance);
|
||||
QVERIFY(qAbs(qGreen(p) - qBlue(p)) <= tolerance);
|
||||
QVERIFY((lastRed - qRed(p)) <= (tolerance / 2));
|
||||
QVERIFY((lastGreen - qGreen(p)) <= (tolerance / 2));
|
||||
QVERIFY((lastBlue - qBlue(p)) <= (tolerance / 2));
|
||||
|
|
@ -460,7 +462,7 @@ void tst_QColorSpace::imageConversionOverLargerGamut()
|
|||
testImage.setColorSpace(csfrom);
|
||||
for (int y = 0; y < 256; ++y)
|
||||
for (int x = 0; x < 256; ++x)
|
||||
testImage.setPixel(x, y, qRgb(x, y, 0));
|
||||
testImage.setPixel(x, y, qRgb(x, y, qAbs(x - y)));
|
||||
|
||||
QImage resultImage = testImage.convertedToColorSpace(csto);
|
||||
for (int y = 0; y < 256; ++y) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue