Search code examples
x86interpolationintelbmpfading

x86 assembly fading bmp with linear interpolation


The task is : Fade the top of a 24 bpp .BMP image to white so that every pixel's color is linearly interpolated between its original color and white based on its distance form the top edge. Pixels of distances >= dist are not faded.

I created all code and tried to fade something . It seems that it works only for distances which are power of 2 so 32,64,128 etc... I am using linear interpolation equaction and i dont have any idea why it doesnt work for other numbers. Any ideas ? :).

Sample image:

before

After fading I am getting... after

global fadetop

; parm.
%define     img         [ebp+8]
%define     width       [ebp+12]
%define     height      [ebp+16]
%define     dist        [ebp+20]
; local
%define     row_bytes      [ebp-4]
%define     dist_counter   [ebp-8]

fadetop:
; create stack frame
push    ebp
mov     ebp, esp
sub     esp, 8

; push register on stack
push    ebx
push    esi
push    edi

; calculate size of row 
mov     edx, width              
lea     edx, [edx+edx*2]        
add     edx, 3                  
and     edx, 0fffffffch         
mov     row_bytes, edx          

; address of datas -calculations to write pixels from top left corner
mov eax,height
mul edx
sub eax ,row_bytes
add eax,3
mov     esi, img
add esi,eax

;line couter 

xor edx, edx
mov dist_counter , edx
line:    
; pixel counter in line 
mov     edi, width

; index rexister 
xor     ebx, ebx

; INTERPOlATION STARTS NOW !!!


convert:                                  
movzx   eax, byte [esi+ebx+0]   ; take color 1
sub     eax, 255        ; sub white value
mov     ecx, dist_counter   
imul    ecx         ;color * actual position  
cdq
mov     ecx, dist       ; devide by full fade operation distance 
idiv    ecx
add     eax, 255        ; add 255 
mov     [esi+ebx+0], al     ;put in this place  

movzx   eax,byte [esi+ebx+1]    ; take color 2
sub     eax, 255
mov     ecx , dist_counter
mul     ecx
cdq
mov     ecx, dist
div     ecx
add     eax, 255
mov     [esi+ebx+1], al

movzx   eax,byte [esi+ebx+2]    ; take color 3
sub     eax, 255
mov     ecx , dist_counter
mul     ecx
cdq
mov     ecx, dist
div     ecx
add     eax, 255
mov     [esi+ebx+2], al

;________________________________________________________________
                    ;if still in one line 
    add     ebx, 3
    dec     edi
    jnz     convert

                        ; if next line 
sub     esi, row_bytes
mov     edx,dist_counter
inc     edx
mov     dist_counter,edx
mov     eax, dist
cmp     eax, edx      
jnz     line


    ; pop registers
    pop     edi
    pop     esi
    pop     ebx

    ; return trace         
    mov     esp, ebp
    pop     ebp
    ret

code in C for those who would like to run it and try fading :

#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

void fadetop(void* img, int width, int height ,int dist );

int main (int argc, char** argv) {

    char* buff;

    if (argc != 3 ) {
        printf("Use file: %s [file]\n",argv[0]);
        return 1;

    }

    struct stat st;
    stat(argv[1], &st);

    buff = (char *) malloc(st.st_size);
    if (buff == NULL) {
        printf("Memory error!!\n");

        return 1;
    }

    int fd = open(argv[1], O_RDONLY, 0);
    if (fd == -1) {
        printf("File access error\n");
        free(buff);

        return 1;
    }

    int size = read(fd, buff, st.st_size);
    uint32_t offset    = *(uint32_t *) (buff + 0x0a);
    uint32_t width    = *(uint32_t *) (buff + 0x12);
    uint32_t height    = *(uint32_t *) (buff + 0x16);
    uint16_t bpp    = *(uint16_t *) (buff + 0x1c);

    if (bpp == 24) {
        int fd_out;
        printf("worked dist: %d , heigh: %d\n", atoi(argv[2]), height);
        fadetop(buff + offset, width , height , atoi(argv[2]) );
        fd_out = creat("fade.bmp", 0644);
        write(fd_out, buff, size);
        close(fd_out);

        printf("Image faded.\n");
    }
    else {
        printf("Invalid BMP\n");
    }

    close(fd);
    free(buff);

    return 0;
}

Solution

  • Your formula seems correct: you have something like

     color_new = (color-255)*dist_counter/dist + 255
    

    although I would suggest the somewhat easier

    color *= dist_counter/dist             ; scale original color over current dist
    color += 256*(dist-dist_counter)/dist  ; add white, scaled over current dist
    

    (which at least visually gives the same result). Your main error is that since you are adding to the current value, you may be overflowing the maximum value of 255. After the calculations, test if color > 255 and clamp if it is.

    In assembly (unable to test so I hope I get it right first time):

        movzx   eax, byte [esi+ebx+0]   ; take color 1
        mov     ecx, dist_counter   
        imul    ecx         ;color * current position
        cdq
        mov     ecx, dist       ; devide by full fade operation distance 
        idiv    ecx
    
        mov     ebx, eax        ; save result so far
    
        mov     eax, dist       ; calculate 2nd half
        mov     ecx, eax
        sub     eax, dist_counter
        shl     eax, 8
        idiv    ecx
    
        add     eax, ebx
        test    ah, ah
        jz      skip_clamp
        mov     al, 255
    skip_clamp:
        mov     [esi+ebx+0], al     ;put in this place
    

    .. repeated for your 3 color components.

    A few additional notes which may be helpful:

    1. My second line of calculating the color is a constant for each line. So you only have to calculate it in your y loop and save it somewhere.

    2. You don't actually have to repeat the above code for each of the color components per pixel. Just repeat the x loop 3*width:

      mov     edi, width
      lea     edi, [edi+2*edi]
      
    3. Your starting position seems off, not only by a few lines but by a few pixels as well. This is partly because of the add eax,3 here:

      sub eax ,row_bytes
      add eax,3
      

      but I'm not sure where the larger error comes from, because your padding calculation is correct. I could not test your actual code so I implemented it in bare C, and with that I get the following output for the input ./a.out flower.bmp 400:

    partially faded flower