// File: /workspace/github_cpp_makefile_star2_O2/Lut99[P]RayTracer/src/lib/LodePNG.cpp
// Function: filter(unsigned char*, unsigned char const*, unsigned int, unsigned int, LodePNGColorMode const*, LodePNGEncoderSettings const*)
static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h,
                       const LodePNGColorMode* info, const LodePNGEncoderSettings* settings) {
  /*
  For PNG filter method 0
  out must be a buffer with as size: h + (w * h * bpp + 7u) / 8u, because there are
  the scanlines with 1 extra byte per scanline
  */

  unsigned bpp = lodepng_get_bpp(info);
  /*the width of a scanline in bytes, not including the filter type*/
  size_t linebytes = (w * bpp + 7u) / 8u;
  /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/
  size_t bytewidth = (bpp + 7u) / 8u;
  const unsigned char* prevline = 0;
  unsigned x, y;
  unsigned error = 0;
  LodePNGFilterStrategy strategy = settings->filter_strategy;

  /*
  There is a heuristic called the minimum sum of absolute differences heuristic, suggested by the PNG standard:
   *  If the image type is Palette, or the bit depth is smaller than 8, then do not filter the image (i.e.
      use fixed filtering, with the filter None).
   * (The other case) If the image type is Grayscale or RGB (with or without Alpha), and the bit depth is
     not smaller than 8, then use adaptive filtering heuristic as follows: independently for each row, apply
     all five filters and select the filter that produces the smallest sum of absolute values per row.
  This heuristic is used if filter strategy is LFS_MINSUM and filter_palette_zero is true.

  If filter_palette_zero is true and filter_strategy is not LFS_MINSUM, the above heuristic is followed,
  but for "the other case", whatever strategy filter_strategy is set to instead of the minimum sum
  heuristic is used.
  */
  if(settings->filter_palette_zero &&
     (info->colortype == LCT_PALETTE || info->bitdepth < 8)) strategy = LFS_ZERO;

  if(bpp == 0) return 31; /*error: invalid color type*/

  if(strategy >= LFS_ZERO && strategy <= LFS_FOUR) {
    unsigned char type = (unsigned char)strategy;
    for(y = 0; y != h; ++y) {
      size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/
      size_t inindex = linebytes * y;
      out[outindex] = type; /*filter type byte*/
      filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, type);
      prevline = &in[inindex];
    }
  } else if(strategy == LFS_MINSUM) {
    /*adaptive filtering*/
    unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/
    size_t smallest = 0;
    unsigned char type, bestType = 0;

    for(type = 0; type != 5; ++type) {
      attempt[type] = (unsigned char*)lodepng_malloc(linebytes);
      if(!attempt[type]) return 83; /*alloc fail*/
    }

    if(!error) {
      for(y = 0; y != h; ++y) {
        /*try the 5 filter types*/
        for(type = 0; type != 5; ++type) {
          size_t sum = 0;
          filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type);

          /*calculate the sum of the result*/
          if(type == 0) {
            for(x = 0; x != linebytes; ++x) sum += (unsigned char)(attempt[type][x]);
          } else {
            for(x = 0; x != linebytes; ++x) {
              /*For differences, each byte should be treated as signed, values above 127 are negative
              (converted to signed char). Filtertype 0 isn't a difference though, so use unsigned there.
              This means filtertype 0 is almost never chosen, but that is justified.*/
              unsigned char s = attempt[type][x];
              sum += s < 128 ? s : (255U - s);
            }
          }

          /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/
          if(type == 0 || sum < smallest) {
            bestType = type;
            smallest = sum;
          }
        }

        prevline = &in[y * linebytes];

        /*now fill the out values*/
        out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/
        for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x];
      }
    }

    for(type = 0; type != 5; ++type) lodepng_free(attempt[type]);
  } else if(strategy == LFS_ENTROPY) {
    unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/
    size_t bestSum = 0;
    unsigned type, bestType = 0;
    unsigned count[256];

    for(type = 0; type != 5; ++type) {
      attempt[type] = (unsigned char*)lodepng_malloc(linebytes);
      if(!attempt[type]) return 83; /*alloc fail*/
    }

    for(y = 0; y != h; ++y) {
      /*try the 5 filter types*/
      for(type = 0; type != 5; ++type) {
        size_t sum = 0;
        filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type);
        for(x = 0; x != 256; ++x) count[x] = 0;
        for(x = 0; x != linebytes; ++x) ++count[attempt[type][x]];
        ++count[type]; /*the filter type itself is part of the scanline*/
        for(x = 0; x != 256; ++x) {
          sum += ilog2i(count[x]);
        }
        /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/
        if(type == 0 || sum > bestSum) {
          bestType = type;
          bestSum = sum;
        }
      }

      prevline = &in[y * linebytes];

      /*now fill the out values*/
      out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/
      for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x];
    }

    for(type = 0; type != 5; ++type) lodepng_free(attempt[type]);
  } else if(strategy == LFS_PREDEFINED) {
    for(y = 0; y != h; ++y) {
      size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/
      size_t inindex = linebytes * y;
      unsigned char type = settings->predefined_filters[y];
      out[outindex] = type; /*filter type byte*/
      filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, type);
      prevline = &in[inindex];
    }
  } else if(strategy == LFS_BRUTE_FORCE) {
    /*brute force filter chooser.
    deflate the scanline after every filter attempt to see which one deflates best.
    This is very slow and gives only slightly smaller, sometimes even larger, result*/
    size_t size[5];
    unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/
    size_t smallest = 0;
    unsigned type = 0, bestType = 0;
    unsigned char* dummy;
    LodePNGCompressSettings zlibsettings = settings->zlibsettings;
    /*use fixed tree on the attempts so that the tree is not adapted to the filtertype on purpose,
    to simulate the true case where the tree is the same for the whole image. Sometimes it gives
    better result with dynamic tree anyway. Using the fixed tree sometimes gives worse, but in rare
    cases better compression. It does make this a bit less slow, so it's worth doing this.*/
    zlibsettings.btype = 1;
    /*a custom encoder likely doesn't read the btype setting and is optimized for complete PNG
    images only, so disable it*/
    zlibsettings.custom_zlib = 0;
    zlibsettings.custom_deflate = 0;
    for(type = 0; type != 5; ++type) {
      attempt[type] = (unsigned char*)lodepng_malloc(linebytes);
      if(!attempt[type]) return 83; /*alloc fail*/
    }
    for(y = 0; y != h; ++y) /*try the 5 filter types*/ {
      for(type = 0; type != 5; ++type) {
        unsigned testsize = (unsigned)linebytes;
        /*if(testsize > 8) testsize /= 8;*/ /*it already works good enough by testing a part of the row*/

        filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type);
        size[type] = 0;
        dummy = 0;
        zlib_compress(&dummy, &size[type], attempt[type], testsize, &zlibsettings);
        lodepng_free(dummy);
        /*check if this is smallest size (or if type == 0 it's the first case so always store the values)*/
        if(type == 0 || size[type] < smallest) {
          bestType = type;
          smallest = size[type];
        }
      }
      prevline = &in[y * linebytes];
      out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/
      for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x];
    }
    for(type = 0; type != 5; ++type) lodepng_free(attempt[type]);
  }
  else return 88; /* unknown filter strategy */

  return error;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x4b8, %rsp            # imm = 0x4B8
movq %r9, %rbp
movq %r8, %r12
movl %ecx, %r13d
movl %edx, %ebx
movq %rsi, 0x20(%rsp)
movq %rdi, %r15
movq %r8, %rdi
callq 0x7607a
imull %eax, %ebx
addl $0x7, %ebx
shrl $0x3, %ebx
leal 0x7(%rax), %ecx
shrl $0x3, %ecx
movq %rcx, 0x28(%rsp)
movl 0x38(%rbp), %ecx
cmpl $0x0, 0x34(%rbp)
pushq $0x1f
popq %rdx
movl %r13d, %r13d
je 0x7da88
cmpl $0x3, (%r12)
je 0x7da7d
cmpl $0x8, 0x4(%r12)
jae 0x7da88
xorl %ecx, %ecx
testl %eax, %eax
jne 0x7da95
jmp 0x7e06b
testl %eax, %eax
je 0x7e06b
cmpl $0x4, %ecx
ja 0x7dad9
leaq 0x1(%rbx), %r14
incq %r15
movzbl %cl, %ebp
xorl %edx, %edx
movq 0x20(%rsp), %rax
subq $0x1, %r13
jb 0x7e069
movq %rax, %r12
movb %bpl, -0x1(%r15)
movq %r15, %rdi
movq %rax, %rsi
movq %rbx, %rcx
movq 0x28(%rsp), %r8
movl %ebp, %r9d
callq 0x7e07f
addq %r14, %r15
leaq (%r12,%rbx), %rax
movq %r12, %rdx
jmp 0x7daa6
addl $-0x5, %ecx
cmpl $0x3, %ecx
ja 0x7dc01
leaq 0x12b3c(%rip), %rax     # 0x90628
movslq (%rax,%rcx,4), %rcx
addq %rax, %rcx
movq %r15, 0x40(%rsp)
movq %r13, 0x30(%rsp)
jmpq *%rcx
xorl %r14d, %r14d
cmpq $0x5, %r14
je 0x7dd50
movq %rbx, %rdi
callq 0x525b0
movq %rax, 0xb0(%rsp,%r14,8)
incq %r14
testq %rax, %rax
jne 0x7db02
jmp 0x7dbfd
movups (%rbp), %xmm0
movups 0x10(%rbp), %xmm1
movups 0x20(%rbp), %xmm2
movaps %xmm0, 0xb0(%rsp)
movaps %xmm2, 0xd0(%rsp)
movaps %xmm1, 0xc0(%rsp)
movl $0x1, 0xb0(%rsp)
xorps %xmm0, %xmm0
movups %xmm0, 0xc8(%rsp)
xorl %r14d, %r14d
cmpq $0x5, %r14
je 0x7dc09
movq %rbx, %rdi
callq 0x525b0
movq %rax, 0x80(%rsp,%r14,8)
incq %r14
testq %rax, %rax
jne 0x7db66
jmp 0x7dbfd
leaq 0x1(%rbx), %r14
incq %r15
movq %r15, %r13
xorl %r15d, %r15d
xorl %edx, %edx
movq 0x20(%rsp), %rax
cmpq %r15, 0x30(%rsp)
je 0x7e069
movq %rax, %r12
movq 0x40(%rbp), %rax
movzbl (%rax,%r15), %r9d
movb %r9b, -0x1(%r13)
movq %r13, %rdi
movq %r12, %rsi
movq %rbx, %rcx
movq 0x28(%rsp), %r8
callq 0x7e07f
incq %r15
addq %r14, %r13
leaq (%r12,%rbx), %rax
movq %r12, %rdx
jmp 0x7db9e
xorl %r14d, %r14d
cmpq $0x5, %r14
je 0x7de6c
movq %rbx, %rdi
callq 0x525b0
movq %rax, 0x50(%rsp,%r14,8)
incq %r14
testq %rax, %rax
jne 0x7dbde
pushq $0x53
jmp 0x7dc03
pushq $0x58
popq %rdx
jmp 0x7e06b
leaq 0x1(%rbx), %rsi
leaq 0x1(%r15), %rdi
xorl %r8d, %r8d
movq $0x0, 0x8(%rsp)
xorl %r14d, %r14d
xorl %r15d, %r15d
movq %rsi, 0x38(%rsp)
cmpq %r13, %r8
movq 0x20(%rsp), %rax
je 0x7dd32
movq %rdi, 0x10(%rsp)
movq %r8, 0x18(%rsp)
imulq %rbx, %r8
addq %rax, %r8
movq %r8, (%rsp)
xorl %r13d, %r13d
xorl %r12d, %r12d
cmpq $0x5, %r12
je 0x7dcdd
movq 0x80(%rsp,%r12,8), %rbp
movzbl %r12b, %r9d
movq %rbp, %rdi
movq (%rsp), %rsi
movq 0x8(%rsp), %rdx
movq %rbx, %rcx
movq 0x28(%rsp), %r8
callq 0x7e07f
andq $0x0, 0x50(%rsp,%r12,8)
leaq (%rsp,%r13), %rsi
addq $0x50, %rsi
andq $0x0, 0x48(%rsp)
leaq 0x48(%rsp), %rdi
movq %rbp, %rdx
movq %rbx, %rcx
leaq 0xb0(%rsp), %r8
callq 0x7bc2f
movq 0x48(%rsp), %rdi
callq 0x52530
testq %r13, %r13
sete %al
movq 0x50(%rsp,%r12,8), %rcx
cmpq %r14, %rcx
setb %dl
orb %al, %dl
cmovneq %rcx, %r14
cmovnel %r12d, %r15d
incq %r12
addq $0x8, %r13
jmp 0x7dc51
movq 0x38(%rsp), %rsi
movq %rsi, %rax
movq 0x18(%rsp), %r8
imulq %r8, %rax
movq 0x40(%rsp), %rcx
movb %r15b, (%rcx,%rax)
movl %r15d, %eax
xorl %ecx, %ecx
movq 0x30(%rsp), %r13
movq 0x10(%rsp), %rdi
cmpq %rcx, %rbx
je 0x7dd1e
movq 0x80(%rsp,%rax,8), %rdx
movb (%rdx,%rcx), %dl
movb %dl, (%rdi,%rcx)
incq %rcx
jmp 0x7dd06
incq %r8
addq %rsi, %rdi
movq (%rsp), %rax
movq %rax, 0x8(%rsp)
jmp 0x7dc28
xorl %ebx, %ebx
cmpq $0x5, %rbx
je 0x7e069
movq 0x80(%rsp,%rbx,8), %rdi
callq 0x52530
incq %rbx
jmp 0x7dd34
leaq 0x1(%rbx), %rsi
leaq 0x1(%r15), %rdi
xorl %r8d, %r8d
xorl %r15d, %r15d
xorl %eax, %eax
movq $0x0, (%rsp)
movq %rsi, 0x18(%rsp)
cmpq %r13, %r8
movq 0x20(%rsp), %rcx
je 0x7e038
movq %rdi, 0x8(%rsp)
movq %r8, 0x10(%rsp)
movq %r8, %r13
imulq %rbx, %r13
addq %rcx, %r13
xorl %ebp, %ebp
movzbl %al, %r14d
cmpq $0x5, %rbp
je 0x7de1f
movq 0xb0(%rsp,%rbp,8), %r12
movzbl %bpl, %r9d
movq %r12, %rdi
movq %r13, %rsi
movq (%rsp), %rdx
movq %rbx, %rcx
movq 0x28(%rsp), %r8
callq 0x7e07f
xorl %ecx, %ecx
xorl %eax, %eax
testq %rbp, %rbp
je 0x7ddf5
cmpq %rcx, %rbx
je 0x7ddfa
movzbl (%r12,%rcx), %edx
movl %edx, %esi
xorl $0xff, %esi
testb %dl, %dl
cmovnsl %edx, %esi
addq %rsi, %rax
incq %rcx
jmp 0x7ddcb
movzbl (%r12,%rcx), %edx
addq %rdx, %rax
incq %rcx
cmpq %rcx, %rbx
jne 0x7ddea
testq %rbp, %rbp
sete %cl
cmpq %r15, %rax
setb %dl
orb %cl, %dl
cmovneq %rax, %r15
movzbl %bpl, %eax
cmovnel %eax, %r14d
incq %rbp
movl %r14d, %eax
jmp 0x7dd91
movq 0x18(%rsp), %rsi
movq %rsi, %rcx
movq 0x10(%rsp), %r8
imulq %r8, %rcx
movq 0x40(%rsp), %rdx
movb %r14b, (%rdx,%rcx)
xorl %ecx, %ecx
movq 0x8(%rsp), %rdi
cmpq %rcx, %rbx
je 0x7de58
movq 0xb0(%rsp,%r14,8), %rdx
movb (%rdx,%rcx), %dl
movb %dl, (%rdi,%rcx)
incq %rcx
jmp 0x7de40
incq %r8
addq %rsi, %rdi
movq %r13, (%rsp)
movq 0x30(%rsp), %r13
jmp 0x7dd6d
leaq 0x1(%rbx), %rax
movq %rax, 0x10(%rsp)
leaq 0x1(%r15), %r14
xorl %esi, %esi
pushq $0x1
popq %rbp
xorl %r13d, %r13d
xorl %r11d, %r11d
movq $0x0, 0x8(%rsp)
cmpq 0x30(%rsp), %rsi
movq 0x20(%rsp), %rax
je 0x7e052
movq %r14, 0x18(%rsp)
movq %rsi, 0x38(%rsp)
imulq %rbx, %rsi
addq %rax, %rsi
movq %rsi, (%rsp)
xorl %r15d, %r15d
cmpq $0x5, %r15
je 0x7dfeb
movl %r11d, %r14d
movq 0x50(%rsp,%r15,8), %r12
movzbl %r15b, %r9d
movq %r12, %rdi
movq (%rsp), %rsi
movq 0x8(%rsp), %rdx
movq %rbx, %rcx
movq 0x28(%rsp), %r8
callq 0x7e07f
xorl %eax, %eax
cmpq $0x100, %rax            # imm = 0x100
je 0x7defb
andl $0x0, 0xb0(%rsp,%rax,4)
incq %rax
jmp 0x7dee6
xorl %eax, %eax
movl %r14d, %r11d
cmpq %rax, %rbx
je 0x7df16
movzbl (%r12,%rax), %ecx
incl 0xb0(%rsp,%rcx,4)
incq %rax
jmp 0x7df00
incl 0xb0(%rsp,%r15,4)
xorl %edx, %edx
xorl %eax, %eax
cmpq $0x100, %rdx            # imm = 0x100
je 0x7dfcd
movl 0xb0(%rsp,%rdx,4), %edi
testq %rdi, %rdi
je 0x7dfc0
leaq (%rdi,%rdi), %rsi
movq %rdi, %r10
shlq $0x4, %r10
xorl %ecx, %ecx
movq %rdi, %r8
xorl %r9d, %r9d
cmpq $0xffff, %r8            # imm = 0xFFFF
jbe 0x7df6b
addq $0x10, %r9
shrq $0x10, %r8
addl $0x10, %ecx
addq %r10, %rsi
jmp 0x7df52
leaq (,%rdi,8), %r10
cmpq $0xff, %r8
jbe 0x7df8c
addq $0x8, %r9
shrq $0x8, %r8
addl $0x8, %ecx
addq %r10, %rsi
jmp 0x7df73
leaq (,%rdi,4), %r9
cmpq $0xf, %r8
jbe 0x7dfae
shrq $0x4, %r8
addl $0x4, %ecx
addq %r9, %rsi
jmp 0x7df94
shrq %r8
incl %ecx
addq %rdi, %rsi
cmpq $0x2, %r8
jae 0x7dfa6
movl %ebp, %edi
shll %cl, %edi
addq %rdi, %rdi
subq %rdi, %rsi
jmp 0x7dfc2
xorl %esi, %esi
addq %rsi, %rax
incq %rdx
jmp 0x7df22
testq %r15, %r15
sete %cl
cmpq %r13, %rax
seta %dl
orb %cl, %dl
cmovneq %rax, %r13
cmovnel %r15d, %r11d
incq %r15
jmp 0x7deb5
movq 0x10(%rsp), %rax
movq 0x38(%rsp), %rsi
imulq %rsi, %rax
movq 0x40(%rsp), %rcx
movb %r11b, (%rcx,%rax)
movl %r11d, %eax
xorl %ecx, %ecx
movq 0x18(%rsp), %r14
cmpq %rcx, %rbx
je 0x7e022
movq 0x50(%rsp,%rax,8), %rdx
movb (%rdx,%rcx), %dl
movb %dl, (%r14,%rcx)
incq %rcx
jmp 0x7e00c
incq %rsi
addq 0x10(%rsp), %r14
movq (%rsp), %rax
movq %rax, 0x8(%rsp)
jmp 0x7de8d
xorl %ebx, %ebx
cmpq $0x5, %rbx
je 0x7e069
movq 0xb0(%rsp,%rbx,8), %rdi
callq 0x52530
incq %rbx
jmp 0x7e03a
xorl %ebx, %ebx
cmpq $0x5, %rbx
je 0x7e069
movq 0x50(%rsp,%rbx,8), %rdi
callq 0x52530
incq %rbx
jmp 0x7e054
xorl %edx, %edx
movl %edx, %eax
addq $0x4b8, %rsp            # imm = 0x4B8
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq