// File: jpbalarini[P]otsus/./framework/CImage.cpp
// Function: CImage::save_PNG(char*, int)
void CImage::save_PNG(char *filename, int bits_per_channel) {
  png_byte color_type;
  png_byte bit_depth;
  png_structp png_ptr;
  png_infop info_ptr;
  png_bytep * row_pointers;

  /* create file */
  FILE *fp = fopen(filename, "wb");
  if (!fp) {
    PRINT_ERROR("File %s could not be opened for writing\n", filename);
    exit(-1);
  }

  /* initialize stuff */
  png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

  if (!png_ptr) {
    PRINT_ERROR("png_create_write_struct failed\n");
    exit(-1);
  }

  info_ptr = png_create_info_struct(png_ptr);
  if (!info_ptr) {
    PRINT_ERROR("png_create_info_struct failed\n");
    exit(-1);
  }

  if (setjmp(png_jmpbuf(png_ptr))) {
    PRINT_ERROR("Error during init_io\n");
    exit(-1);
  }

  png_init_io(png_ptr, fp);

  /* write header */
  if (setjmp(png_jmpbuf(png_ptr))) {
    PRINT_ERROR("Error during writing header\n");
    exit(-1);
  }

  bit_depth = bits_per_channel;
  color_type = (this->get_num_channels() == 1 || bit_depth < 8 ?
                PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_RGB);

  int height = this->get_height();
  int width = this->get_width();
  
  png_set_IHDR(png_ptr, info_ptr, this->get_width(), height,
         bit_depth, color_type, PNG_INTERLACE_NONE,
         PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);

  png_write_info(png_ptr, info_ptr);

  // Create row pointers
  int bytes_per_row = png_get_rowbytes(png_ptr, info_ptr);  
  png_byte *png_buffer = new png_byte[this->Ny * bytes_per_row];
  
  row_pointers = (png_bytep*)png_malloc(png_ptr, this->Ny * sizeof(png_bytep));
  for (int y = 0; y < this->Ny; y++)
    row_pointers[y] = &png_buffer[y * bytes_per_row];

  /* write bytes */
  if (setjmp(png_jmpbuf(png_ptr))) {
    PRINT_ERROR("error during writing bytes\n");
    exit(-1);
  }

  int channels = this->get_num_channels();
  
  this->bits = bits_per_channel;

  if (bits_per_channel == 8) {
    for (int ch = 0; ch < channels; ch++) {
      float *orig = this->get_channel(ch);
      for (int y = 0; y < height; y++) {
        for (int x = 0; x < this->get_width(); x++)
          row_pointers[y][x * channels + ch] = orig[width * y + x];
      }
    }
  }

  if (bits_per_channel == 16) {
    float multiplier = (this->bits == 8 ? 256 : 1);
    for (int ch = 0; ch < channels; ch++) {
      float *orig = this->get_channel(ch);
      for (int y = 0; y < height; y++) {
        for (int x = 0; x < this->get_width(); x++) {
          int data = orig[width * y + x] * multiplier;
          int pos = (x * channels + ch) * 2;
          row_pointers[y][pos] = (data & 0xFF00) >> 8;
          row_pointers[y][pos + 1] = (data & 0x00FF);
        }
      }
    }
  }

  png_write_image(png_ptr, row_pointers);

  /* end write */
  if (setjmp(png_jmpbuf(png_ptr))) {
    PRINT_ERROR("error during end of write\n");
    exit(-1);
  }

  png_write_end(png_ptr, NULL);
  fclose(fp);

  /* cleanup heap allocation */
  // Free memory
  free(row_pointers);
  delete[] png_buffer;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0xe0, %rsp
movq %rdi, -0x8(%rbp)
movq %rsi, -0x10(%rbp)
movl %edx, -0x14(%rbp)
movq -0x8(%rbp), %rax
movq %rax, -0x98(%rbp)
movq -0x10(%rbp), %rdi
leaq 0x5665(%rip), %rsi      # 0xd141
callq 0x33c0
movq %rax, -0x38(%rbp)
cmpq $0x0, -0x38(%rbp)
jne 0x7b36
movq 0xb4c5(%rip), %rax      # 0x12fb8
movq (%rax), %rdi
leaq 0x55cc(%rip), %rsi      # 0xd0c9
movl $0x190, %edx            # imm = 0x190
leaq 0x57c1(%rip), %rcx      # 0xd2ca
movb $0x0, %al
callq 0x3470
movq 0xb4a1(%rip), %rax      # 0x12fb8
movq (%rax), %rdi
movq -0x10(%rbp), %rdx
leaq 0x59f5(%rip), %rsi      # 0xd51a
movb $0x0, %al
callq 0x3470
movl $0xffffffff, %edi       # imm = 0xFFFFFFFF
callq 0x33e0
leaq 0x596a(%rip), %rdi      # 0xd4a7
xorl %eax, %eax
movl %eax, %ecx
movq %rcx, %rsi
movq %rcx, %rdx
callq 0x3140
movq %rax, -0x20(%rbp)
cmpq $0x0, -0x20(%rbp)
jne 0x7b9d
movq 0xb45a(%rip), %rax      # 0x12fb8
movq (%rax), %rdi
leaq 0x5561(%rip), %rsi      # 0xd0c9
movl $0x198, %edx            # imm = 0x198
leaq 0x5756(%rip), %rcx      # 0xd2ca
movb $0x0, %al
callq 0x3470
movq 0xb436(%rip), %rax      # 0x12fb8
movq (%rax), %rdi
leaq 0x59b7(%rip), %rsi      # 0xd543
movb $0x0, %al
callq 0x3470
movl $0xffffffff, %edi       # imm = 0xFFFFFFFF
callq 0x33e0
movq -0x20(%rbp), %rdi
callq 0x3550
movq %rax, -0x28(%rbp)
cmpq $0x0, -0x28(%rbp)
jne 0x7bf7
movq 0xb400(%rip), %rax      # 0x12fb8
movq (%rax), %rdi
leaq 0x5507(%rip), %rsi      # 0xd0c9
movl $0x19e, %edx            # imm = 0x19E
leaq 0x56fc(%rip), %rcx      # 0xd2ca
movb $0x0, %al
callq 0x3470
movq 0xb3dc(%rip), %rax      # 0x12fb8
movq (%rax), %rdi
leaq 0x58e7(%rip), %rsi      # 0xd4cd
movb $0x0, %al
callq 0x3470
movl $0xffffffff, %edi       # imm = 0xFFFFFFFF
callq 0x33e0
movq -0x20(%rbp), %rdi
movq 0xb386(%rip), %rsi      # 0x12f88
movl $0xc8, %edx
callq 0x3350
movq %rax, %rdi
callq 0x3480
cmpl $0x0, %eax
je 0x7c5f
movq 0xb398(%rip), %rax      # 0x12fb8
movq (%rax), %rdi
leaq 0x549f(%rip), %rsi      # 0xd0c9
movl $0x1a3, %edx            # imm = 0x1A3
leaq 0x5694(%rip), %rcx      # 0xd2ca
movb $0x0, %al
callq 0x3470
movq 0xb374(%rip), %rax      # 0x12fb8
movq (%rax), %rdi
leaq 0x5915(%rip), %rsi      # 0xd563
movb $0x0, %al
callq 0x3470
movl $0xffffffff, %edi       # imm = 0xFFFFFFFF
callq 0x33e0
movq -0x20(%rbp), %rdi
movq -0x38(%rbp), %rsi
callq 0x35b0
movq -0x20(%rbp), %rdi
movq 0xb311(%rip), %rsi      # 0x12f88
movl $0xc8, %edx
callq 0x3350
movq %rax, %rdi
callq 0x3480
cmpl $0x0, %eax
je 0x7cd4
movq 0xb323(%rip), %rax      # 0x12fb8
movq (%rax), %rdi
leaq 0x542a(%rip), %rsi      # 0xd0c9
movl $0x1ab, %edx            # imm = 0x1AB
leaq 0x561f(%rip), %rcx      # 0xd2ca
movb $0x0, %al
callq 0x3470
movq 0xb2ff(%rip), %rax      # 0x12fb8
movq (%rax), %rdi
leaq 0x58b6(%rip), %rsi      # 0xd579
movb $0x0, %al
callq 0x3470
movl $0xffffffff, %edi       # imm = 0xFFFFFFFF
callq 0x33e0
movq -0x98(%rbp), %rdi
movl -0x14(%rbp), %eax
movb %al, -0x16(%rbp)
callq 0x69f0
movl %eax, %ecx
movb $0x1, %al
cmpl $0x1, %ecx
movb %al, -0x99(%rbp)
je 0x7d05
movzbl -0x16(%rbp), %eax
cmpl $0x8, %eax
setl %al
movb %al, -0x99(%rbp)
movq -0x98(%rbp), %rdi
movb -0x99(%rbp), %dl
movl $0x2, %eax
xorl %ecx, %ecx
testb $0x1, %dl
cmovnel %ecx, %eax
movb %al, -0x15(%rbp)
callq 0x82b0
movq -0x98(%rbp), %rdi
movl %eax, -0x3c(%rbp)
callq 0x82d0
movq -0x98(%rbp), %rdi
movl %eax, -0x40(%rbp)
movq -0x20(%rbp), %rax
movq %rax, -0xb0(%rbp)
movq -0x28(%rbp), %rax
movq %rax, -0xa8(%rbp)
callq 0x82d0
movq -0xb0(%rbp), %rdi
movq -0xa8(%rbp), %rsi
movl %eax, %edx
movl -0x3c(%rbp), %ecx
movzbl -0x16(%rbp), %r8d
movzbl -0x15(%rbp), %r9d
xorl %eax, %eax
movl $0x0, (%rsp)
movl $0x0, 0x8(%rsp)
movl $0x0, 0x10(%rsp)
callq 0x3510
movq -0x20(%rbp), %rdi
movq -0x28(%rbp), %rsi
callq 0x31e0
movq -0x20(%rbp), %rdi
movq -0x28(%rbp), %rsi
callq 0x30c0
movq %rax, %rcx
movq -0x98(%rbp), %rax
movl %ecx, -0x44(%rbp)
movl 0x4(%rax), %eax
imull -0x44(%rbp), %eax
movslq %eax, %rdi
callq 0x3030
movq %rax, %rcx
movq -0x98(%rbp), %rax
movq %rcx, -0x50(%rbp)
movq -0x20(%rbp), %rdi
movslq 0x4(%rax), %rsi
shlq $0x3, %rsi
callq 0x3520
movq %rax, -0x30(%rbp)
movl $0x0, -0x54(%rbp)
movq -0x98(%rbp), %rcx
movl -0x54(%rbp), %eax
cmpl 0x4(%rcx), %eax
jge 0x7e2c
movq -0x50(%rbp), %rdx
movl -0x54(%rbp), %eax
imull -0x44(%rbp), %eax
cltq
addq %rax, %rdx
movq -0x30(%rbp), %rax
movslq -0x54(%rbp), %rcx
movq %rdx, (%rax,%rcx,8)
movl -0x54(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x54(%rbp)
jmp 0x7df6
movq -0x20(%rbp), %rdi
movq 0xb151(%rip), %rsi      # 0x12f88
movl $0xc8, %edx
callq 0x3350
movq %rax, %rdi
callq 0x3480
cmpl $0x0, %eax
je 0x7e94
movq 0xb163(%rip), %rax      # 0x12fb8
movq (%rax), %rdi
leaq 0x526a(%rip), %rsi      # 0xd0c9
movl $0x1c6, %edx            # imm = 0x1C6
leaq 0x545f(%rip), %rcx      # 0xd2ca
movb $0x0, %al
callq 0x3470
movq 0xb13f(%rip), %rax      # 0x12fb8
movq (%rax), %rdi
leaq 0x5713(%rip), %rsi      # 0xd596
movb $0x0, %al
callq 0x3470
movl $0xffffffff, %edi       # imm = 0xFFFFFFFF
callq 0x33e0
movq -0x98(%rbp), %rdi
callq 0x69f0
movl %eax, %ecx
movq -0x98(%rbp), %rax
movl %ecx, -0x58(%rbp)
movl -0x14(%rbp), %ecx
movl %ecx, 0x8(%rax)
cmpl $0x8, -0x14(%rbp)
jne 0x7f7b
movl $0x0, -0x5c(%rbp)
movl -0x5c(%rbp), %eax
cmpl -0x58(%rbp), %eax
jge 0x7f79
movq -0x98(%rbp), %rdi
movl -0x5c(%rbp), %esi
callq 0x81e0
movq %rax, -0x68(%rbp)
movl $0x0, -0x6c(%rbp)
movl -0x6c(%rbp), %eax
cmpl -0x3c(%rbp), %eax
jge 0x7f69
movl $0x0, -0x70(%rbp)
movq -0x98(%rbp), %rdi
movl -0x70(%rbp), %eax
movl %eax, -0xb4(%rbp)
callq 0x82d0
movl %eax, %ecx
movl -0xb4(%rbp), %eax
cmpl %ecx, %eax
jge 0x7f5c
movq -0x68(%rbp), %rax
movl -0x40(%rbp), %ecx
movl -0x6c(%rbp), %edx
imull %edx, %ecx
movl -0x70(%rbp), %edx
addl %edx, %ecx
movslq %ecx, %rcx
cvttss2si (%rax,%rcx,4), %eax
movb %al, %dl
movq -0x30(%rbp), %rax
movslq -0x6c(%rbp), %rcx
movq (%rax,%rcx,8), %rax
movl -0x70(%rbp), %ecx
imull -0x58(%rbp), %ecx
addl -0x5c(%rbp), %ecx
movslq %ecx, %rcx
movb %dl, (%rax,%rcx)
movl -0x70(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x70(%rbp)
jmp 0x7ef8
jmp 0x7f5e
movl -0x6c(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x6c(%rbp)
jmp 0x7ee9
jmp 0x7f6b
movl -0x5c(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x5c(%rbp)
jmp 0x7ec3
jmp 0x7f7b
cmpl $0x10, -0x14(%rbp)
jne 0x80e9
movq -0x98(%rbp), %rax
movl 0x8(%rax), %edx
movl $0x1, %eax
movl $0x100, %ecx            # imm = 0x100
cmpl $0x8, %edx
cmovel %ecx, %eax
cvtsi2ss %eax, %xmm0
movss %xmm0, -0x74(%rbp)
movl $0x0, -0x78(%rbp)
movl -0x78(%rbp), %eax
cmpl -0x58(%rbp), %eax
jge 0x80e7
movq -0x98(%rbp), %rdi
movl -0x78(%rbp), %esi
callq 0x81e0
movq %rax, -0x80(%rbp)
movl $0x0, -0x84(%rbp)
movl -0x84(%rbp), %eax
cmpl -0x3c(%rbp), %eax
jge 0x80d7
movl $0x0, -0x88(%rbp)
movq -0x98(%rbp), %rdi
movl -0x88(%rbp), %eax
movl %eax, -0xb8(%rbp)
callq 0x82d0
movl %eax, %ecx
movl -0xb8(%rbp), %eax
cmpl %ecx, %eax
jge 0x80c1
movq -0x80(%rbp), %rax
movl -0x40(%rbp), %ecx
imull -0x84(%rbp), %ecx
addl -0x88(%rbp), %ecx
movslq %ecx, %rcx
movss (%rax,%rcx,4), %xmm0
mulss -0x74(%rbp), %xmm0
cvttss2si %xmm0, %eax
movl %eax, -0x8c(%rbp)
movl -0x88(%rbp), %eax
imull -0x58(%rbp), %eax
addl -0x78(%rbp), %eax
shll %eax
movl %eax, -0x90(%rbp)
movl -0x8c(%rbp), %eax
andl $0xff00, %eax           # imm = 0xFF00
sarl $0x8, %eax
movb %al, %dl
movq -0x30(%rbp), %rax
movslq -0x84(%rbp), %rcx
movq (%rax,%rcx,8), %rax
movslq -0x90(%rbp), %rcx
movb %dl, (%rax,%rcx)
movl -0x8c(%rbp), %eax
andl $0xff, %eax
movb %al, %dl
movq -0x30(%rbp), %rax
movslq -0x84(%rbp), %rcx
movq (%rax,%rcx,8), %rax
movl -0x90(%rbp), %ecx
addl $0x1, %ecx
movslq %ecx, %rcx
movb %dl, (%rax,%rcx)
movl -0x88(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x88(%rbp)
jmp 0x7ff1
jmp 0x80c3
movl -0x84(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x84(%rbp)
jmp 0x7fd8
jmp 0x80d9
movl -0x78(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x78(%rbp)
jmp 0x7faf
jmp 0x80e9
movq -0x20(%rbp), %rdi
movq -0x30(%rbp), %rsi
callq 0x3050
movq -0x20(%rbp), %rdi
movq 0xae87(%rip), %rsi      # 0x12f88
movl $0xc8, %edx
callq 0x3350
movq %rax, %rdi
callq 0x3480
cmpl $0x0, %eax
je 0x815e
movq 0xae99(%rip), %rax      # 0x12fb8
movq (%rax), %rdi
leaq 0x4fa0(%rip), %rsi      # 0xd0c9
movl $0x1eb, %edx            # imm = 0x1EB
leaq 0x5195(%rip), %rcx      # 0xd2ca
movb $0x0, %al
callq 0x3470
movq 0xae75(%rip), %rax      # 0x12fb8
movq (%rax), %rdi
leaq 0x5465(%rip), %rsi      # 0xd5b2
movb $0x0, %al
callq 0x3470
movl $0xffffffff, %edi       # imm = 0xFFFFFFFF
callq 0x33e0
movq -0x20(%rbp), %rdi
xorl %eax, %eax
movl %eax, %esi
callq 0x3310
movq -0x38(%rbp), %rdi
callq 0x32e0
movq -0x30(%rbp), %rdi
callq 0x33d0
movq -0x50(%rbp), %rax
movq %rax, -0xc0(%rbp)
cmpq $0x0, %rax
je 0x819a
movq -0xc0(%rbp), %rdi
callq 0x3440
addq $0xe0, %rsp
popq %rbp
retq
nopw %cs:(%rax,%rax)