// File: /workspace/github_cpp_makefile_star2_O3/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
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x38, %rsp
movl %edx, %ebp
movq %rsi, %rbx
movq %rdi, %r14
leaq 0x2898(%rip), %rsi      # 0x80ff
movq %rbx, %rdi
callq 0x32f0
testq %rax, %rax
je 0x5ba6
movq %rax, %r15
leaq 0x2c03(%rip), %rdi      # 0x8485
xorl %eax, %eax
movl %eax, %ecx
movq %rcx, %rsi
movq %rcx, %rdx
callq 0x3130
testq %rax, %rax
je 0x5bae
movq %rax, %rbx
movq %rax, %rdi
callq 0x34a0
testq %rax, %rax
je 0x5bb3
movq %rax, %r13
movq 0x66d0(%rip), %rsi      # 0xbf88
movl $0xc8, %edx
movq %rbx, %rdi
callq 0x3290
movq %rax, %rdi
callq 0x3390
testl %eax, %eax
jne 0x5bb8
movq %rbx, %rdi
movq %r15, 0x30(%rsp)
movq %r15, %rsi
callq 0x34e0
movq 0x669c(%rip), %rsi      # 0xbf88
movl $0xc8, %edx
movq %rbx, %rdi
callq 0x3290
movq %rax, %rdi
callq 0x3390
testl %eax, %eax
jne 0x5bbd
xorl %eax, %eax
cmpl $0x1, 0x18(%r14)
setne %cl
movl %ebp, 0x14(%rsp)
movzbl %bpl, %r8d
movb %cl, %al
addl %eax, %eax
xorl %r10d, %r10d
cmpl $0x8, %r8d
movl (%r14), %r15d
movl 0x4(%r14), %r12d
movl %eax, %r9d
cmovbl %r10d, %r9d
subq $0x8, %rsp
movl %r10d, %eax
movq %rbx, %rdi
movq %r13, %rsi
movl %r15d, %edx
movl %r12d, %ecx
pushq %rax
pushq %rax
pushq %rax
callq 0x3450
addq $0x20, %rsp
movq %rbx, %rdi
movq %r13, %rsi
callq 0x3190
movq %rbx, %rdi
movq %r13, %rsi
callq 0x30b0
movq %rax, %rbp
movq %rbx, 0x18(%rsp)
movslq 0x4(%r14), %rbx
movslq %eax, %rdi
imulq %rbx, %rdi
callq 0x3030
movq %rax, %r13
movq %rbx, %rsi
movq 0x18(%rsp), %rbx
shlq $0x3, %rsi
movq %rbx, %rdi
callq 0x3460
movq %rax, %r8
movslq 0x4(%r14), %rax
testq %rax, %rax
jle 0x59c3
movslq %ebp, %rcx
xorl %edx, %edx
movl %edx, %esi
movq %r13, %rdx
movq %rdx, (%r8,%rsi,8)
movq %rsi, %rdi
incq %rdi
addq %rcx, %rdx
movq %rdi, %rsi
cmpq %rdi, %rax
jne 0x59ae
movq %r8, (%rsp)
movq 0x65ba(%rip), %rsi      # 0xbf88
movl $0xc8, %edx
movq %rbx, %rdi
callq 0x3290
movq %rax, %rdi
callq 0x3390
testl %eax, %eax
jne 0x5bc2
movq %r13, 0x28(%rsp)
movslq %r15d, %rbx
movl 0x18(%r14), %eax
movl %eax, %r15d
movl 0x14(%rsp), %eax
movl %eax, 0x8(%r14)
cmpl $0x10, %eax
je 0x5a9b
cmpl $0x8, %eax
movq (%rsp), %r9
jne 0x5b3c
movq %r15, %rax
testl %eax, %eax
jle 0x5b3c
shlq $0x2, %rbx
xorl %ebp, %ebp
movl %ebp, %r13d
movl %r12d, 0xc(%rsp)
movl %r13d, %esi
movq %r14, %rdi
callq 0x5bd6
movq (%rsp), %r9
cmpl $0x0, 0xc(%rsp)
jle 0x5a88
movl (%r14), %edx
movl %ebp, %ecx
testl %edx, %edx
jle 0x5a77
movq (%r9,%rcx,8), %rdx
addq %r13, %rdx
movl %ebp, %edi
cvttss2si (%rax,%rdi,4), %esi
movb %sil, (%rdx)
movq %rdi, %r8
incq %r8
movslq (%r14), %rsi
addq %r15, %rdx
movq %r8, %rdi
cmpq %rsi, %r8
jl 0x5a59
movl %esi, %edx
movq %rcx, %rsi
incq %rsi
addq %rbx, %rax
movq %rsi, %rcx
cmpq %r12, %rsi
jne 0x5a4c
movq %r13, %rax
incq %rax
movq %rax, %r13
cmpq %r15, %rax
jne 0x5a31
jmp 0x5b3c
testl %r15d, %r15d
movq (%rsp), %r9
jle 0x5b3c
movq %r12, %rax
movq %rbx, %rbp
shlq $0x2, %rbp
leaq (%r15,%r15), %rbx
movl $0x1, %r13d
xorl %ecx, %ecx
movl %eax, 0x10(%rsp)
movq %rcx, %rax
movq %rax, 0x20(%rsp)
movl %eax, %esi
movq %r14, %rdi
callq 0x5bd6
xorl %r10d, %r10d
movq (%rsp), %r9
cmpl $0x0, 0x10(%rsp)
jle 0x5b28
movl (%r14), %edx
movl %r10d, %ecx
testl %edx, %edx
jle 0x5b17
movq (%r9,%rcx,8), %rsi
addq %r13, %rsi
movl %r10d, %edi
cvttss2si (%rax,%rdi,4), %edx
rolw $0x8, %dx
movw %dx, -0x1(%rsi)
movq %rdi, %r8
incq %r8
movslq (%r14), %rdx
addq %rbx, %rsi
movq %r8, %rdi
cmpq %rdx, %r8
jl 0x5af6
movq %rcx, %rsi
incq %rsi
addq %rbp, %rax
movq %rsi, %rcx
cmpq %r12, %rsi
jne 0x5ae8
movq 0x20(%rsp), %rcx
incq %rcx
addq $0x2, %r13
movq %rcx, %rax
cmpq %r15, %rcx
jne 0x5ac5
movq 0x18(%rsp), %rbx
movq %rbx, %rdi
movq %r9, %rsi
movq %r9, %r14
callq 0x3050
movq 0x6432(%rip), %rsi      # 0xbf88
movl $0xc8, %edx
movq %rbx, %rdi
callq 0x3290
movq %rax, %rdi
callq 0x3390
testl %eax, %eax
jne 0x5bc7
xorl %eax, %eax
movl %eax, %esi
movq %rbx, %rdi
callq 0x3270
movq 0x30(%rsp), %rdi
callq 0x3240
movq %r14, %rdi
callq 0x3300
movq 0x28(%rsp), %rdi
callq 0x3350
addq $0x38, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq %rbx, %rdi
callq 0x3b7e
callq 0x3b3c
callq 0x3afa
callq 0x39f2
callq 0x3a34
callq 0x3a76
callq 0x3ab8