// File: 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
movq %rsp, %rbp
subq $0x3f0, %rsp            # imm = 0x3F0
movq %rdi, -0x8(%rbp)
movl %esi, -0xc(%rbp)
movss %xmm0, -0x10(%rbp)
movq %rdx, -0x18(%rbp)
movq %rcx, -0x20(%rbp)
movl %r8d, -0x24(%rbp)
movq 0xb96(%rip), %rax       # 0x2050
movq %rax, -0x260(%rbp)
movq 0xb90(%rip), %rax       # 0x2058
movq %rax, -0x258(%rbp)
leaq -0x290(%rbp), %rdi
leaq 0xb83(%rip), %rsi       # 0x2060
movl $0x24, %edx
callq 0x1090
leaq -0x2d0(%rbp), %rdi
leaq 0xb9b(%rip), %rsi       # 0x2090
movl $0x40, %edx
callq 0x1090
leaq -0x3d0(%rbp), %rdi
leaq 0xbc3(%rip), %rsi       # 0x20d0
movl $0x100, %edx            # imm = 0x100
callq 0x1090
movq -0x8(%rbp), %rax
movl (%rax), %eax
movq -0x8(%rbp), %rcx
imull 0x4(%rcx), %eax
movl %eax, -0x30(%rbp)
movq -0x8(%rbp), %rax
movl (%rax), %ecx
movq -0x18(%rbp), %rax
movl %ecx, (%rax)
movq -0x8(%rbp), %rax
movl 0x4(%rax), %ecx
movq -0x18(%rbp), %rax
movl %ecx, 0x4(%rax)
movq -0x8(%rbp), %rax
movl (%rax), %ecx
movq -0x20(%rbp), %rax
movl %ecx, (%rax)
movq -0x8(%rbp), %rax
movl 0x4(%rax), %ecx
movq -0x20(%rbp), %rax
movl %ecx, 0x4(%rax)
movslq -0x30(%rbp), %rdi
callq 0x1130
movq %rax, %rcx
movq -0x20(%rbp), %rax
movq %rcx, 0x8(%rax)
movslq -0x30(%rbp), %rdi
callq 0x1130
movq %rax, %rcx
movq -0x18(%rbp), %rax
movq %rcx, 0x8(%rax)
movq -0x18(%rbp), %rax
cmpq $0x0, 0x8(%rax)
jne 0x15ac
movq 0x2a5a(%rip), %rdi      # 0x3ff0
leaq 0xc9b(%rip), %rsi       # 0x2238
callq 0x10b0
movl $0x1, %edi
callq 0x10f0
movq -0x20(%rbp), %rax
cmpq $0x0, 0x8(%rax)
jne 0x15d4
movq 0x2a32(%rip), %rdi      # 0x3ff0
leaq 0xc73(%rip), %rsi       # 0x2238
callq 0x10b0
movl $0x1, %edi
callq 0x10f0
movl $0x0, -0x28(%rbp)
cmpl $0x100, -0x28(%rbp)     # imm = 0x100
jge 0x1637
cvtsi2sdl -0x28(%rbp), %xmm0
movsd 0xa17(%rip), %xmm1      # 0x2008
divsd %xmm1, %xmm0
movss -0x10(%rbp), %xmm1
cvtss2sd %xmm1, %xmm2
movsd 0xa0a(%rip), %xmm1      # 0x2010
divsd %xmm2, %xmm1
callq 0x1190
movsd 0x9f1(%rip), %xmm1      # 0x2008
mulsd %xmm1, %xmm0
cvttsd2si %xmm0, %eax
movb %al, %cl
movslq -0x28(%rbp), %rax
movb %cl, -0x250(%rbp,%rax)
movl -0x28(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x28(%rbp)
jmp 0x15db
movq -0x20(%rbp), %rax
movq 0x8(%rax), %rax
movq %rax, -0x48(%rbp)
movq -0x18(%rbp), %rax
movq 0x8(%rax), %rax
movq %rax, -0x40(%rbp)
movq -0x8(%rbp), %rax
movq 0x8(%rax), %rax
movq %rax, -0x38(%rbp)
movl $0x0, -0x28(%rbp)
movl -0x28(%rbp), %eax
cmpl -0x30(%rbp), %eax
jge 0x1693
movq -0x38(%rbp), %rax
movslq -0x28(%rbp), %rcx
movzbl (%rax,%rcx), %eax
movb -0x250(%rbp,%rax), %dl
movq -0x48(%rbp), %rax
movslq -0x28(%rbp), %rcx
movb %dl, (%rax,%rcx)
movl -0x28(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x28(%rbp)
jmp 0x1662
movl $0x100, %eax            # imm = 0x100
cltd
idivl -0xc(%rbp)
movl %eax, -0x2c(%rbp)
movl $0x0, -0x28(%rbp)
cmpl $0x100, -0x28(%rbp)     # imm = 0x100
jge 0x16e6
movl -0x2c(%rbp), %eax
movl %eax, -0x3ec(%rbp)
movl -0x28(%rbp), %eax
cltd
idivl -0x2c(%rbp)
movl %eax, %ecx
movl -0x3ec(%rbp), %eax
imull %ecx, %eax
cltd
idivl -0x2c(%rbp)
movb %al, %cl
movslq -0x28(%rbp), %rax
movb %cl, -0x150(%rbp,%rax)
movl -0x28(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x28(%rbp)
jmp 0x16a6
movl $0x0, -0x28(%rbp)
movl -0x28(%rbp), %eax
cmpl -0x30(%rbp), %eax
jge 0x171e
movq -0x48(%rbp), %rax
movslq -0x28(%rbp), %rcx
movzbl (%rax,%rcx), %eax
movb -0x150(%rbp,%rax), %dl
movq -0x40(%rbp), %rax
movslq -0x28(%rbp), %rcx
movb %dl, (%rax,%rcx)
movl -0x28(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x28(%rbp)
jmp 0x16ed
movq -0x18(%rbp), %rax
movl 0x4(%rax), %eax
movl %eax, -0x3d4(%rbp)
movq -0x18(%rbp), %rax
movl (%rax), %eax
movl %eax, -0x3d8(%rbp)
movl $0x0, -0x3dc(%rbp)
movl -0x3dc(%rbp), %eax
cmpl -0x3d4(%rbp), %eax
jge 0x1928
movl $0x0, -0x3e0(%rbp)
movl -0x3e0(%rbp), %eax
cmpl -0x3d8(%rbp), %eax
jge 0x1912
movl -0x3e0(%rbp), %eax
cltd
idivl -0x24(%rbp)
movl %edx, -0x3e4(%rbp)
movl -0x3dc(%rbp), %eax
cltd
idivl -0x24(%rbp)
movl %edx, -0x3e8(%rbp)
cmpl $0x3, -0x24(%rbp)
jne 0x1802
movq -0x40(%rbp), %rax
movl -0x3dc(%rbp), %ecx
imull -0x3d8(%rbp), %ecx
addl -0x3e0(%rbp), %ecx
movslq %ecx, %rcx
movzbl (%rax,%rcx), %edx
movslq -0x3e4(%rbp), %rcx
leaq -0x290(%rbp), %rax
imulq $0xc, %rcx, %rcx
addq %rcx, %rax
movslq -0x3e8(%rbp), %rcx
movl (%rax,%rcx,4), %esi
xorl %eax, %eax
movl $0xff, %ecx
cmpl %esi, %edx
cmovgl %ecx, %eax
movb %al, %dl
movq -0x40(%rbp), %rax
movl -0x3dc(%rbp), %ecx
imull -0x3d8(%rbp), %ecx
addl -0x3e0(%rbp), %ecx
movslq %ecx, %rcx
movb %dl, (%rax,%rcx)
jmp 0x18fc
cmpl $0x4, -0x24(%rbp)
jne 0x1875
movq -0x40(%rbp), %rax
movl -0x3dc(%rbp), %ecx
imull -0x3d8(%rbp), %ecx
addl -0x3e0(%rbp), %ecx
movslq %ecx, %rcx
movzbl (%rax,%rcx), %edx
movslq -0x3e4(%rbp), %rcx
leaq -0x2d0(%rbp), %rax
shlq $0x4, %rcx
addq %rcx, %rax
movslq -0x3e8(%rbp), %rcx
movl (%rax,%rcx,4), %esi
xorl %eax, %eax
movl $0xff, %ecx
cmpl %esi, %edx
cmovgl %ecx, %eax
movb %al, %dl
movq -0x40(%rbp), %rax
movl -0x3dc(%rbp), %ecx
imull -0x3d8(%rbp), %ecx
addl -0x3e0(%rbp), %ecx
movslq %ecx, %rcx
movb %dl, (%rax,%rcx)
jmp 0x18fa
cmpl $0x8, -0x24(%rbp)
jne 0x18e5
movq -0x40(%rbp), %rax
movl -0x3dc(%rbp), %ecx
imull -0x3d8(%rbp), %ecx
addl -0x3e0(%rbp), %ecx
movslq %ecx, %rcx
movzbl (%rax,%rcx), %edx
movslq -0x3e4(%rbp), %rcx
leaq -0x3d0(%rbp), %rax
shlq $0x5, %rcx
addq %rcx, %rax
movslq -0x3e8(%rbp), %rcx
movl (%rax,%rcx,4), %esi
xorl %eax, %eax
movl $0xff, %ecx
cmpl %esi, %edx
cmovgl %ecx, %eax
movb %al, %dl
movq -0x40(%rbp), %rax
movl -0x3dc(%rbp), %ecx
imull -0x3d8(%rbp), %ecx
addl -0x3e0(%rbp), %ecx
movslq %ecx, %rcx
movb %dl, (%rax,%rcx)
jmp 0x18f8
movq 0x2704(%rip), %rdi      # 0x3ff0
leaq 0x744(%rip), %rsi       # 0x2037
callq 0x10b0
jmp 0x18fa
jmp 0x18fc
jmp 0x18fe
movl -0x3e0(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x3e0(%rbp)
jmp 0x175d
jmp 0x1914
movl -0x3dc(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x3dc(%rbp)
jmp 0x1741
addq $0x3f0, %rsp            # imm = 0x3F0
popq %rbp
retq
nopw %cs:(%rax,%rax)