X86 bmp fading with linear interpolation
Challenge: Thicken the top of the 24 Bpp.BMP image to white so that each pixel color is linearly interpolated between its original color and white based on its distance from the top edge. Distance pixels> = dist have not disappeared.
I created all the code and tried to guess something. It only seems to work for distances that have a power of 2, so 32,64,128, etc. I am using linear interpolation equality and I have no idea why it doesn't work for other numbers. Any ideas?:.)
Image example:
After fading away, I get ...
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 out:
#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;
}
source to share
Your formula seems to be correct: you have something like
color_new = (color-255)*dist_counter/dist + 255
although I would suggest a little simpler
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). The main mistake is that, since you are adding the current value, you may overflow the maximum value of 255. After the calculations, check if color > 255
and the clamp if there is.
In assembly (cannot be verified, so I hope I get it right at first):
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
.. repeats for your tricolor components.
A few additional notes that might be helpful:
-
My second line of calculation
color
is a constant for each line. So you only need to compute it in your y-loop and store it somewhere. -
You don't actually need to repeat the above code for each of the color per pixel components. Just repeat the x 3 * loop
width
:mov edi, width lea edi, [edi+2*edi]
-
Your starting position looks like not only a few lines, but a few pixels. This is partly due to
add eax,3
here:sub eax ,row_bytes add eax,3
but I'm not sure where the big mistake is coming from, because your addition calculation is correct. I couldn't verify your actual code, so I implemented it in bare C, and with that I get the following output to input
./a.out flower.bmp 400
:
source to share