diff --git a/docs/2026.html b/docs/2026.html
index 433d5707ba..5916a8cb52 100644
--- a/docs/2026.html
+++ b/docs/2026.html
@@ -48,6 +48,7 @@
New features
Improving
- AMX-BF16 optimizations of class SynetQuantizedConvolutionNhwcSpecV1.
+ - SSE4.1 optimizations of class ImagePngLoader.
Bug fixing
diff --git a/src/Simd/SimdSse41ImageLoadPng.cpp b/src/Simd/SimdSse41ImageLoadPng.cpp
index 74f9a0964b..5fa30f2640 100644
--- a/src/Simd/SimdSse41ImageLoadPng.cpp
+++ b/src/Simd/SimdSse41ImageLoadPng.cpp
@@ -22,10 +22,13 @@
* SOFTWARE.
*/
#include "Simd/SimdImageLoad.h"
+#include "Simd/SimdImageSavePng.h"
#include "Simd/SimdArray.h"
#include "Simd/SimdCpu.h"
#include "Simd/SimdBase.h"
#include "Simd/SimdSse41.h"
+#include "Simd/SimdMemory.h"
+#include "Simd/SimdUnpack.h"
namespace Simd
{
@@ -55,6 +58,455 @@ namespace Simd
//-------------------------------------------------------------------------------------------------
+ template SIMD_INLINE __m128i LoadN(const uint8_t* p);
+
+ template<> SIMD_INLINE __m128i LoadN<1>(const uint8_t* p)
+ {
+ return _mm_cvtsi32_si128(p[0]);
+ }
+
+ template<> SIMD_INLINE __m128i LoadN<2>(const uint8_t* p)
+ {
+ return _mm_cvtsi32_si128(*(uint16_t*)p);
+ }
+
+ template<> SIMD_INLINE __m128i LoadN<3>(const uint8_t* p)
+ {
+ return _mm_cvtsi32_si128(uint32_t(p[0]) | (uint32_t(p[1]) << 8) | (uint32_t(p[2]) << 16));
+ }
+
+ template<> SIMD_INLINE __m128i LoadN<4>(const uint8_t* p)
+ {
+ return _mm_cvtsi32_si128(*(uint32_t*)p);
+ }
+
+ template<> SIMD_INLINE __m128i LoadN<6>(const uint8_t* p)
+ {
+ return _mm_unpacklo_epi32(_mm_cvtsi32_si128(*(uint32_t*)p), _mm_cvtsi32_si128(*(uint16_t*)(p + 4)));
+ }
+
+ template<> SIMD_INLINE __m128i LoadN<8>(const uint8_t* p)
+ {
+ return _mm_loadl_epi64((__m128i*)p);
+ }
+
+ template SIMD_INLINE void StoreN(uint8_t* p, __m128i v);
+
+ template<> SIMD_INLINE void StoreN<1>(uint8_t* p, __m128i v)
+ {
+ p[0] = (uint8_t)_mm_cvtsi128_si32(v);
+ }
+
+ template<> SIMD_INLINE void StoreN<2>(uint8_t* p, __m128i v)
+ {
+ *(uint16_t*)p = (uint16_t)_mm_cvtsi128_si32(v);
+ }
+
+ template<> SIMD_INLINE void StoreN<3>(uint8_t* p, __m128i v)
+ {
+ uint32_t t = _mm_cvtsi128_si32(v);
+ p[0] = (uint8_t)t;
+ p[1] = (uint8_t)(t >> 8);
+ p[2] = (uint8_t)(t >> 16);
+ }
+
+ template<> SIMD_INLINE void StoreN<4>(uint8_t* p, __m128i v)
+ {
+ *(uint32_t*)p = _mm_cvtsi128_si32(v);
+ }
+
+ template<> SIMD_INLINE void StoreN<6>(uint8_t* p, __m128i v)
+ {
+ *(uint32_t*)p = _mm_cvtsi128_si32(v);
+ *(uint16_t*)(p + 4) = (uint16_t)_mm_extract_epi16(v, 2);
+ }
+
+ template<> SIMD_INLINE void StoreN<8>(uint8_t* p, __m128i v)
+ {
+ _mm_storel_epi64((__m128i*)p, v);
+ }
+
+ SIMD_INLINE void FillExtra(uint8_t* dst, int srcN, int dstN)
+ {
+ for (int i = srcN; i < dstN; ++i)
+ dst[i] = 0xFF;
+ }
+
+ template SIMD_INLINE __m128i PrefixSum(__m128i x);
+
+ template<> SIMD_INLINE __m128i PrefixSum<1>(__m128i x)
+ {
+ x = _mm_add_epi8(x, _mm_slli_si128(x, 1));
+ x = _mm_add_epi8(x, _mm_slli_si128(x, 2));
+ x = _mm_add_epi8(x, _mm_slli_si128(x, 4));
+ x = _mm_add_epi8(x, _mm_slli_si128(x, 8));
+ return x;
+ }
+
+ template<> SIMD_INLINE __m128i PrefixSum<2>(__m128i x)
+ {
+ x = _mm_add_epi8(x, _mm_slli_si128(x, 2));
+ x = _mm_add_epi8(x, _mm_slli_si128(x, 4));
+ x = _mm_add_epi8(x, _mm_slli_si128(x, 8));
+ return x;
+ }
+
+ template<> SIMD_INLINE __m128i PrefixSum<3>(__m128i x)
+ {
+ x = _mm_add_epi8(x, _mm_slli_si128(x, 3));
+ x = _mm_add_epi8(x, _mm_slli_si128(x, 6));
+ x = _mm_add_epi8(x, _mm_slli_si128(x, 12));
+ return x;
+ }
+
+ template<> SIMD_INLINE __m128i PrefixSum<4>(__m128i x)
+ {
+ x = _mm_add_epi8(x, _mm_slli_si128(x, 4));
+ x = _mm_add_epi8(x, _mm_slli_si128(x, 8));
+ return x;
+ }
+
+ template<> SIMD_INLINE __m128i PrefixSum<6>(__m128i x)
+ {
+ x = _mm_add_epi8(x, _mm_slli_si128(x, 6));
+ x = _mm_add_epi8(x, _mm_slli_si128(x, 12));
+ return x;
+ }
+
+ template<> SIMD_INLINE __m128i PrefixSum<8>(__m128i x)
+ {
+ return _mm_add_epi8(x, _mm_slli_si128(x, 8));
+ }
+
+ template SIMD_INLINE __m128i FirstN()
+ {
+ return _mm_srli_si128(K_INV_ZERO, 16 - n);
+ }
+
+ template SIMD_INLINE void StoreStep(uint8_t* dst, __m128i x);
+
+ template<> SIMD_INLINE void StoreStep<8>(uint8_t* dst, __m128i x)
+ {
+ _mm_storel_epi64((__m128i*)dst, x);
+ }
+
+ template<> SIMD_INLINE void StoreStep<12>(uint8_t* dst, __m128i x)
+ {
+ _mm_storel_epi64((__m128i*)dst, x);
+ *(uint32_t*)(dst + 8) = _mm_extract_epi32(x, 2);
+ }
+
+ template<> SIMD_INLINE void StoreStep<15>(uint8_t* dst, __m128i x)
+ {
+ StoreStep<12>(dst, x);
+ *(uint16_t*)(dst + 12) = (uint16_t)_mm_extract_epi16(x, 6);
+ dst[14] = (uint8_t)_mm_extract_epi8(x, 14);
+ }
+
+ template<> SIMD_INLINE void StoreStep<16>(uint8_t* dst, __m128i x)
+ {
+ _mm_storeu_si128((__m128i*)dst, x);
+ }
+
+ template SIMD_INLINE __m128i LastPixel(__m128i x)
+ {
+ return _mm_and_si128(_mm_srli_si128(x, step - n), FirstN());
+ }
+
+ template void DecodeSubEq(const uint8_t* curr, int width, uint8_t* dst)
+ {
+ int size = width * n, i = 0;
+ __m128i prev = _mm_setzero_si128();
+ for (; i + (int)A <= size; i += step)
+ {
+ __m128i x = _mm_add_epi8(_mm_loadu_si128((__m128i*)(curr + i)), prev);
+ x = PrefixSum(x);
+ StoreStep(dst + i, x);
+ prev = LastPixel(x);
+ }
+ if (i == 0)
+ {
+ for (; i < n && i < size; ++i)
+ dst[i] = curr[i];
+ }
+ for (; i < size; ++i)
+ dst[i] = curr[i] + dst[i - n];
+ }
+
+ SIMD_INLINE __m128i Average(__m128i a, __m128i b)
+ {
+ return _mm_sub_epi8(_mm_avg_epu8(a, b), _mm_and_si128(_mm_xor_si128(a, b), K8_01));
+ }
+
+ SIMD_INLINE __m128i PaethPredictor(__m128i a, __m128i b, __m128i c)
+ {
+ __m128i p = _mm_sub_epi16(_mm_add_epi16(a, b), c);
+ __m128i pa = _mm_abs_epi16(_mm_sub_epi16(p, a));
+ __m128i pb = _mm_abs_epi16(_mm_sub_epi16(p, b));
+ __m128i pc = _mm_abs_epi16(_mm_sub_epi16(p, c));
+ __m128i mbc = _mm_or_si128(_mm_cmpgt_epi16(pa, pb), _mm_cmpgt_epi16(pa, pc));
+ __m128i mc = _mm_cmpgt_epi16(pb, pc);
+ return _mm_blendv_epi8(a, _mm_blendv_epi8(b, c, mc), mbc);
+ }
+
+ SIMD_INLINE __m128i PaethPack(__m128i a, __m128i b, __m128i c)
+ {
+ return _mm_packus_epi16(PaethPredictor(UnpackU8<0>(a), UnpackU8<0>(b), UnpackU8<0>(c)), K_ZERO);
+ }
+
+ template void DecodeAvgEq(const uint8_t* curr, const uint8_t* prev, int width, uint8_t* dst)
+ {
+ int size = width * n;
+ if (size <= 0)
+ return;
+ StoreN(dst, _mm_add_epi8(LoadN(curr), Average(LoadN(prev), K_ZERO)));
+ for (int i = n; i < size; i += n)
+ StoreN(dst + i, _mm_add_epi8(LoadN(curr + i), Average(LoadN(prev + i), LoadN(dst + i - n))));
+ }
+
+ template void DecodePaethEq(const uint8_t* curr, const uint8_t* prev, int width, uint8_t* dst)
+ {
+ int size = width * n;
+ if (size <= 0)
+ return;
+ StoreN(dst, _mm_add_epi8(LoadN(curr), PaethPack(K_ZERO, LoadN(prev), K_ZERO)));
+ for (int i = n; i < size; i += n)
+ StoreN(dst + i, _mm_add_epi8(LoadN(curr + i), PaethPack(LoadN(dst + i - n), LoadN(prev + i), LoadN(prev + i - n))));
+ }
+
+ template void DecodeAvgFirstEq(const uint8_t* curr, int width, uint8_t* dst)
+ {
+ int size = width * n;
+ if (size <= 0)
+ return;
+ StoreN(dst, LoadN(curr));
+ for (int i = n; i < size; i += n)
+ StoreN(dst + i, _mm_add_epi8(LoadN(curr + i), Average(LoadN(dst + i - n), K_ZERO)));
+ }
+
+ //-------------------------------------------------------------------------------------------------
+
+ void DecodeLine0(const uint8_t* curr, const uint8_t* prev, int width, int srcN, int dstN, uint8_t* dst)
+ {
+ if (srcN == dstN)
+ {
+ int size = width * srcN, i = 0, sizeA = (int)AlignLo(size, A);
+ for (; i < sizeA; i += (int)A)
+ _mm_storeu_si128((__m128i*)(dst + i), _mm_loadu_si128((__m128i*)(curr + i)));
+ for (; i < size; ++i)
+ dst[i] = curr[i];
+ }
+ else
+ {
+ for (int x = 0; x < width; ++x)
+ {
+ int i = 0;
+ for (; i < srcN; ++i)
+ dst[i] = curr[i];
+ FillExtra(dst, srcN, dstN);
+ curr += srcN;
+ dst += dstN;
+ }
+ }
+ }
+
+ void DecodeLine1(const uint8_t* curr, const uint8_t* prev, int width, int srcN, int dstN, uint8_t* dst)
+ {
+ if (srcN == dstN)
+ {
+ switch (srcN)
+ {
+ case 1: DecodeSubEq<1, 16>(curr, width, dst); break;
+ case 2: DecodeSubEq<2, 16>(curr, width, dst); break;
+ case 3: DecodeSubEq<3, 15>(curr, width, dst); break;
+ case 4: DecodeSubEq<4, 16>(curr, width, dst); break;
+ case 6: DecodeSubEq<6, 12>(curr, width, dst); break;
+ case 8: DecodeSubEq<8, 16>(curr, width, dst); break;
+ default:
+ for (int i = 0; i < srcN; ++i)
+ dst[i] = curr[i];
+ for (int i = srcN, n = srcN * width; i < n; ++i)
+ dst[i] = curr[i] + dst[i - dstN];
+ break;
+ }
+ }
+ else
+ {
+ int i = 0;
+ for (; i < srcN; ++i)
+ dst[i] = curr[i];
+ FillExtra(dst, srcN, dstN);
+ curr += srcN;
+ dst += dstN;
+ for (int x = 1; x < width; ++x)
+ {
+ i = 0;
+ for (; i < srcN; ++i)
+ dst[i] = curr[i] + dst[i - dstN];
+ FillExtra(dst, srcN, dstN);
+ curr += srcN;
+ dst += dstN;
+ }
+ }
+ }
+
+ void DecodeLine2(const uint8_t* curr, const uint8_t* prev, int width, int srcN, int dstN, uint8_t* dst)
+ {
+ if (srcN == dstN)
+ {
+ int size = width * srcN, i = 0, sizeA = (int)AlignLo(size, A);
+ for (; i < sizeA; i += (int)A)
+ {
+ __m128i _curr = _mm_loadu_si128((__m128i*)(curr + i));
+ __m128i _prev = _mm_loadu_si128((__m128i*)(prev + i));
+ _mm_storeu_si128((__m128i*)(dst + i), _mm_add_epi8(_curr, _prev));
+ }
+ for (; i < size; ++i)
+ dst[i] = curr[i] + prev[i];
+ }
+ else
+ {
+ for (int x = 0; x < width; ++x)
+ {
+ int i = 0;
+ for (; i < srcN; ++i)
+ dst[i] = curr[i] + prev[i];
+ FillExtra(dst, srcN, dstN);
+ curr += srcN;
+ prev += dstN;
+ dst += dstN;
+ }
+ }
+ }
+
+ void DecodeLine3(const uint8_t* curr, const uint8_t* prev, int width, int srcN, int dstN, uint8_t* dst)
+ {
+ if (srcN == dstN)
+ {
+ switch (srcN)
+ {
+ case 2: DecodeAvgEq<2>(curr, prev, width, dst); break;
+ case 3: DecodeAvgEq<3>(curr, prev, width, dst); break;
+ case 4: DecodeAvgEq<4>(curr, prev, width, dst); break;
+ case 6: DecodeAvgEq<6>(curr, prev, width, dst); break;
+ case 8: DecodeAvgEq<8>(curr, prev, width, dst); break;
+ default:
+ for (int i = 0; i < srcN; ++i)
+ dst[i] = curr[i] + (prev[i] >> 1);
+ for (int i = srcN, n = srcN * width; i < n; ++i)
+ dst[i] = curr[i] + ((prev[i] + dst[i - dstN]) >> 1);
+ break;
+ }
+ }
+ else
+ {
+ int i = 0;
+ for (; i < srcN; ++i)
+ dst[i] = curr[i] + (prev[i] >> 1);
+ FillExtra(dst, srcN, dstN);
+ curr += srcN;
+ prev += dstN;
+ dst += dstN;
+ for (int x = 1; x < width; ++x)
+ {
+ i = 0;
+ for (; i < srcN; ++i)
+ dst[i] = curr[i] + ((prev[i] + dst[i - dstN]) >> 1);
+ FillExtra(dst, srcN, dstN);
+ curr += srcN;
+ prev += dstN;
+ dst += dstN;
+ }
+ }
+ }
+
+ void DecodeLine4(const uint8_t* curr, const uint8_t* prev, int width, int srcN, int dstN, uint8_t* dst)
+ {
+ if (srcN == dstN)
+ {
+ switch (srcN)
+ {
+ case 2: DecodePaethEq<2>(curr, prev, width, dst); break;
+ case 3: DecodePaethEq<3>(curr, prev, width, dst); break;
+ case 4: DecodePaethEq<4>(curr, prev, width, dst); break;
+ case 6: DecodePaethEq<6>(curr, prev, width, dst); break;
+ case 8: DecodePaethEq<8>(curr, prev, width, dst); break;
+ default:
+ for (int i = 0; i < srcN; ++i)
+ dst[i] = curr[i] + Base::Paeth(0, prev[i], 0);
+ for (int i = srcN, n = srcN * width; i < n; ++i)
+ dst[i] = curr[i] + Base::Paeth(dst[i - dstN], prev[i], prev[i - dstN]);
+ break;
+ }
+ }
+ else
+ {
+ int i = 0;
+ for (; i < srcN; ++i)
+ dst[i] = curr[i] + Base::Paeth(0, prev[i], 0);
+ FillExtra(dst, srcN, dstN);
+ curr += srcN;
+ prev += dstN;
+ dst += dstN;
+ for (int x = 1; x < width; ++x)
+ {
+ i = 0;
+ for (; i < srcN; ++i)
+ dst[i] = curr[i] + Base::Paeth(dst[i - dstN], prev[i], prev[i - dstN]);
+ FillExtra(dst, srcN, dstN);
+ curr += srcN;
+ prev += dstN;
+ dst += dstN;
+ }
+ }
+ }
+
+ void DecodeLine5(const uint8_t* curr, const uint8_t* prev, int width, int srcN, int dstN, uint8_t* dst)
+ {
+ if (srcN == dstN)
+ {
+ switch (srcN)
+ {
+ case 2: DecodeAvgFirstEq<2>(curr, width, dst); break;
+ case 3: DecodeAvgFirstEq<3>(curr, width, dst); break;
+ case 4: DecodeAvgFirstEq<4>(curr, width, dst); break;
+ case 6: DecodeAvgFirstEq<6>(curr, width, dst); break;
+ case 8: DecodeAvgFirstEq<8>(curr, width, dst); break;
+ default:
+ for (int i = 0; i < srcN; ++i)
+ dst[i] = curr[i];
+ for (int i = srcN, n = srcN * width; i < n; ++i)
+ dst[i] = curr[i] + (dst[i - dstN] >> 1);
+ break;
+ }
+ }
+ else
+ {
+ int i = 0;
+ for (; i < srcN; ++i)
+ dst[i] = curr[i];
+ FillExtra(dst, srcN, dstN);
+ curr += srcN;
+ dst += dstN;
+ for (int x = 1; x < width; ++x)
+ {
+ i = 0;
+ for (; i < srcN; ++i)
+ dst[i] = curr[i] + (dst[i - dstN] >> 1);
+ FillExtra(dst, srcN, dstN);
+ curr += srcN;
+ dst += dstN;
+ }
+ }
+ }
+
+ void DecodeLine6(const uint8_t* curr, const uint8_t* prev, int width, int srcN, int dstN, uint8_t* dst)
+ {
+ DecodeLine1(curr, prev, width, srcN, dstN, dst);
+ }
+
+ //-------------------------------------------------------------------------------------------------
+
ImagePngLoader::ImagePngLoader(const ImageLoaderParam& param)
: Base::ImagePngLoader(param)
{
@@ -65,6 +517,13 @@ namespace Simd
void ImagePngLoader::SetHandlers()
{
Base::ImagePngLoader::SetHandlers();
+ _decodeLine[0] = DecodeLine0;
+ _decodeLine[1] = DecodeLine1;
+ _decodeLine[2] = DecodeLine2;
+ _decodeLine[3] = DecodeLine3;
+ _decodeLine[4] = DecodeLine4;
+ _decodeLine[5] = DecodeLine5;
+ _decodeLine[6] = DecodeLine6;
if (_width >= A)
{
if (_depth <= 8)