// File: /workspace/github_cpp_makefile_star2_O2/Alcaro[P]bridges/arlib/image-png-decode.cpp
// Function: image::png_defilter(unsigned char*, unsigned long, unsigned char const*, int, unsigned long, unsigned long)
bool image::png_defilter(uint8_t * out, size_t out_stride, const uint8_t * in, int bytes_per_pixel, size_t width_bytes, size_t height)
{
	for (size_t y=0;y<height;y++)
	{
		const uint8_t * defilter_in = in + y*(width_bytes+1) + 1;
		uint8_t filter_type = defilter_in[-1]; // cache this, in case input and output ends at same place
		
		uint8_t * defilter_out = out + y*out_stride;
		*(defilter_out++) = 0; // x=-1 is all zeroes (always use 4, not width_bytes, makes the math easier)
		*(defilter_out++) = 0;
		*(defilter_out++) = 0;
		*(defilter_out++) = 0;
		
		memmove(defilter_out, defilter_in, width_bytes);
		switch (filter_type)
		{
		case 0: // None
			break;
		
		case 1: // Sub
			for (size_t x=0;x<width_bytes;x++)
				defilter_out[x] += defilter_out[x-bytes_per_pixel];
			break;
		
		case 2: // Up
			for (size_t x=0;x<width_bytes;x++)
				defilter_out[x] += defilter_out[x-out_stride];
			break;
		
		case 3: // Average
			for (size_t x=0;x<width_bytes;x++)
			{
				int a = defilter_out[x-bytes_per_pixel];
				int b = defilter_out[x-out_stride];
				defilter_out[x] += (a+b)/2;
			}
			break;
		
		case 4: // Paeth
		{
#ifdef __SSE2__
#ifndef __SSSE3__
			auto _mm_abs_epi16 = [](__m128i a) -> __m128i {
				// abs(x) = (x^(x<0)) - (x<0)
				// however, that's four insns (three obvious, plus have to copy the argument)
				// instead, this one is three (or two, if first call reuses a zeroed reg from the a/b/c calculation)
				// abs(x) = max_i16(x, -x) (or min_u16(x, -x))
				return _mm_max_epi16(a, _mm_sub_epi16(_mm_setzero_si128(), a)); 
			};
#endif
			// ignore simding the other filters; Paeth is the most common, and also the most expensive per pixel
			// it can only be SIMDed to a degree of bytes_per_pixel, so ignore bytes_per_pixel=1 (and =2, gray+alpha, too rare)
			// (it's possible to interlace processing of two consecutive Paeth scanlines by having the upper one 1px ahead of the
			//    one below, but the required data structures would be quite annoying, and combining the bytes would be annoying too)
			// (it'd also be possible to assume the previous pixel was predicted Up aka B and discard the calculation if wrong,
			//    but it's 84.6% Left/A, 13% Up/B, 2.4% Diag/C, so such a thing would be wrong most of the time and not accomplish much)
			// macro instead of function is 7% faster
#define SIMD_PAETH(bytes_per_pixel) do { \
					uint32_t a32 = *(uint32_t*)(defilter_out+x           -bytes_per_pixel);          \
					uint32_t b32 = *(uint32_t*)(defilter_out+x-out_stride                );          \
					uint32_t c32 = *(uint32_t*)(defilter_out+x-out_stride-bytes_per_pixel);          \
					__m128i a = _mm_unpacklo_epi8(_mm_cvtsi32_si128(a32), _mm_setzero_si128());      \
					__m128i b = _mm_unpacklo_epi8(_mm_cvtsi32_si128(b32), _mm_setzero_si128());      \
					__m128i c = _mm_unpacklo_epi8(_mm_cvtsi32_si128(c32), _mm_setzero_si128());      \
					                                                                                 \
					__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));                                 \
					                                                                                 \
					/* 0 to use pa, -1 for something else */                                         \
					__m128i pa_skip = _mm_cmplt_epi16(_mm_min_epi16(pb, pc), pa);                    \
					/* -1 to use pc, 0 for something else */                                         \
					__m128i pc_use = _mm_and_si128(pa_skip, _mm_cmplt_epi16(pc, pb));                \
					__m128i pb_use = _mm_andnot_si128(pc_use, pa_skip);                              \
					                                                                                 \
					__m128i prediction = _mm_or_si128(_mm_andnot_si128(pa_skip, a),                  \
					                     _mm_or_si128(_mm_and_si128(   pb_use, b),                   \
					                                  _mm_and_si128(   pc_use, c)));                 \
					                                                                                 \
					uint32_t* outp = (uint32_t*)(defilter_out+x);                                    \
					__m128i out = _mm_add_epi8(_mm_cvtsi32_si128(*outp),                             \
					                           _mm_packus_epi16(prediction, _mm_undefined_si128())); \
					uint32_t out32 = _mm_cvtsi128_si32(out);                                         \
					if (bytes_per_pixel == 3)                                                        \
						out32 = (out32&0xFFFFFF00) | (*outp&0x000000FF);                             \
					*outp = out32;                                                                   \
				} while(0)
			if (bytes_per_pixel == 3)
			{
				//one pixel at the time, no need for a scalar tail loop
				//shift one byte to the left, so we don't overflow the malloc at the last scanline (x=-1 is safe, it's all zero)
				//we don't actually change that byte, but it's UB anyways
				defilter_out--;
				for (size_t x=0;x<width_bytes;x+=3)
				{
					SIMD_PAETH(3);
				}
				break;
			}
			if (bytes_per_pixel == 4)
			{
				for (size_t x=0;x<width_bytes;x+=4)
				{
					SIMD_PAETH(4);
				}
				break;
			}
			
			// not SIMD_LOOP_TAIL, the below runs for bytes_per_pixel=1 aka palette
#endif
			for (size_t x=0;x<width_bytes;x++)
			{
				int a = defilter_out[x-bytes_per_pixel];
				int b = defilter_out[x-out_stride];
				int c = defilter_out[x-out_stride-bytes_per_pixel];
				
				int p = a+b-c;
				int pa = abs(p-a);
				int pb = abs(p-b);
				int pc = abs(p-c);
				
				int prediction;
				if (pa <= pb && pa <= pc) prediction = a;
				else if (pb <= pc) prediction = b;
				else prediction = c;
				
				defilter_out[x] += prediction;
			}
		}
		break;
		
		default:
			return false;
		}
	}
	return true;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x48, %rsp
movq %r9, 0x18(%rsp)
movq %r8, %r14
movq %rdx, 0x40(%rsp)
movq %rsi, %r11
leaq 0x1(%r8), %rax
movq %rax, 0x30(%rsp)
movq %rsi, %rax
negq %rax
movq %rax, 0x10(%rsp)
movl %ecx, 0xc(%rsp)
movslq %ecx, %rax
leaq 0x4(%rdi), %rbp
leaq 0x3(%rdi), %r15
movq %rdi, %r13
subq %rax, %r13
addq $0x4, %r13
pushq $0x4
popq %rbx
subq %rsi, %rbx
leaq (%rdi,%rbx), %rcx
movq %rcx, 0x20(%rsp)
subq %rax, %rbx
movq %rdi, 0x38(%rsp)
addq %rdi, %rbx
xorl %eax, %eax
movq %r8, 0x28(%rsp)
cmpq 0x18(%rsp), %rax
je 0x9cf8
movq %rax, (%rsp)
movq (%rsp), %rax
imulq 0x30(%rsp), %rax
movq 0x40(%rsp), %rcx
leaq (%rcx,%rax), %rsi
incq %rsi
movzbl -0x1(%rsi), %r12d
movq (%rsp), %rax
imulq %r11, %rax
movq 0x38(%rsp), %rcx
leaq (%rcx,%rax), %rdi
addq $0x4, %rdi
andl $0x0, -0x4(%rdi)
movq %r14, %rdx
movq %r13, %r14
movq %rbx, %r13
movq %r15, %rbx
movq %r11, %r15
callq 0x3600
pxor %xmm7, %xmm7
movq (%rsp), %rax
movq %r15, %r11
movq %rbx, %r15
movq %r13, %rbx
movq %r14, %r13
movq 0x28(%rsp), %r14
cmpq $0x4, %r12
ja 0x9cf8
leaq 0x1edc7(%rip), %rcx     # 0x28830
movslq (%rcx,%r12,4), %rax
addq %rcx, %rax
movq 0x20(%rsp), %r12
jmpq *%rax
movq (%rsp), %rax
incq %rax
addq %r11, %rbp
addq %r11, %r15
addq %r11, %r13
addq %r11, %rbx
addq %r11, %r12
movq %r12, 0x20(%rsp)
jmp 0x99e8
movl 0xc(%rsp), %eax
cmpl $0x3, %eax
je 0x9bbe
cmpl $0x4, %eax
jne 0x9c8f
movq 0x10(%rsp), %rax
addq %rbp, %rax
xorl %ecx, %ecx
cmpq %r14, %rcx
jae 0x9a77
movd -0x4(%rbp,%rcx), %xmm0
punpcklbw %xmm7, %xmm0    # xmm0 = xmm0[0],xmm7[0],xmm0[1],xmm7[1],xmm0[2],xmm7[2],xmm0[3],xmm7[3],xmm0[4],xmm7[4],xmm0[5],xmm7[5],xmm0[6],xmm7[6],xmm0[7],xmm7[7]
movd (%rax,%rcx), %xmm2
punpcklbw %xmm7, %xmm2    # xmm2 = xmm2[0],xmm7[0],xmm2[1],xmm7[1],xmm2[2],xmm7[2],xmm2[3],xmm7[3],xmm2[4],xmm7[4],xmm2[5],xmm7[5],xmm2[6],xmm7[6],xmm2[7],xmm7[7]
movd -0x4(%rax,%rcx), %xmm1
punpcklbw %xmm7, %xmm1    # xmm1 = xmm1[0],xmm7[0],xmm1[1],xmm7[1],xmm1[2],xmm7[2],xmm1[3],xmm7[3],xmm1[4],xmm7[4],xmm1[5],xmm7[5],xmm1[6],xmm7[6],xmm1[7],xmm7[7]
movdqa %xmm2, %xmm5
paddw %xmm0, %xmm5
psubw %xmm1, %xmm5
movdqa %xmm5, %xmm3
psubw %xmm0, %xmm3
pxor %xmm4, %xmm4
psubw %xmm3, %xmm4
pmaxsw %xmm3, %xmm4
movdqa %xmm5, %xmm6
psubw %xmm2, %xmm6
pxor %xmm3, %xmm3
psubw %xmm6, %xmm3
pmaxsw %xmm6, %xmm3
psubw %xmm1, %xmm5
pxor %xmm6, %xmm6
psubw %xmm5, %xmm6
pmaxsw %xmm5, %xmm6
movdqa %xmm3, %xmm5
pminsw %xmm6, %xmm5
pcmpgtw %xmm5, %xmm4
pcmpgtw %xmm6, %xmm3
pand %xmm4, %xmm3
movdqa %xmm3, %xmm5
pandn %xmm2, %xmm5
pand %xmm4, %xmm5
pandn %xmm0, %xmm4
pand %xmm1, %xmm3
por %xmm4, %xmm3
por %xmm5, %xmm3
packuswb %xmm3, %xmm3
movd (%rbp,%rcx), %xmm0
paddb %xmm3, %xmm0
movd %xmm0, (%rbp,%rcx)
addq $0x4, %rcx
jmp 0x9ab7
xorl %eax, %eax
cmpq %rax, %r14
je 0x9a77
movb (%r12,%rax), %cl
addb %cl, (%rbp,%rax)
incq %rax
jmp 0x9b6c
xorl %eax, %eax
cmpq %rax, %r14
je 0x9a77
movzbl (%r13,%rax), %ecx
movzbl (%r12,%rax), %edx
addl %ecx, %edx
shrl %edx
addb %dl, (%rbp,%rax)
incq %rax
jmp 0x9b84
xorl %eax, %eax
cmpq %rax, %r14
je 0x9a77
movb (%r13,%rax), %cl
addb %cl, (%rbp,%rax)
incq %rax
jmp 0x9ba7
movq 0x10(%rsp), %rax
addq %r15, %rax
xorl %ecx, %ecx
cmpq %r14, %rcx
jae 0x9a77
movd -0x3(%r15,%rcx), %xmm0
punpcklbw %xmm7, %xmm0    # xmm0 = xmm0[0],xmm7[0],xmm0[1],xmm7[1],xmm0[2],xmm7[2],xmm0[3],xmm7[3],xmm0[4],xmm7[4],xmm0[5],xmm7[5],xmm0[6],xmm7[6],xmm0[7],xmm7[7]
movd (%rax,%rcx), %xmm2
punpcklbw %xmm7, %xmm2    # xmm2 = xmm2[0],xmm7[0],xmm2[1],xmm7[1],xmm2[2],xmm7[2],xmm2[3],xmm7[3],xmm2[4],xmm7[4],xmm2[5],xmm7[5],xmm2[6],xmm7[6],xmm2[7],xmm7[7]
movd -0x3(%rax,%rcx), %xmm1
punpcklbw %xmm7, %xmm1    # xmm1 = xmm1[0],xmm7[0],xmm1[1],xmm7[1],xmm1[2],xmm7[2],xmm1[3],xmm7[3],xmm1[4],xmm7[4],xmm1[5],xmm7[5],xmm1[6],xmm7[6],xmm1[7],xmm7[7]
movdqa %xmm2, %xmm5
paddw %xmm0, %xmm5
psubw %xmm1, %xmm5
movdqa %xmm5, %xmm3
psubw %xmm0, %xmm3
pxor %xmm4, %xmm4
psubw %xmm3, %xmm4
pmaxsw %xmm3, %xmm4
movdqa %xmm5, %xmm6
psubw %xmm2, %xmm6
pxor %xmm3, %xmm3
psubw %xmm6, %xmm3
pmaxsw %xmm6, %xmm3
psubw %xmm1, %xmm5
pxor %xmm6, %xmm6
psubw %xmm5, %xmm6
pmaxsw %xmm5, %xmm6
movdqa %xmm3, %xmm5
pminsw %xmm6, %xmm5
pcmpgtw %xmm5, %xmm4
pcmpgtw %xmm6, %xmm3
pand %xmm4, %xmm3
movdqa %xmm3, %xmm5
pandn %xmm2, %xmm5
pand %xmm4, %xmm5
pandn %xmm0, %xmm4
pand %xmm1, %xmm3
por %xmm4, %xmm3
por %xmm5, %xmm3
packuswb %xmm3, %xmm3
movl (%r15,%rcx), %edx
movd %edx, %xmm0
paddb %xmm3, %xmm0
movd %xmm0, %esi
andl $0xffffff00, %esi       # imm = 0xFFFFFF00
movzbl %dl, %edx
orl %esi, %edx
movl %edx, (%r15,%rcx)
addq $0x3, %rcx
jmp 0x9bc8
xorl %eax, %eax
cmpq %rax, %r14
je 0x9a77
movzbl (%r13,%rax), %ecx
movzbl (%r12,%rax), %esi
movzbl (%rbx,%rax), %edx
leal (%rsi,%rcx), %r10d
subl %edx, %r10d
movl %r10d, %r8d
subl %ecx, %r8d
movl %r8d, %edi
negl %edi
cmovsl %r8d, %edi
movl %r10d, %r9d
subl %esi, %r9d
movl %r9d, %r8d
negl %r8d
cmovsl %r9d, %r8d
subl %edx, %r10d
movl %r10d, %r9d
negl %r9d
cmovsl %r10d, %r9d
cmpl %r9d, %r8d
ja 0x9ce3
movl %esi, %edx
cmpl %r9d, %edi
cmoval %edx, %ecx
cmpl %r8d, %edi
cmoval %edx, %ecx
addb %cl, (%rbp,%rax)
incq %rax
jmp 0x9c91
cmpq 0x18(%rsp), %rax
setae %al
addq $0x48, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
nop