// File: drummyfish[P]flowchart/src/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 + 7) / 8, 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 + 7) / 8;
  /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/
  size_t bytewidth = (bpp + 7) / 8;
  const unsigned char* prevline = 0;
  unsigned x, y;
  unsigned error = 0;
  /*
  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_HEURISTIC. Parts of it are used for filter strategies
   LFS_MINSUM and LFS_ZERO.
  */
  int heuristic_zero = info->colortype == LCT_PALETTE || info->bitdepth < 8;

  if(bpp == 0) return 31; /*error: invalid color type*/

  if((!heuristic_zero && settings->filter_strategy == LFS_HEURISTIC)||
      settings->filter_strategy == LFS_MINSUM)
  {
    /*adaptive filtering*/
    size_t sum[5];
    ucvector attempt[5]; /*five filtering attempts, one for each filter type*/
    size_t smallest = 0;
    unsigned type, bestType = 0;

    for(type = 0; type < 5; type++) ucvector_init(&attempt[type]);

    for(type = 0; type < 5; type++)
    {
      if(!ucvector_resize(&attempt[type], linebytes)) ERROR_BREAK(83 /*alloc fail*/);
    }

    if(!error)
    {
      for(y = 0; y < h; y++)
      {
        /*try the 5 filter types*/
        for(type = 0; type < 5; type++)
        {
          filterScanline(attempt[type].data, &in[y * linebytes], prevline, linebytes, bytewidth, type);

          /*calculate the sum of the result*/
          sum[type] = 0;
          /*note that not all pixels are checked to speed this up while still having probably the best choice*/
          for(x = 0; x < attempt[type].size; x+=3)
          {
            /*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.*/
            if(type == 0) sum[type] += (unsigned char)(attempt[type].data[x]);
            else
            {
              signed char s = (signed char)(attempt[type].data[x]);
              sum[type] += s < 0 ? -s : 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[type] < smallest)
          {
            bestType = type;
            smallest = sum[type];
          }
        }

        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].data[x];
      }
    }

    for(type = 0; type < 5; type++) ucvector_cleanup(&attempt[type]);
  }
  else if((heuristic_zero && settings->filter_strategy == LFS_HEURISTIC)||
      settings->filter_strategy == LFS_ZERO)
  {
    for(y = 0; y < h; y++)
    {
      size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/
      size_t inindex = linebytes * y;
      const unsigned TYPE = 0;
      out[outindex] = TYPE; /*filter type byte*/
      filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, TYPE);
      prevline = &in[inindex];
    }
  }
  else if(settings->filter_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 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 /*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];
    ucvector 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++)
    {
      ucvector_init(&attempt[type]);
      ucvector_resize(&attempt[type], linebytes); /*todo: give error if resize failed*/
    }
    for(y = 0; y < h; y++) /*try the 5 filter types*/
    {
      for(type = 0; type < 5; type++)
      {
        unsigned testsize = attempt[type].size;
        /*if(testsize > 8) testsize /= 8;*/ /*it already works good enough by testing a part of the row*/

        filterScanline(attempt[type].data, &in[y * linebytes], prevline, linebytes, bytewidth, type);
        size[type] = 0;
        dummy = 0;
        zlib_compress(&dummy, &size[type], attempt[type].data, testsize, &zlibsettings);
        myfree(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].data[x];
    }
    for(type = 0; type < 5; type++) ucvector_cleanup(&attempt[type]);
  }

  return error;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x250, %rsp            # imm = 0x250
movq %rdi, -0x10(%rbp)
movq %rsi, -0x18(%rbp)
movl %edx, -0x1c(%rbp)
movl %ecx, -0x20(%rbp)
movq %r8, -0x28(%rbp)
movq %r9, -0x30(%rbp)
movq -0x28(%rbp), %rdi
callq 0x5320
movl %eax, -0x34(%rbp)
movl -0x1c(%rbp), %eax
imull -0x34(%rbp), %eax
addl $0x7, %eax
shrl $0x3, %eax
movl %eax, %eax
movq %rax, -0x40(%rbp)
movl -0x34(%rbp), %eax
addl $0x7, %eax
shrl $0x3, %eax
movl %eax, %eax
movq %rax, -0x48(%rbp)
movq $0x0, -0x50(%rbp)
movl $0x0, -0x5c(%rbp)
movq -0x28(%rbp), %rcx
movb $0x1, %al
cmpl $0x3, (%rcx)
movb %al, -0x245(%rbp)
je 0x12460
movq -0x28(%rbp), %rax
cmpl $0x8, 0x4(%rax)
setb %al
movb %al, -0x245(%rbp)
movb -0x245(%rbp), %al
andb $0x1, %al
movzbl %al, %eax
movl %eax, -0x60(%rbp)
cmpl $0x0, -0x34(%rbp)
jne 0x12480
movl $0x1f, -0x4(%rbp)
jmp 0x12c67
cmpl $0x0, -0x60(%rbp)
jne 0x12490
movq -0x30(%rbp), %rax
cmpl $0x0, 0x2c(%rax)
je 0x1249e
movq -0x30(%rbp), %rax
cmpl $0x2, 0x2c(%rax)
jne 0x127ed
movq $0x0, -0x118(%rbp)
movl $0x0, -0x120(%rbp)
movl $0x0, -0x11c(%rbp)
cmpl $0x5, -0x11c(%rbp)
jae 0x124f0
movl -0x11c(%rbp), %eax
leaq -0x110(%rbp), %rdi
imulq $0x18, %rax, %rax
addq %rax, %rdi
callq 0xa980
movl -0x11c(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x11c(%rbp)
jmp 0x124bd
movl $0x0, -0x11c(%rbp)
cmpl $0x5, -0x11c(%rbp)
jae 0x12541
movl -0x11c(%rbp), %eax
leaq -0x110(%rbp), %rdi
imulq $0x18, %rax, %rax
addq %rax, %rdi
movq -0x40(%rbp), %rsi
callq 0xd730
cmpl $0x0, %eax
jne 0x1252e
movl $0x53, -0x5c(%rbp)
jmp 0x12541
jmp 0x12530
movl -0x11c(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x11c(%rbp)
jmp 0x124fa
cmpl $0x0, -0x5c(%rbp)
jne 0x127ab
movl $0x0, -0x58(%rbp)
movl -0x58(%rbp), %eax
cmpl -0x20(%rbp), %eax
jae 0x127a9
movl $0x0, -0x11c(%rbp)
cmpl $0x5, -0x11c(%rbp)
jae 0x1270e
movl -0x11c(%rbp), %eax
movl %eax, %ecx
leaq -0x110(%rbp), %rax
imulq $0x18, %rcx, %rcx
addq %rcx, %rax
movq (%rax), %rdi
movq -0x18(%rbp), %rsi
movl -0x58(%rbp), %eax
imulq -0x40(%rbp), %rax
addq %rax, %rsi
movq -0x50(%rbp), %rdx
movq -0x40(%rbp), %rcx
movq -0x48(%rbp), %r8
movl -0x11c(%rbp), %eax
movzbl %al, %r9d
callq 0x13050
movl -0x11c(%rbp), %eax
movq $0x0, -0x90(%rbp,%rax,8)
movl $0x0, -0x54(%rbp)
movl -0x54(%rbp), %eax
movl -0x11c(%rbp), %ecx
movl %ecx, %edx
leaq -0x110(%rbp), %rcx
imulq $0x18, %rdx, %rdx
addq %rdx, %rcx
cmpq 0x8(%rcx), %rax
jae 0x126b7
cmpl $0x0, -0x11c(%rbp)
jne 0x12637
movl -0x11c(%rbp), %eax
movl %eax, %ecx
leaq -0x110(%rbp), %rax
imulq $0x18, %rcx, %rcx
addq %rcx, %rax
movq (%rax), %rax
movl -0x54(%rbp), %ecx
movzbl (%rax,%rcx), %eax
movl %eax, %ecx
movl -0x11c(%rbp), %eax
addq -0x90(%rbp,%rax,8), %rcx
movq %rcx, -0x90(%rbp,%rax,8)
jmp 0x126a7
movl -0x11c(%rbp), %eax
movl %eax, %ecx
leaq -0x110(%rbp), %rax
imulq $0x18, %rcx, %rcx
addq %rcx, %rax
movq (%rax), %rax
movl -0x54(%rbp), %ecx
movb (%rax,%rcx), %al
movb %al, -0x121(%rbp)
movsbl -0x121(%rbp), %eax
cmpl $0x0, %eax
jge 0x1267b
movsbl -0x121(%rbp), %ecx
xorl %eax, %eax
subl %ecx, %eax
movl %eax, -0x24c(%rbp)
jmp 0x12688
movsbl -0x121(%rbp), %eax
movl %eax, -0x24c(%rbp)
movl -0x24c(%rbp), %eax
movslq %eax, %rcx
movl -0x11c(%rbp), %eax
addq -0x90(%rbp,%rax,8), %rcx
movq %rcx, -0x90(%rbp,%rax,8)
jmp 0x126a9
movl -0x54(%rbp), %eax
addl $0x3, %eax
movl %eax, -0x54(%rbp)
jmp 0x125d1
cmpl $0x0, -0x11c(%rbp)
je 0x126d7
movl -0x11c(%rbp), %eax
movq -0x90(%rbp,%rax,8), %rax
cmpq -0x118(%rbp), %rax
jae 0x126f8
movl -0x11c(%rbp), %eax
movl %eax, -0x120(%rbp)
movl -0x11c(%rbp), %eax
movq -0x90(%rbp,%rax,8), %rax
movq %rax, -0x118(%rbp)
jmp 0x126fa
movl -0x11c(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x11c(%rbp)
jmp 0x12568
movq -0x18(%rbp), %rax
movl -0x58(%rbp), %ecx
imulq -0x40(%rbp), %rcx
addq %rcx, %rax
movq %rax, -0x50(%rbp)
movl -0x120(%rbp), %eax
movb %al, %dl
movq -0x10(%rbp), %rax
movl -0x58(%rbp), %ecx
movq -0x40(%rbp), %rsi
addq $0x1, %rsi
imulq %rsi, %rcx
movb %dl, (%rax,%rcx)
movl $0x0, -0x54(%rbp)
movl -0x54(%rbp), %eax
cmpq -0x40(%rbp), %rax
jae 0x12799
movl -0x120(%rbp), %eax
movl %eax, %ecx
leaq -0x110(%rbp), %rax
imulq $0x18, %rcx, %rcx
addq %rcx, %rax
movq (%rax), %rax
movl -0x54(%rbp), %ecx
movb (%rax,%rcx), %dl
movq -0x10(%rbp), %rax
movl -0x58(%rbp), %ecx
movq -0x40(%rbp), %rsi
addq $0x1, %rsi
imulq %rsi, %rcx
addq $0x1, %rcx
movl -0x54(%rbp), %esi
addq %rsi, %rcx
movb %dl, (%rax,%rcx)
movl -0x54(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x54(%rbp)
jmp 0x12746
jmp 0x1279b
movl -0x58(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x58(%rbp)
jmp 0x12552
jmp 0x127ab
movl $0x0, -0x11c(%rbp)
cmpl $0x5, -0x11c(%rbp)
jae 0x127e8
movl -0x11c(%rbp), %eax
leaq -0x110(%rbp), %rdi
imulq $0x18, %rax, %rax
addq %rax, %rdi
callq 0x10950
movl -0x11c(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x11c(%rbp)
jmp 0x127b5
jmp 0x12c61
cmpl $0x0, -0x60(%rbp)
je 0x127fd
movq -0x30(%rbp), %rax
cmpl $0x0, 0x2c(%rax)
je 0x1280b
movq -0x30(%rbp), %rax
cmpl $0x1, 0x2c(%rax)
jne 0x128b2
movl $0x0, -0x58(%rbp)
movl -0x58(%rbp), %eax
cmpl -0x20(%rbp), %eax
jae 0x128ad
movq -0x40(%rbp), %rax
addq $0x1, %rax
movl -0x58(%rbp), %ecx
imulq %rcx, %rax
movq %rax, -0x130(%rbp)
movq -0x40(%rbp), %rax
movl -0x58(%rbp), %ecx
imulq %rcx, %rax
movq %rax, -0x138(%rbp)
movl $0x0, -0x13c(%rbp)
movq -0x10(%rbp), %rax
movq -0x130(%rbp), %rcx
movb $0x0, (%rax,%rcx)
movq -0x10(%rbp), %rdi
movq -0x130(%rbp), %rax
addq $0x1, %rax
addq %rax, %rdi
movq -0x18(%rbp), %rsi
addq -0x138(%rbp), %rsi
movq -0x50(%rbp), %rdx
movq -0x40(%rbp), %rcx
movq -0x48(%rbp), %r8
xorl %r9d, %r9d
callq 0x13050
movq -0x18(%rbp), %rax
addq -0x138(%rbp), %rax
movq %rax, -0x50(%rbp)
movl -0x58(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x58(%rbp)
jmp 0x12812
jmp 0x12c5f
movq -0x30(%rbp), %rax
cmpl $0x4, 0x2c(%rax)
jne 0x12980
movl $0x0, -0x58(%rbp)
movl -0x58(%rbp), %eax
cmpl -0x20(%rbp), %eax
jae 0x1297b
movq -0x40(%rbp), %rax
addq $0x1, %rax
movl -0x58(%rbp), %ecx
imulq %rcx, %rax
movq %rax, -0x148(%rbp)
movq -0x40(%rbp), %rax
movl -0x58(%rbp), %ecx
imulq %rcx, %rax
movq %rax, -0x150(%rbp)
movq -0x30(%rbp), %rax
movq 0x30(%rax), %rax
movl -0x58(%rbp), %ecx
movzbl (%rax,%rcx), %eax
movl %eax, -0x154(%rbp)
movl -0x154(%rbp), %eax
movb %al, %dl
movq -0x10(%rbp), %rax
movq -0x148(%rbp), %rcx
movb %dl, (%rax,%rcx)
movq -0x10(%rbp), %rdi
movq -0x148(%rbp), %rax
addq $0x1, %rax
addq %rax, %rdi
movq -0x18(%rbp), %rsi
addq -0x150(%rbp), %rsi
movq -0x50(%rbp), %rdx
movq -0x40(%rbp), %rcx
movq -0x48(%rbp), %r8
movl -0x154(%rbp), %eax
movzbl %al, %r9d
callq 0x13050
movq -0x18(%rbp), %rax
addq -0x150(%rbp), %rax
movq %rax, -0x50(%rbp)
movl -0x58(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x58(%rbp)
jmp 0x128c7
jmp 0x12c5d
movq $0x0, -0x208(%rbp)
movl $0x0, -0x20c(%rbp)
movl $0x0, -0x210(%rbp)
movq -0x30(%rbp), %rsi
leaq -0x240(%rbp), %rdi
movl $0x28, %edx
callq 0x31c0
movl $0x1, -0x240(%rbp)
movq $0x0, -0x230(%rbp)
movq $0x0, -0x228(%rbp)
movl $0x0, -0x20c(%rbp)
cmpl $0x5, -0x20c(%rbp)
jae 0x12a2e
movl -0x20c(%rbp), %eax
leaq -0x200(%rbp), %rdi
imulq $0x18, %rax, %rax
addq %rax, %rdi
callq 0xa980
movl -0x20c(%rbp), %eax
leaq -0x200(%rbp), %rdi
imulq $0x18, %rax, %rax
addq %rax, %rdi
movq -0x40(%rbp), %rsi
callq 0xd730
movl -0x20c(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x20c(%rbp)
jmp 0x129de
movl $0x0, -0x58(%rbp)
movl -0x58(%rbp), %eax
cmpl -0x20(%rbp), %eax
jae 0x12c1e
movl $0x0, -0x20c(%rbp)
cmpl $0x5, -0x20c(%rbp)
jae 0x12b83
movl -0x20c(%rbp), %eax
movl %eax, %ecx
leaq -0x200(%rbp), %rax
imulq $0x18, %rcx, %rcx
addq %rcx, %rax
movq 0x8(%rax), %rax
movl %eax, -0x244(%rbp)
movl -0x20c(%rbp), %eax
movl %eax, %ecx
leaq -0x200(%rbp), %rax
imulq $0x18, %rcx, %rcx
addq %rcx, %rax
movq (%rax), %rdi
movq -0x18(%rbp), %rsi
movl -0x58(%rbp), %eax
imulq -0x40(%rbp), %rax
addq %rax, %rsi
movq -0x50(%rbp), %rdx
movq -0x40(%rbp), %rcx
movq -0x48(%rbp), %r8
movl -0x20c(%rbp), %eax
movzbl %al, %r9d
callq 0x13050
movl -0x20c(%rbp), %eax
movq $0x0, -0x180(%rbp,%rax,8)
movq $0x0, -0x218(%rbp)
movl -0x20c(%rbp), %eax
leaq -0x180(%rbp), %rsi
shlq $0x3, %rax
addq %rax, %rsi
movl -0x20c(%rbp), %eax
movl %eax, %ecx
leaq -0x200(%rbp), %rax
imulq $0x18, %rcx, %rcx
addq %rcx, %rax
movq (%rax), %rdx
movl -0x244(%rbp), %eax
movl %eax, %ecx
leaq -0x218(%rbp), %rdi
leaq -0x240(%rbp), %r8
callq 0xc440
movq -0x218(%rbp), %rdi
callq 0x4050
cmpl $0x0, -0x20c(%rbp)
je 0x12b4c
movl -0x20c(%rbp), %eax
movq -0x180(%rbp,%rax,8), %rax
cmpq -0x208(%rbp), %rax
jae 0x12b6d
movl -0x20c(%rbp), %eax
movl %eax, -0x210(%rbp)
movl -0x20c(%rbp), %eax
movq -0x180(%rbp,%rax,8), %rax
movq %rax, -0x208(%rbp)
jmp 0x12b6f
movl -0x20c(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x20c(%rbp)
jmp 0x12a4b
movq -0x18(%rbp), %rax
movl -0x58(%rbp), %ecx
imulq -0x40(%rbp), %rcx
addq %rcx, %rax
movq %rax, -0x50(%rbp)
movl -0x210(%rbp), %eax
movb %al, %dl
movq -0x10(%rbp), %rax
movl -0x58(%rbp), %ecx
movq -0x40(%rbp), %rsi
addq $0x1, %rsi
imulq %rsi, %rcx
movb %dl, (%rax,%rcx)
movl $0x0, -0x54(%rbp)
movl -0x54(%rbp), %eax
cmpq -0x40(%rbp), %rax
jae 0x12c0e
movl -0x210(%rbp), %eax
movl %eax, %ecx
leaq -0x200(%rbp), %rax
imulq $0x18, %rcx, %rcx
addq %rcx, %rax
movq (%rax), %rax
movl -0x54(%rbp), %ecx
movb (%rax,%rcx), %dl
movq -0x10(%rbp), %rax
movl -0x58(%rbp), %ecx
movq -0x40(%rbp), %rsi
addq $0x1, %rsi
imulq %rsi, %rcx
addq $0x1, %rcx
movl -0x54(%rbp), %esi
addq %rsi, %rcx
movb %dl, (%rax,%rcx)
movl -0x54(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x54(%rbp)
jmp 0x12bbb
jmp 0x12c10
movl -0x58(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x58(%rbp)
jmp 0x12a35
movl $0x0, -0x20c(%rbp)
cmpl $0x5, -0x20c(%rbp)
jae 0x12c5b
movl -0x20c(%rbp), %eax
leaq -0x200(%rbp), %rdi
imulq $0x18, %rax, %rax
addq %rax, %rdi
callq 0x10950
movl -0x20c(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x20c(%rbp)
jmp 0x12c28
jmp 0x12c5d
jmp 0x12c5f
jmp 0x12c61
movl -0x5c(%rbp), %eax
movl %eax, -0x4(%rbp)
movl -0x4(%rbp), %eax
addq $0x250, %rsp            # imm = 0x250
popq %rbp
retq
nopw %cs:(%rax,%rax)