/src/Simd/src/Simd/SimdAvx2BgrToHsv.cpp
Line | Count | Source |
1 | | /* |
2 | | * Simd Library (http://ermig1979.github.io/Simd). |
3 | | * |
4 | | * Copyright (c) 2011-2026 Yermalayeu Ihar. |
5 | | * |
6 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | | * of this software and associated documentation files (the "Software"), to deal |
8 | | * in the Software without restriction, including without limitation the rights |
9 | | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
10 | | * copies of the Software, and to permit persons to whom the Software is |
11 | | * furnished to do so, subject to the following conditions: |
12 | | * |
13 | | * The above copyright notice and this permission notice shall be included in |
14 | | * all copies or substantial portions of the Software. |
15 | | * |
16 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
19 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
20 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
21 | | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
22 | | * SOFTWARE. |
23 | | */ |
24 | | #include "Simd/SimdMemory.h" |
25 | | #include "Simd/SimdStore.h" |
26 | | #include "Simd/SimdConversion.h" |
27 | | #include "Simd/SimdInterleave.h" |
28 | | |
29 | | namespace Simd |
30 | | { |
31 | | #ifdef SIMD_AVX2_ENABLE |
32 | | namespace Avx2 |
33 | | { |
34 | | SIMD_INLINE __m256i MulDiv32Hsv(__m256i dividend, __m256i divisor, const __m256& scale) |
35 | 0 | { |
36 | 0 | return _mm256_cvttps_epi32(_mm256_div_ps(_mm256_mul_ps(scale, _mm256_cvtepi32_ps(dividend)), _mm256_cvtepi32_ps(divisor))); |
37 | 0 | } |
38 | | |
39 | | SIMD_INLINE __m256i MulDiv16Hsv(__m256i dividend, __m256i divisor, const __m256& scale) |
40 | 0 | { |
41 | 0 | const __m256i lo = MulDiv32Hsv(_mm256_unpacklo_epi16(dividend, K_ZERO), _mm256_unpacklo_epi16(divisor, K_ZERO), scale); |
42 | 0 | const __m256i hi = MulDiv32Hsv(_mm256_unpackhi_epi16(dividend, K_ZERO), _mm256_unpackhi_epi16(divisor, K_ZERO), scale); |
43 | 0 | return _mm256_packs_epi32(lo, hi); |
44 | 0 | } |
45 | | |
46 | | SIMD_INLINE void BgrToHsv16(__m256i blue, __m256i green, __m256i red, |
47 | | __m256i& hue, __m256i& sat, __m256i& val, |
48 | | const __m256& KF_255_DIV_6, const __m256& K_255F) |
49 | 0 | { |
50 | 0 | __m256i max = MaxI16(red, green, blue); |
51 | 0 | __m256i min = MinI16(red, green, blue); |
52 | 0 | __m256i range = _mm256_sub_epi16(max, min); |
53 | | |
54 | | // Hue: determine which channel is the maximum |
55 | 0 | __m256i redMaxMask = _mm256_cmpeq_epi16(red, max); |
56 | 0 | __m256i greenMaxMask = _mm256_andnot_si256(redMaxMask, _mm256_cmpeq_epi16(green, max)); |
57 | 0 | __m256i blueMaxMask = _mm256_andnot_si256(_mm256_or_si256(redMaxMask, greenMaxMask), K_INV_ZERO); |
58 | |
|
59 | 0 | __m256i hueDividend = _mm256_or_si256( |
60 | 0 | _mm256_and_si256(redMaxMask, |
61 | 0 | _mm256_add_epi16(_mm256_sub_epi16(green, blue), _mm256_mullo_epi16(range, K16_0006))), |
62 | 0 | _mm256_or_si256( |
63 | 0 | _mm256_and_si256(greenMaxMask, |
64 | 0 | _mm256_add_epi16(_mm256_sub_epi16(blue, red), _mm256_mullo_epi16(range, K16_0002))), |
65 | 0 | _mm256_and_si256(blueMaxMask, |
66 | 0 | _mm256_add_epi16(_mm256_sub_epi16(red, green), _mm256_mullo_epi16(range, K16_0004))))); |
67 | |
|
68 | 0 | __m256i safeRange = _mm256_max_epi16(range, K16_0001); |
69 | 0 | hue = _mm256_andnot_si256(_mm256_cmpeq_epi16(range, K_ZERO), |
70 | 0 | _mm256_and_si256(MulDiv16Hsv(hueDividend, safeRange, KF_255_DIV_6), K16_00FF)); |
71 | | |
72 | | // Value: V = max |
73 | 0 | val = max; |
74 | | |
75 | | // Saturation: S = 255 * range / max, zero when max == 0 |
76 | 0 | __m256i safeMax = _mm256_max_epi16(max, K16_0001); |
77 | 0 | sat = _mm256_andnot_si256(_mm256_cmpeq_epi16(max, K_ZERO), |
78 | 0 | MulDiv16Hsv(range, safeMax, K_255F)); |
79 | 0 | } |
80 | | |
81 | | template <bool align> SIMD_INLINE void BgrToHsv32(const uint8_t* bgr, uint8_t* hsv, |
82 | | const __m256& KF_255_DIV_6, const __m256& K_255F) |
83 | 0 | { |
84 | 0 | __m256i bgr_data[3]; |
85 | 0 | bgr_data[0] = Load<align>((__m256i*)bgr + 0); |
86 | 0 | bgr_data[1] = Load<align>((__m256i*)bgr + 1); |
87 | 0 | bgr_data[2] = Load<align>((__m256i*)bgr + 2); |
88 | |
|
89 | 0 | __m256i blue8 = BgrToBlue(bgr_data); |
90 | 0 | __m256i green8 = BgrToGreen(bgr_data); |
91 | 0 | __m256i red8 = BgrToRed(bgr_data); |
92 | |
|
93 | 0 | __m256i blue_lo = _mm256_unpacklo_epi8(blue8, K_ZERO); |
94 | 0 | __m256i blue_hi = _mm256_unpackhi_epi8(blue8, K_ZERO); |
95 | 0 | __m256i green_lo = _mm256_unpacklo_epi8(green8, K_ZERO); |
96 | 0 | __m256i green_hi = _mm256_unpackhi_epi8(green8, K_ZERO); |
97 | 0 | __m256i red_lo = _mm256_unpacklo_epi8(red8, K_ZERO); |
98 | 0 | __m256i red_hi = _mm256_unpackhi_epi8(red8, K_ZERO); |
99 | |
|
100 | 0 | __m256i hue_lo, sat_lo, val_lo, hue_hi, sat_hi, val_hi; |
101 | 0 | BgrToHsv16(blue_lo, green_lo, red_lo, hue_lo, sat_lo, val_lo, KF_255_DIV_6, K_255F); |
102 | 0 | BgrToHsv16(blue_hi, green_hi, red_hi, hue_hi, sat_hi, val_hi, KF_255_DIV_6, K_255F); |
103 | |
|
104 | 0 | __m256i hue8 = _mm256_packus_epi16(hue_lo, hue_hi); |
105 | 0 | __m256i sat8 = _mm256_packus_epi16(sat_lo, sat_hi); |
106 | 0 | __m256i val8 = _mm256_packus_epi16(val_lo, val_hi); |
107 | |
|
108 | 0 | Store<align>((__m256i*)hsv + 0, InterleaveBgr<0>(hue8, sat8, val8)); |
109 | 0 | Store<align>((__m256i*)hsv + 1, InterleaveBgr<1>(hue8, sat8, val8)); |
110 | 0 | Store<align>((__m256i*)hsv + 2, InterleaveBgr<2>(hue8, sat8, val8)); |
111 | 0 | } Unexecuted instantiation: void Simd::Avx2::BgrToHsv32<true>(unsigned char const*, unsigned char*, float __vector(8) const&, float __vector(8) const&) Unexecuted instantiation: void Simd::Avx2::BgrToHsv32<false>(unsigned char const*, unsigned char*, float __vector(8) const&, float __vector(8) const&) |
112 | | |
113 | | template <bool align> void BgrToHsv(const uint8_t* bgr, size_t width, size_t height, size_t bgrStride, uint8_t* hsv, size_t hsvStride) |
114 | 0 | { |
115 | 0 | assert(width >= A); |
116 | 0 | if (align) |
117 | 0 | assert(Aligned(bgr) && Aligned(bgrStride) && Aligned(hsv) && Aligned(hsvStride)); |
118 | |
|
119 | 0 | size_t alignedWidth = AlignLo(width, A); |
120 | 0 | const __m256 KF = _mm256_set1_ps(Base::KF_255_DIV_6); |
121 | 0 | const __m256 K255 = _mm256_set1_ps(255.0f); |
122 | |
|
123 | 0 | for (size_t row = 0; row < height; ++row) |
124 | 0 | { |
125 | 0 | for (size_t col = 0; col < alignedWidth; col += A) |
126 | 0 | BgrToHsv32<align>(bgr + 3 * col, hsv + 3 * col, KF, K255); |
127 | 0 | if (width != alignedWidth) |
128 | 0 | BgrToHsv32<false>(bgr + 3 * (width - A), hsv + 3 * (width - A), KF, K255); |
129 | 0 | bgr += bgrStride; |
130 | 0 | hsv += hsvStride; |
131 | 0 | } |
132 | 0 | } Unexecuted instantiation: void Simd::Avx2::BgrToHsv<true>(unsigned char const*, unsigned long, unsigned long, unsigned long, unsigned char*, unsigned long) Unexecuted instantiation: void Simd::Avx2::BgrToHsv<false>(unsigned char const*, unsigned long, unsigned long, unsigned long, unsigned char*, unsigned long) |
133 | | |
134 | | void BgrToHsv(const uint8_t* bgr, size_t width, size_t height, size_t bgrStride, uint8_t* hsv, size_t hsvStride) |
135 | 0 | { |
136 | 0 | if (Aligned(bgr) && Aligned(bgrStride) && Aligned(hsv) && Aligned(hsvStride)) |
137 | 0 | BgrToHsv<true>(bgr, width, height, bgrStride, hsv, hsvStride); |
138 | 0 | else |
139 | 0 | BgrToHsv<false>(bgr, width, height, bgrStride, hsv, hsvStride); |
140 | 0 | } |
141 | | } |
142 | | #endif |
143 | | } |