// File: /workspace/github_cpp_makefile_star2_O3/misohena[P]drawstars/./image_png.h
// Function: bool writePNGFile<Image<ColorRGB<double>>>(char const*, Image<ColorRGB<double>> const&)
bool writePNGFile(const char *filename, const ImageT &image)
{
    png_bytep *rows = nullptr;

    File file(filename, "wb");
    if(!file){
        return false;
    }

    const unsigned int width = image.width();
    const unsigned int height = image.height();
    const unsigned int lineStride = 3 * width;
    std::unique_ptr<std::uint8_t[]> body(new std::uint8_t[lineStride * height]);
    for(int y = 0; y < height; ++y){
        auto dst = body.get() + y * lineStride;
        auto src = image.at(0, y);
        for(int x = 0; x < width; ++x, ++src, dst += 3){
            using VT = typename ImageT::PixelType::ValueType;
            dst[0] = static_cast<std::uint8_t>(clamp(255*src->r, VT(0), VT(255)));
            dst[1] = static_cast<std::uint8_t>(clamp(255*src->g, VT(0), VT(255)));
            dst[2] = static_cast<std::uint8_t>(clamp(255*src->b, VT(0), VT(255)));
        }
    }


    const int color_type = PNG_COLOR_TYPE_RGB; //PNG_COLOR_TYPE_RGB_ALPHA, PNG_COLOR_TYPE_GRAY

    // init png objects.
    png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if(!png_ptr){
        return false;
    }
    png_infop info_ptr = png_create_info_struct(png_ptr);
    if(!info_ptr){
        png_destroy_read_struct(&png_ptr, NULL, NULL);
        return false;
    }

    // error handler.
    if(setjmp(png_jmpbuf(png_ptr))){
        if(rows){
            png_free(png_ptr, rows);
        }
        png_destroy_write_struct(&png_ptr, &info_ptr);
        return false;
    }

    // setup i/o function.
    //png_set_write_fn(png_ptr, stream, &stream_write, &stream_flush);
    png_init_io(png_ptr, file);

    png_set_IHDR(
        png_ptr,
        info_ptr,
        width,
        height,
        8,
        color_type,
        PNG_INTERLACE_NONE,
        PNG_COMPRESSION_TYPE_DEFAULT,
        PNG_FILTER_TYPE_DEFAULT);

    /// @todo support palette
    //png_set_PLTE(png_ptr, info_ptr, 

    rows = (png_bytep *)png_malloc(png_ptr, sizeof(png_bytep) * height);

    for(unsigned int y = 0; y < height; ++y){
        rows[y] = const_cast<png_bytep>(body.get() + y * lineStride);
    }
    png_set_rows(png_ptr, info_ptr, rows);

    const int transform = PNG_TRANSFORM_IDENTITY; //PNG_TRANSFORM_BGR;

    png_write_png(png_ptr, info_ptr, transform, NULL);

    png_free(png_ptr, rows);
    png_destroy_write_struct(&png_ptr, &info_ptr);
    return true;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x18, %rsp
movq %rsi, %r14
leaq 0x3c54(%rip), %rsi      # 0xd548
callq 0x4350
testq %rax, %rax
je 0x9a65
movq %rax, %rbx
movl (%r14), %ebp
movl 0x4(%r14), %eax
movl %eax, %r13d
movl %r13d, %eax
imull %ebp, %eax
leal (%rax,%rax,2), %eax
movl %eax, %edi
callq 0x4030
movq %rax, %r15
testq %r13, %r13
je 0x99f7
movq 0x8(%r14), %rax
movl %ebp, %ecx
leal (%rcx,%rcx,2), %ecx
xorl %edx, %edx
movl %edx, %r8d
movsd 0x3727(%rip), %xmm0     # 0xd068
xorpd %xmm1, %xmm1
movl %edx, %esi
movl %edx, %edi
testl %ebp, %ebp
je 0x99e1
movl %esi, %r9d
addq %r15, %r9
movl %edi, %r10d
leaq (%r10,%r10,2), %r10
leaq (%rax,%r10,8), %r10
movl %edx, %r14d
movl %ebp, %r11d
movsd (%r10,%r14,8), %xmm2
mulsd %xmm0, %xmm2
movapd %xmm0, %xmm3
minsd %xmm2, %xmm3
cmpltsd %xmm1, %xmm2
andnpd %xmm3, %xmm2
cvttsd2si %xmm2, %r12d
movb %r12b, (%r9,%r14)
movsd 0x8(%r10,%r14,8), %xmm2
mulsd %xmm0, %xmm2
movapd %xmm0, %xmm3
minsd %xmm2, %xmm3
cmpltsd %xmm1, %xmm2
andnpd %xmm3, %xmm2
cvttsd2si %xmm2, %r12d
movb %r12b, 0x1(%r9,%r14)
movsd 0x10(%r10,%r14,8), %xmm2
mulsd %xmm0, %xmm2
movapd %xmm0, %xmm3
minsd %xmm2, %xmm3
cmpltsd %xmm1, %xmm2
andnpd %xmm3, %xmm2
cvttsd2si %xmm2, %r12d
movb %r12b, 0x2(%r9,%r14)
addq $0x3, %r14
decl %r11d
jne 0x9968
movq %r8, %r9
incq %r9
addl %ebp, %edi
addl %ecx, %esi
movq %r9, %r8
cmpq %r13, %r9
jne 0x9949
movq %r15, %r14
leaq 0x3b4a(%rip), %rdi      # 0xd54b
xorl %eax, %eax
movl %eax, %ecx
movq %rcx, %rsi
movq %rcx, %rdx
callq 0x40e0
movq %rax, 0x8(%rsp)
testq %rax, %rax
je 0x9a5e
movq %rax, %r12
movq %rax, %rdi
callq 0x4500
movq %rax, 0x10(%rsp)
testq %rax, %rax
je 0x9a6c
movq 0x857a(%rip), %rsi      # 0x11fb0
movl $0xc8, %edx
movq %r12, %rdi
callq 0x4330
movq %rax, %rdi
callq 0x4400
testl %eax, %eax
je 0x9a87
leaq 0x8(%rsp), %rdi
leaq 0x10(%rsp), %rsi
callq 0x4220
xorl %eax, %eax
jmp 0x9b49
xorl %eax, %eax
jmp 0x9b5d
xorl %eax, %eax
movl %eax, %ebp
movl %eax, %edx
leaq 0x8(%rsp), %rdi
movq %rdx, %rsi
callq 0x41b0
movb %bpl, %al
jmp 0x9b49
movq 0x8(%rsp), %rdi
movq %rbx, %rsi
callq 0x4550
movl %r13d, %ecx
movq 0x8(%rsp), %rdi
movq 0x10(%rsp), %rsi
subq $0x8, %rsp
xorl %eax, %eax
movl %eax, %r10d
movl %ebp, %edx
movl $0x8, %r8d
movl $0x2, %r9d
pushq %r10
pushq %r10
pushq %rax
callq 0x44a0
addq $0x20, %rsp
movq 0x8(%rsp), %rdi
leaq (,%r13,8), %rsi
callq 0x44b0
movq %rax, %r12
testq %r13, %r13
je 0x9b02
movl %ebp, %eax
leal (%rax,%rax,2), %eax
xorl %ecx, %ecx
movl %ecx, %edx
movl %ecx, %esi
addq %r15, %rsi
movq %rsi, (%r12,%rdx,8)
movq %rdx, %rsi
incq %rsi
addl %eax, %ecx
movq %rsi, %rdx
cmpq %rsi, %r13
jne 0x9ae9
movq 0x8(%rsp), %rdi
movq 0x10(%rsp), %rsi
movq %r12, %rdx
callq 0x4460
movq 0x8(%rsp), %rdi
movq 0x10(%rsp), %rsi
xorl %edx, %edx
movl %edx, %ecx
callq 0x40f0
movq 0x8(%rsp), %rdi
movq %r12, %rsi
callq 0x41d0
movb $0x1, %bpl
leaq 0x8(%rsp), %rdi
leaq 0x10(%rsp), %rsi
callq 0x4220
movb %bpl, %al
movl %eax, %ebp
movq %r14, %rdi
callq 0x4370
movq %rbx, %rdi
callq 0x42c0
movl %ebp, %eax
addq $0x18, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
jmp 0x9b75
jmp 0x9b75
movq %rax, %r14
jmp 0x9b83
movq %rax, %r15
movq %r14, %rdi
callq 0x4370
movq %r15, %r14
movq %rbx, %rdi
callq 0x42c0
movq %r14, %rdi
callq 0x4510