// File: /workspace/github_cpp_makefile_star2_O3/brianwu02[P]ImageProcessing/ordered_dither.cpp
// Function: ordered_dither(imageS*, int, float, imageS*, imageS*, int)
void ordered_dither(imageP I1, int n, float gamma, imageP I2, imageP tmp_img, int m) {
    
    int i, scale, total;
    uchar *in, *out, *tmp_out, lut[256];
    uchar gamma_corrected_lut[256];

    // initialize the dither matrices. I wish there were a cleaner way :/

    int dither2[2][2] = {
        {0, 2,},
        {3, 1}
    };

    int dither3[3][3] = {
        {6, 8, 4,},
        {1, 0, 3,},
        {5, 2, 7}
    };

    int dither4[4][4] = {
        {0,  8,  2, 10,}, 
        {12,  4, 14,  6,},
        {3, 11,  1,  9,},
        {15,  7, 13,  5}
    };
    
    int dither8[8][8] = {
        {0, 48, 12, 60, 3, 51, 15, 63,},
        {32, 16, 44, 28, 35, 19, 47, 31,},
        {8, 56, 4, 52, 11, 59, 7, 55,},
        {40, 24, 36, 20, 43, 27, 39, 23,},
        {2, 50, 14, 62, 1, 49, 13, 61,},
        {34, 18, 46, 30, 33, 17, 45, 29,},
        {10, 58, 6, 54, 9, 57, 5, 53,},
        {42, 26, 38, 22, 41, 25, 37, 21}
    };


    // total num of pixels = length * width
    total = I1->width * I1->height;

    // initialize I2 dimensions & buffers
    I2->width  = I1->width;
    I2->height = I1->height;

    tmp_img->width = I1->width;
    tmp_img->height = I1->height;

    tmp_img->image = (uchar *) malloc(total);
    I2->image  = (uchar *) malloc(total);
    
    if (I2->image == NULL) {
        cerr << "Insufficient memory\n";
        exit(1);
    }
    if (tmp_img->image == NULL) {
        cerr << "Insufficient memory\n";
        exit(1);
    }

    for (i=0; i<256; i++) {
    gamma_corrected_lut[i] = (int) (pow((double) i / 255.0 , (1.0 / gamma)) * 255.0);
    }
    tmp_out = tmp_img->image; // tmp output image buffer after gamma correction
    out = I2->image;    // output image buffer
    in  = I1->image;    // input  image buffer
    
    // this should produce gamma corrected image from look up table.
    // and store the image in tmp_out;
    for (i=0; i<total; i++) tmp_out[i] = gamma_corrected_lut[ in[i] ];

    scale = 256 / n;
    //cout << scale << endl;
    // init lookup table for quantization
    for (i=0; i < 256; i++) {
        //lut[i] = scale * (int) (i/scale);
        lut[i] = (int) (scale * (i/scale)) / scale;
    }
    // visit all input pixels and apply quantization to n levels via lookup table
    for (i=0; i<total; i++) {
        out[i] = lut[tmp_out[i]];
    }

    int h = I2->height;
    int w = I2->width;
    for (int y = 0; y < h; y++) {
        for (int x = 0; x < w; x++ ) {
            int i = x % m;
            int j = y % m;
            
            if (m == 3) {
                out[y*w+x] = ((out[y*w+x] > dither3[i][j]) ? 255 : 0);
            } else if (m == 4) {
                out[y*w+x] = ((out[y*w+x] > dither4[i][j]) ? 255 : 0);
            } else if (m == 8) { 
                out[y*w+x] = ((out[y*w+x] > dither8[i][j]) ? 255 : 0);
            } else {
                // should not come here anyway.
                cerr << "something went wrong\n";
            }
        }
    }
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x218, %rsp            # imm = 0x218
movl %r8d, %ebx
movq %rcx, %r12
movq %rdx, %r14
movss %xmm0, (%rsp)
movl %esi, %r15d
movl (%rdi), %eax
movq %rdi, 0x8(%rsp)
movl 0x4(%rdi), %ebp
movl %eax, (%rdx)
movl %ebp, 0x4(%rdx)
movl %eax, (%rcx)
movl %ebp, 0x4(%rcx)
imull %eax, %ebp
movslq %ebp, %r13
movq %r13, %rdi
callq 0x2150
movq %rax, 0x8(%r12)
movq %r13, %rdi
callq 0x2150
movq %rax, 0x8(%r14)
testq %rax, %rax
je 0x263f
cmpq $0x0, 0x8(%r12)
je 0x263f
movl %r15d, %r13d
xorps %xmm0, %xmm0
cvtss2sd (%rsp), %xmm0
movsd 0xb56(%rip), %xmm1      # 0x3008
divsd %xmm0, %xmm1
movsd %xmm1, (%rsp)
xorl %r15d, %r15d
xorps %xmm0, %xmm0
cvtsi2sd %r15d, %xmm0
movsd 0xb42(%rip), %xmm1      # 0x3010
divsd %xmm1, %xmm0
movsd (%rsp), %xmm1
callq 0x21b0
mulsd 0xb2c(%rip), %xmm0      # 0x3010
cvttsd2si %xmm0, %eax
movb %al, 0x10(%rsp,%r15)
incq %r15
cmpq $0x100, %r15            # imm = 0x100
jne 0x24be
movq 0x8(%r12), %rdi
movq 0x8(%r14), %r15
movl %ebp, %r8d
testl %ebp, %ebp
jle 0x2527
movq 0x8(%rsp), %rax
movq 0x8(%rax), %rax
xorl %ecx, %ecx
movzbl (%rax,%rcx), %edx
movb 0x10(%rsp,%rdx), %dl
movb %dl, (%rdi,%rcx)
incq %rcx
cmpq %rcx, %r8
jne 0x2514
xorl %ecx, %ecx
movl $0x100, %eax            # imm = 0x100
xorl %edx, %edx
idivl %r13d
movl %eax, %esi
movl %ecx, %eax
cltd
idivl %esi
movb %al, 0x110(%rsp,%rcx)
incq %rcx
cmpq $0x100, %rcx            # imm = 0x100
jne 0x2535
testl %ebp, %ebp
jle 0x256a
xorl %eax, %eax
movzbl (%rdi,%rax), %ecx
movb 0x110(%rsp,%rcx), %cl
movb %cl, (%r15,%rax)
incq %rax
cmpq %rax, %r8
jne 0x2553
movslq 0x4(%r14), %rax
movq %rax, (%rsp)
testq %rax, %rax
jle 0x262d
movl (%r14), %r12d
xorl %r14d, %r14d
testl %r12d, %r12d
jle 0x261d
movl %r14d, %eax
cltd
idivl %ebx
movl %edx, %r13d
xorl %ebp, %ebp
movl %ebp, %eax
cltd
idivl %ebx
cmpl $0x8, %ebx
je 0x25d5
cmpl $0x4, %ebx
je 0x25c1
cmpl $0x3, %ebx
jne 0x25f9
movzbl (%r15,%rbp), %eax
movl %edx, %ecx
leaq (%rcx,%rcx,2), %rcx
leaq 0xa95(%rip), %rdx       # 0x3050
leaq (%rdx,%rcx,4), %rcx
jmp 0x25ea
movzbl (%r15,%rbp), %eax
movl %edx, %ecx
shlq $0x4, %rcx
leaq 0xaad(%rip), %rdx       # 0x3080
jmp 0x25e7
movzbl (%r15,%rbp), %eax
movl %edx, %ecx
shlq $0x5, %rcx
leaq 0xad9(%rip), %rdx       # 0x30c0
addq %rdx, %rcx
cmpl %eax, (%rcx,%r13,4)
setl %al
negb %al
movb %al, (%r15,%rbp)
jmp 0x2611
movl $0x15, %edx
movq 0x29eb(%rip), %rdi      # 0x4ff0
leaq 0xa2b(%rip), %rsi       # 0x3037
callq 0x20f0
incq %rbp
cmpq %rbp, %r12
jne 0x2595
incq %r14
addq %r12, %r15
cmpq (%rsp), %r14
jne 0x2581
addq $0x218, %rsp            # imm = 0x218
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq 0x29aa(%rip), %rdi      # 0x4ff0
leaq 0xbd8(%rip), %rsi       # 0x3225
callq 0x20d0
movl $0x1, %edi
callq 0x2110