// File: Alcaro[P]bridges/arlib/image-png-decode.cpp
// Function: bool image::png_unpack_plte<1>(unsigned char const*, unsigned long, unsigned int const*, unsigned int) const
bool image::png_unpack_plte(const uint8_t * source, size_t srcstride, const uint32_t * palette, uint32_t pallen) const
{
	uint32_t * pixels = this->pixels32;
	uint32_t width = this->width;
	uint32_t height = this->height;
	
	uint32_t nbytes = ((uint64_t)width*bpp_in+7)/8;
	
#ifdef __SSE2__
	__m128i bpp1_bit0 = _mm_set1_epi32(palette[0]); // hoist those (gcc won't automatically, can't prove height/nbytes != 0)
	__m128i bpp1_bitxor = _mm_set1_epi32(palette[0]^palette[1]);
	uint64_t bpp1_fanout_magic = 0x8040201008040201; // no clue know why this isn't hoisted though
#endif
	uint8_t bpp1_max_bits = 0;
	
	for (uint32_t y=0;y<height;y++)
	{
		//in this function, all pixels have one sample per pixel
		
		//this will write 7 pixels out of bounds for bpp=1 and size=int*8+1; this will be absorbed by our overallocations
		//TODO: test on 1*1, 1*2, 1*7, 1*8, 1*9, 1*1023, 1*1024 and 1*1025, bpp=1
		for (uint32_t byte=0;byte<nbytes;byte++)
		{
			uint8_t packed = source[byte];
#define  WRITE(xs, idx)  pixels[byte*8/bpp_in + (size_t)(xs)] = palette[(idx)]
			
			if (bpp_in == 1)
			{
				bpp1_max_bits |= packed;
				
#ifdef __SSE2__
				uint64_t fanout = (uint64_t)packed * bpp1_fanout_magic; // expand bits of packed to 0x80 bits of 64bit reg
				__m128i mask = _mm_cvtsi64_si128(fanout); // the other 56 bits of fanout are don't care terms
				mask = _mm_unpacklo_epi8(mask, mask); // left term is don't care, use mask because _mm_undefined_si128() optimizes poorly
				mask = _mm_srai_epi16(mask, 15); // replicate 0x80 bits of fanout to every bit of int16s
				
				_mm_storeu_si128((__m128i*)(pixels+byte*8+0),
					_mm_xor_si128(bpp1_bit0, _mm_and_si128(bpp1_bitxor, _mm_unpacklo_epi16(mask,mask))));
				_mm_storeu_si128((__m128i*)(pixels+byte*8+4),
					_mm_xor_si128(bpp1_bit0, _mm_and_si128(bpp1_bitxor, _mm_unpackhi_epi16(mask,mask))));
#else
				WRITE(0, (packed>>7)&1);
				WRITE(1, (packed>>6)&1);
				WRITE(2, (packed>>5)&1);
				WRITE(3, (packed>>4)&1);
				WRITE(4, (packed>>3)&1);
				WRITE(5, (packed>>2)&1);
				WRITE(6, (packed>>1)&1);
				WRITE(7, (packed>>0)&1);
#endif
			}
			if (bpp_in == 2)
			{
				// overflow if
				// (((x+y)^((x^y)&HIGH)) & HIGH) != 0
				// HIGH = bits to detect overflow into
				// x = a byte
				// y = 1<<nbits - pallen, repeated
				// only used for 2bpp, more straightforward algorithms are better for the other three
				uint32_t addend_2bpp = (4-pallen) * 0x55;
				if (UNLIKELY( ((packed+addend_2bpp)^((packed^addend_2bpp)&0x154)) & 0x154 )) return false;
				
				WRITE(0, (packed>>6)&3);
				WRITE(1, (packed>>4)&3);
				WRITE(2, (packed>>2)&3);
				WRITE(3, (packed>>0)&3);
			}
			if (bpp_in == 4)
			{
				if (UNLIKELY((uint32_t)(packed&15) >= pallen)) return false;
				if (UNLIKELY((uint32_t)(packed>>4) >= pallen)) return false;
				
				WRITE(0, (packed>>4)&15);
				WRITE(1, (packed>>0)&15);
			}
			if (bpp_in == 8)
			{
				if (UNLIKELY(packed >= pallen)) return false;
				
				WRITE(0, (packed>>0)&255);
			}
#undef WRITE
		}
		
		pixels += width;
		source += srcstride;
	}
	
	if (bpp_in == 1 && UNLIKELY(pallen == 1 && bpp1_max_bits != 0)) return false;
	return true;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x1d0, %rsp            # imm = 0x1D0
movq %rdi, -0x1b0(%rbp)
movq %rsi, -0x1b8(%rbp)
movq %rdx, -0x1c0(%rbp)
movq %rcx, -0x1c8(%rbp)
movl %r8d, -0x1cc(%rbp)
movq -0x1b0(%rbp), %rax
movq 0x10(%rax), %rcx
movq %rcx, -0x1d8(%rbp)
movl (%rax), %ecx
movl %ecx, -0x1dc(%rbp)
movl 0x4(%rax), %eax
movl %eax, -0x1e0(%rbp)
movl -0x1dc(%rbp), %eax
addq $0x7, %rax
shrq $0x3, %rax
movl %eax, -0x1e4(%rbp)
movq -0x1c8(%rbp), %rax
movl (%rax), %eax
movl %eax, -0x134(%rbp)
movl -0x134(%rbp), %eax
movl %eax, -0x24(%rbp)
movl %eax, -0x28(%rbp)
movl %eax, -0x2c(%rbp)
movl %eax, -0x30(%rbp)
movd -0x24(%rbp), %xmm0
movd -0x28(%rbp), %xmm1
punpckldq %xmm0, %xmm1    # xmm1 = xmm1[0],xmm0[0],xmm1[1],xmm0[1]
movd -0x2c(%rbp), %xmm2
movd -0x30(%rbp), %xmm0
punpckldq %xmm2, %xmm0    # xmm0 = xmm0[0],xmm2[0],xmm0[1],xmm2[1]
punpcklqdq %xmm1, %xmm0    # xmm0 = xmm0[0],xmm1[0]
movaps %xmm0, -0x40(%rbp)
movaps -0x40(%rbp), %xmm0
movaps %xmm0, -0x200(%rbp)
movq -0x1c8(%rbp), %rcx
movl (%rcx), %eax
movl 0x4(%rcx), %ecx
xorl %ecx, %eax
movl %eax, -0x138(%rbp)
movl -0x138(%rbp), %eax
movl %eax, -0x4(%rbp)
movl %eax, -0x8(%rbp)
movl %eax, -0xc(%rbp)
movl %eax, -0x10(%rbp)
movd -0x4(%rbp), %xmm0
movd -0x8(%rbp), %xmm1
punpckldq %xmm0, %xmm1    # xmm1 = xmm1[0],xmm0[0],xmm1[1],xmm0[1]
movd -0xc(%rbp), %xmm2
movd -0x10(%rbp), %xmm0
punpckldq %xmm2, %xmm0    # xmm0 = xmm0[0],xmm2[0],xmm0[1],xmm2[1]
punpcklqdq %xmm1, %xmm0    # xmm0 = xmm0[0],xmm1[0]
movdqa %xmm0, -0x20(%rbp)
movdqa -0x20(%rbp), %xmm0
movdqa %xmm0, -0x210(%rbp)
movabsq $-0x7fbfdfeff7fbfdff, %rax # imm = 0x8040201008040201
movq %rax, -0x218(%rbp)
movb $0x0, -0x219(%rbp)
movl $0x0, -0x220(%rbp)
movl -0x220(%rbp), %eax
cmpl -0x1e0(%rbp), %eax
jae 0x17563
movl $0x0, -0x224(%rbp)
movl -0x224(%rbp), %eax
cmpl -0x1e4(%rbp), %eax
jae 0x1751d
movq -0x1b8(%rbp), %rax
movl -0x224(%rbp), %ecx
movb (%rax,%rcx), %al
movb %al, -0x225(%rbp)
movzbl -0x225(%rbp), %ecx
movzbl -0x219(%rbp), %eax
orl %ecx, %eax
movb %al, -0x219(%rbp)
movzbl -0x225(%rbp), %eax
movq -0x218(%rbp), %rcx
imulq %rcx, %rax
movq %rax, -0x230(%rbp)
movq -0x230(%rbp), %rax
movq %rax, -0x120(%rbp)
movq -0x120(%rbp), %xmm0
movaps %xmm0, -0x130(%rbp)
movaps -0x130(%rbp), %xmm0
movaps %xmm0, -0x240(%rbp)
movaps -0x240(%rbp), %xmm0
movaps %xmm0, -0x190(%rbp)
movaps %xmm0, -0x1a0(%rbp)
movaps -0x190(%rbp), %xmm0
movaps -0x1a0(%rbp), %xmm1
punpcklbw %xmm1, %xmm0    # xmm0 = xmm0[0],xmm1[0],xmm0[1],xmm1[1],xmm0[2],xmm1[2],xmm0[3],xmm1[3],xmm0[4],xmm1[4],xmm0[5],xmm1[5],xmm0[6],xmm1[6],xmm0[7],xmm1[7]
movaps %xmm0, -0x240(%rbp)
movaps -0x240(%rbp), %xmm0
movaps %xmm0, -0x110(%rbp)
movl $0xf, -0x114(%rbp)
movaps -0x110(%rbp), %xmm0
movd -0x114(%rbp), %xmm1
psraw %xmm1, %xmm0
movaps %xmm0, -0x240(%rbp)
movq -0x1d8(%rbp), %rax
movl -0x224(%rbp), %ecx
shll $0x3, %ecx
leaq (%rax,%rcx,4), %rax
movaps -0x200(%rbp), %xmm1
movaps -0x210(%rbp), %xmm2
movaps -0x240(%rbp), %xmm0
movaps %xmm0, -0x70(%rbp)
movaps %xmm0, -0x80(%rbp)
movaps -0x70(%rbp), %xmm0
movaps -0x80(%rbp), %xmm3
punpcklwd %xmm3, %xmm0    # xmm0 = xmm0[0],xmm3[0],xmm0[1],xmm3[1],xmm0[2],xmm3[2],xmm0[3],xmm3[3]
movaps %xmm2, -0x150(%rbp)
movaps %xmm0, -0x160(%rbp)
movaps -0x150(%rbp), %xmm0
movaps -0x160(%rbp), %xmm2
pand %xmm2, %xmm0
movaps %xmm1, -0x90(%rbp)
movaps %xmm0, -0xa0(%rbp)
movaps -0x90(%rbp), %xmm0
movaps -0xa0(%rbp), %xmm1
pxor %xmm1, %xmm0
movq %rax, -0xc8(%rbp)
movaps %xmm0, -0xe0(%rbp)
movaps -0xe0(%rbp), %xmm0
movq -0xc8(%rbp), %rax
movups %xmm0, (%rax)
movq -0x1d8(%rbp), %rax
movl -0x224(%rbp), %ecx
shll $0x3, %ecx
leaq 0x10(%rax,%rcx,4), %rax
movaps -0x200(%rbp), %xmm1
movaps -0x210(%rbp), %xmm2
movaps -0x240(%rbp), %xmm0
movaps %xmm0, -0x50(%rbp)
movaps %xmm0, -0x60(%rbp)
movaps -0x50(%rbp), %xmm0
movaps -0x60(%rbp), %xmm3
punpckhwd %xmm3, %xmm0    # xmm0 = xmm0[4],xmm3[4],xmm0[5],xmm3[5],xmm0[6],xmm3[6],xmm0[7],xmm3[7]
movdqa %xmm2, -0x170(%rbp)
movdqa %xmm0, -0x180(%rbp)
movdqa -0x170(%rbp), %xmm0
pand -0x180(%rbp), %xmm0
movdqa %xmm1, -0xb0(%rbp)
movdqa %xmm0, -0xc0(%rbp)
movdqa -0xb0(%rbp), %xmm0
pxor -0xc0(%rbp), %xmm0
movq %rax, -0xe8(%rbp)
movdqa %xmm0, -0x100(%rbp)
movdqa -0x100(%rbp), %xmm0
movq -0xe8(%rbp), %rax
movdqu %xmm0, (%rax)
movl -0x224(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x224(%rbp)
jmp 0x172eb
movl -0x1dc(%rbp), %ecx
movq -0x1d8(%rbp), %rax
movl %ecx, %ecx
shlq $0x2, %rcx
addq %rcx, %rax
movq %rax, -0x1d8(%rbp)
movq -0x1c0(%rbp), %rax
addq -0x1b8(%rbp), %rax
movq %rax, -0x1b8(%rbp)
movl -0x220(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x220(%rbp)
jmp 0x172cf
xorl %eax, %eax
cmpl $0x1, -0x1cc(%rbp)
movb %al, -0x241(%rbp)
jne 0x17587
movzbl -0x219(%rbp), %eax
cmpl $0x0, %eax
setne %al
movb %al, -0x241(%rbp)
movb -0x241(%rbp), %al
xorb $-0x1, %al
xorb $-0x1, %al
testb $0x1, %al
jne 0x17597
jmp 0x175a0
movb $0x0, -0x1a1(%rbp)
jmp 0x175a7
movb $0x1, -0x1a1(%rbp)
movb -0x1a1(%rbp), %al
andb $0x1, %al
addq $0x1d0, %rsp            # imm = 0x1D0
popq %rbp
retq
nopl (%rax,%rax)