// File: /workspace/github_cpp_makefile_star2_O2/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, %r15
movq %rdx, %rbp
movss %xmm0, (%rsp)
movl %esi, 0x8(%rsp)
movq %rdi, %r12
movl (%rdi), %eax
movl 0x4(%rdi), %ecx
movl %eax, (%rdx)
movl %ecx, 0x4(%rdx)
movl %eax, (%r15)
movl %ecx, 0x4(%r15)
movl %ecx, %r14d
imull %eax, %r14d
movslq %r14d, %r13
movq %r13, %rdi
callq 0x1100
movq %rax, 0x8(%r15)
movq %r13, %rdi
callq 0x1100
movq %rax, 0x8(%rbp)
testq %rax, %rax
je 0x15e5
cmpq $0x0, 0x8(%r15)
je 0x15e5
cvtss2sd (%rsp), %xmm0
movsd 0xbbd(%rip), %xmm1      # 0x2008
divsd %xmm0, %xmm1
movsd %xmm1, (%rsp)
xorl %r13d, %r13d
cmpq $0x100, %r13            # imm = 0x100
je 0x1491
cvtsi2sd %r13d, %xmm0
movsd 0xba3(%rip), %xmm1      # 0x2010
divsd %xmm1, %xmm0
movsd (%rsp), %xmm1
callq 0x1160
mulsd 0xb8d(%rip), %xmm0      # 0x2010
cvttsd2si %xmm0, %eax
movb %al, 0x10(%rsp,%r13)
incq %r13
jmp 0x1457
movq 0x8(%r15), %rdi
movq %rbp, %r8
movq 0x8(%rbp), %rbp
movq 0x8(%r12), %rax
xorl %ecx, %ecx
testl %r14d, %r14d
cmovlel %ecx, %r14d
cmpq %rcx, %r14
je 0x14bf
movzbl (%rax,%rcx), %edx
movb 0x10(%rsp,%rdx), %dl
movb %dl, (%rdi,%rcx)
incq %rcx
jmp 0x14aa
xorl %ecx, %ecx
movl $0x100, %eax            # imm = 0x100
xorl %edx, %edx
idivl 0x8(%rsp)
movl %eax, %esi
cmpq $0x100, %rcx            # imm = 0x100
je 0x14e8
movl %ecx, %eax
cltd
idivl %esi
movb %al, 0x110(%rsp,%rcx)
incq %rcx
jmp 0x14ce
xorl %eax, %eax
cmpq %rax, %r14
je 0x1503
movzbl (%rdi,%rax), %ecx
movb 0x110(%rsp,%rcx), %cl
movb %cl, (%rbp,%rax)
incq %rax
jmp 0x14ea
movl 0x4(%r8), %eax
movslq (%r8), %rcx
xorl %r14d, %r14d
testl %ecx, %ecx
movl $0x0, %r15d
movq %rcx, 0x8(%rsp)
cmovgl %ecx, %r15d
testl %eax, %eax
cmovlel %r14d, %eax
movq %rax, (%rsp)
leaq 0xb08(%rip), %r12       # 0x2037
cmpq (%rsp), %r14
je 0x15d3
xorl %r13d, %r13d
cmpq %r13, %r15
je 0x15c6
movl %r13d, %eax
cltd
idivl %ebx
movl %edx, %ecx
movl %r14d, %eax
cltd
idivl %ebx
movl %ecx, %ecx
movl %edx, %eax
cmpl $0x8, %ebx
je 0x158c
cmpl $0x4, %ebx
je 0x1579
cmpl $0x3, %ebx
jne 0x15af
movzbl (%rbp,%r13), %edx
imulq $0xc, %rcx, %rcx
leaq 0xad9(%rip), %rsi       # 0x2050
jmp 0x159d
movzbl (%rbp,%r13), %edx
shlq $0x4, %rcx
leaq 0xaf6(%rip), %rsi       # 0x2080
jmp 0x159d
movzbl (%rbp,%r13), %edx
shlq $0x5, %rcx
leaq 0xb23(%rip), %rsi       # 0x20c0
addq %rsi, %rcx
cmpl %edx, (%rcx,%rax,4)
setl %al
negb %al
movb %al, (%rbp,%r13)
jmp 0x15be
movq 0x2a3a(%rip), %rdi      # 0x3ff0
movq %r12, %rsi
callq 0x1090
incq %r13
jmp 0x153c
incq %r14
addq 0x8(%rsp), %rbp
jmp 0x152f
addq $0x218, %rsp            # imm = 0x218
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq 0x2a04(%rip), %rdi      # 0x3ff0
leaq 0xc32(%rip), %rsi       # 0x2225
callq 0x1090
pushq $0x1
popq %rdi
callq 0x10c0