// File: misohena[P]drawstars/./image_jpeg.h
// Function: bool writeJpegFile<Image<ColorRGB<double>>>(char const*, Image<ColorRGB<double>> const&, int)
bool writeJpegFile(const char *filename, const ImageT &image, int quality)
{
    File file(filename, "wb");
    if(!file){
        return false;
    }

    ///@todo handle errors

    jpeg_compress_struct cinfo;
    jpeg_error_mgr jerr;

    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_compress(&cinfo);

    jpeg_stdio_dest(&cinfo, file);

    cinfo.image_width = image.width();
    cinfo.image_height = image.height();
    cinfo.input_components = 3; // number of components per pixel.
    cinfo.in_color_space = JCS_RGB;
    jpeg_set_defaults(&cinfo);
    jpeg_set_quality(&cinfo, quality, TRUE);


    jpeg_start_compress(&cinfo, TRUE);

    std::unique_ptr<unsigned char[]> lineBytes(new unsigned char[image.width() * 3]);

    while(cinfo.next_scanline < cinfo.image_height){
        // copy scanline bytes
        unsigned char *dst = lineBytes.get();
        const typename ImageT::PixelType *src = image.at(0, cinfo.next_scanline);
        for(unsigned int x = 0; x < image.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)));
        }

        // write
        JSAMPROW row_pointer[1] = {lineBytes.get()};
        jpeg_write_scanlines(&cinfo, row_pointer, 1);
    }

    jpeg_finish_compress(&cinfo);

    jpeg_destroy_compress(&cinfo);

    return true;
}

// Assembly:
subq $0x3b8, %rsp            # imm = 0x3B8
movq %rdi, 0x3a8(%rsp)
movq %rsi, 0x3a0(%rsp)
movl %edx, 0x39c(%rsp)
movq 0x3a8(%rsp), %rsi
leaq 0x109e1(%rip), %rdx     # 0x1e4ae
leaq 0x390(%rsp), %rdi
movq %rdi, 0x58(%rsp)
callq 0x11bb0
movq 0x58(%rsp), %rdi
callq 0x11bf0
movq %rax, 0x60(%rsp)
jmp 0xdaf0
movq 0x60(%rsp), %rax
cmpq $0x0, %rax
jne 0xdb2c
movb $0x0, 0x3b7(%rsp)
movl $0x1, 0x380(%rsp)
jmp 0xde58
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x388(%rsp)
movl %eax, 0x384(%rsp)
jmp 0xde76
leaq 0x90(%rsp), %rdi
callq 0x4070
movq %rax, 0x50(%rsp)
jmp 0xdb40
movq 0x50(%rsp), %rax
movq %rax, 0x138(%rsp)
leaq 0x138(%rsp), %rdi
movl $0x50, %esi
movl $0x248, %edx            # imm = 0x248
callq 0x4060
jmp 0xdb66
leaq 0x390(%rsp), %rdi
callq 0x11bf0
movq %rax, 0x48(%rsp)
jmp 0xdb7a
movq 0x48(%rsp), %rsi
leaq 0x138(%rsp), %rdi
callq 0x45c0
jmp 0xdb8e
movq 0x3a0(%rsp), %rdi
callq 0x11c00
movl %eax, 0x44(%rsp)
jmp 0xdba1
movl 0x44(%rsp), %eax
movl %eax, 0x168(%rsp)
movq 0x3a0(%rsp), %rdi
callq 0x11c10
movl %eax, 0x40(%rsp)
jmp 0xdbbf
movl 0x40(%rsp), %eax
movl %eax, 0x16c(%rsp)
movl $0x3, 0x170(%rsp)
movl $0x2, 0x174(%rsp)
leaq 0x138(%rsp), %rdi
callq 0x4520
jmp 0xdbef
movl 0x39c(%rsp), %esi
leaq 0x138(%rsp), %rdi
movl $0x1, %edx
callq 0x40e0
jmp 0xdc0a
leaq 0x138(%rsp), %rdi
movl $0x1, %esi
callq 0x4530
jmp 0xdc1e
movq 0x3a0(%rsp), %rdi
callq 0x11c00
movl %eax, 0x3c(%rsp)
jmp 0xdc31
movl 0x3c(%rsp), %ecx
movl %ecx, %eax
leal (%rax,%rax,2), %eax
movl %eax, %edi
callq 0x4030
movq %rax, 0x30(%rsp)
jmp 0xdc48
movq 0x30(%rsp), %rsi
leaq 0x88(%rsp), %rdi
callq 0x11c20
movl 0x28c(%rsp), %eax
cmpl 0x16c(%rsp), %eax
jae 0xde1a
leaq 0x88(%rsp), %rdi
callq 0x11c50
movq %rax, 0x80(%rsp)
movq 0x3a0(%rsp), %rdi
movl 0x28c(%rsp), %edx
xorl %esi, %esi
callq 0x11c80
movq %rax, 0x28(%rsp)
jmp 0xdca0
movq 0x28(%rsp), %rax
movq %rax, 0x78(%rsp)
movl $0x0, 0x74(%rsp)
movl 0x74(%rsp), %eax
movl %eax, 0x20(%rsp)
movq 0x3a0(%rsp), %rdi
callq 0x11c00
movl %eax, 0x24(%rsp)
jmp 0xdccd
movl 0x20(%rsp), %eax
movl 0x24(%rsp), %ecx
cmpl %ecx, %eax
jae 0xddea
movq 0x78(%rsp), %rax
movsd (%rax), %xmm0
movsd 0x10372(%rip), %xmm2    # 0x1e060
mulsd %xmm2, %xmm0
xorps %xmm1, %xmm1
callq 0xe9a0
movsd %xmm0, 0x18(%rsp)
jmp 0xdd02
movsd 0x18(%rsp), %xmm0
cvttsd2si %xmm0, %eax
movb %al, %cl
movq 0x80(%rsp), %rax
movb %cl, (%rax)
movq 0x78(%rsp), %rax
movsd 0x8(%rax), %xmm0
movsd 0x10336(%rip), %xmm2    # 0x1e060
mulsd %xmm2, %xmm0
xorps %xmm1, %xmm1
callq 0xe9a0
movsd %xmm0, 0x10(%rsp)
jmp 0xdd3e
movsd 0x10(%rsp), %xmm0
cvttsd2si %xmm0, %eax
movb %al, %cl
movq 0x80(%rsp), %rax
movb %cl, 0x1(%rax)
movq 0x78(%rsp), %rax
movsd 0x10(%rax), %xmm0
movsd 0x102f9(%rip), %xmm2    # 0x1e060
mulsd %xmm2, %xmm0
xorps %xmm1, %xmm1
callq 0xe9a0
movsd %xmm0, 0x8(%rsp)
jmp 0xdd7b
movsd 0x8(%rsp), %xmm0
cvttsd2si %xmm0, %eax
movb %al, %cl
movq 0x80(%rsp), %rax
movb %cl, 0x2(%rax)
movl 0x74(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x74(%rsp)
movq 0x78(%rsp), %rax
addq $0x18, %rax
movq %rax, 0x78(%rsp)
movq 0x80(%rsp), %rax
addq $0x3, %rax
movq %rax, 0x80(%rsp)
jmp 0xdcb2
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x388(%rsp)
movl %eax, 0x384(%rsp)
leaq 0x88(%rsp), %rdi
callq 0x11cc0
jmp 0xde76
leaq 0x88(%rsp), %rdi
callq 0x11c50
movq %rax, 0x68(%rsp)
leaq 0x138(%rsp), %rdi
leaq 0x68(%rsp), %rsi
movl $0x1, %edx
callq 0x4410
jmp 0xde15
jmp 0xdc5a
leaq 0x138(%rsp), %rdi
callq 0x43d0
jmp 0xde29
leaq 0x138(%rsp), %rdi
callq 0x4470
jmp 0xde38
movb $0x1, 0x3b7(%rsp)
movl $0x1, 0x380(%rsp)
leaq 0x88(%rsp), %rdi
callq 0x11cc0
leaq 0x390(%rsp), %rdi
callq 0x11d20
movb 0x3b7(%rsp), %al
andb $0x1, %al
addq $0x3b8, %rsp            # imm = 0x3B8
retq
leaq 0x390(%rsp), %rdi
callq 0x11d20
movq 0x388(%rsp), %rdi
callq 0x45f0