I'm working on a project that reads the contents of a BMP file and performs a smooth digital filter on the image. My code below almost does the job. I just don't know where I should store the processed pixels and how I can write the processed image to a file. My code below is commented out so Any help would be highly appreciated.
.586
.model flat, stdcall
option casemap :none
include ..\masm32\include\windows.inc
include ..\masm32\include\user32.inc
include ..\masm32\include\kernel32.inc
include ..\masm32\macros\macros.asm
include ..\masm32\include\masm32.inc
includelib ..\masm32\lib\user32.lib
includelib ..\masm32\lib\kernel32.lib
includelib ..\masm32\lib\masm32.lib
.data
FileName db "bitmap2.bmp", 0
filename db "bitmap_fil.bmp",0
errMsg BYTE "Cannot create file",0dh,0ah,0
hFile HANDLE ?
hwFile HANDLE ?
hMemory HANDLE ? ;incoming data
pMemory DWORD ?
hMemory_o HANDLE ? ;outgoing data
pMemory_o DWORD ?
ReadSize DWORD ?
bytesWritten DWORD ?
firstLine DWORD ?
FileSize DWORD ?
BDoff DWORD ?
BHSize DWORD ?
szTemp byte 16 dup (0) ;buffer for messages
szPrime byte "%08i", 0 ;message format string
szPrimeH byte "%08lx",0 ;message hexa format string
signature DD 0
MEMORYSIZE equ 65535 ;This is how much memory allocated
; to store the file.
im_offset dd ?
im_width dd ?
im_height dd ?
bits_pix dd ?
.code
;................................
show MACRO caption, value
print SADD(caption)
mov eax, value
invoke wsprintf, offset szTemp, offset szPrime, eax ;converts eax into string
print offset szTemp ;print string
print SADD(13,10)
ENDM
;..................................
start:
invoke CreateFile, addr FileName,GENERIC_READ,FILE_SHARE_READ,NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL
mov hFile, eax
;Allocate and lock the memory for incoming file.
invoke GlobalAlloc, GMEM_MOVEABLE or GMEM_ZEROINIT, MEMORYSIZE
mov hMemory, eax
invoke GlobalLock, hMemory
mov pMemory, eax
;Allocate and lock the memory for outgoing file.
invoke GlobalAlloc, GMEM_MOVEABLE or GMEM_ZEROINIT, MEMORYSIZE
mov hMemory_o, eax
invoke GlobalLock, hMemory_o
mov pMemory_o, eax
;Read file and save image parameters
invoke ReadFile, hFile, pMemory, MEMORYSIZE-1, addr ReadSize, NULL
mov esi, pMemory
add esi, 02 ;get filesize
mov edi, [esi]
mov FileSize,edi
invoke wsprintf, offset szTemp, offset szPrime, edi
print offset szTemp
print SADD(10,13)
add esi, 8 ; jump 8 bytes to get image offset
mov edi, [esi] ; get image offset
mov im_offset,edi
invoke wsprintf, offset szTemp, offset szPrimeH, edi
print offset szTemp
print SADD(10,13)
add esi,8 ;jump 8 bytes to get image width
mov edi, [esi] ; get image width
mov im_width, edi
invoke wsprintf, offset szTemp, offset szPrime, edi
print offset szTemp
print SADD(10,13)
add esi,4 ;jump 4 bytes to get image height
mov edi, [esi] ; get image height
mov im_height, edi
invoke wsprintf, offset szTemp, offset szPrime, edi
print offset szTemp
print SADD(10,13)
add esi,4 ;jump 4 bytes to get color plane
mov ebx, [esi]
shr ebx,16 ; get color plane and bit-pix
mov bits_pix,ebx ;
print SADD("bit-per-pix ")
invoke wsprintf, offset szTemp, offset szPrime, ebx
print offset szTemp
print SADD(10,13)
mov ebp, pMemory ; get ready to start processing the image
add ebp, im_offset ; esi now points to the first pix
;filtering process
;leave first row and first column and last row and last column untouched.
mov esi,1 ; esi is the row counter
mov edi,1 ; edi is the column counter
proc_pix:
;show "current column is: ",edi
;show "current row is: ",esi
xor ebx,ebx ; ebx = 0 ebx will accumulate intermediate values for averaging
mov eax, im_width ; eax is the pointer to the pixel
mul esi
add eax,edi
add ebx, [ebp+eax] ;get the pixel(I,J)
mov eax, im_width
add esi,1 ;next row (I+1)
mul esi
add eax,edi
add ebx, [ebp+eax] ;get the pixel(I+1,J)
mov eax, im_width
sub esi,2 ; prev row (I-1)
mul esi
add eax,edi
add ebx, [ebp+eax] ;get the pixel(I-1,J)
add esi,1 ;back to the current row
add edi,1 ;get next column (J+1)
mov eax, im_width
mul esi
add eax,edi
add ebx, [ebp+eax] ;get the pixel(I,J+1)
sub edi,2 ; get prev column (J-1)
mov eax, im_width
mul esi
add eax,edi
add ebx, [ebp+eax] ;get the pixel(I,J-1)
add edi,1 ;back current column
xor edx, edx ;clear upper part dividend
mov eax, ebx ; move data to eax to divide
mov ecx, 5
div ecx ;do the average (div 5)>>> result in eax
; where should I store the processed pixels?
inc edi ; do the next column
cmp edi, im_width
jl proc_pix
inc esi ; do the next row
mov edi,1 ; skip the first column
cmp esi, im_height
jl proc_pix
;....................................................................................
new_file:
invoke CreateFile,ADDR filename, GENERIC_WRITE, NULL, NULL,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0
mov hwFile,eax ; save file handle
; handling of error if invalid file handle
cmp eax,INVALID_HANDLE_VALUE
jne writef
invoke StdOut, addr errMsg ; Display error message
jmp QuitNow
writef: invoke WriteFile, hwFile, pMemory_o, FileSize, ADDR bytesWritten, 0
QuitNow:
invoke GlobalUnlock, pMemory
invoke GlobalUnlock, pMemory_o
invoke GlobalFree, hMemory
invoke CloseHandle, hFile
invoke CloseHandle, hwFile
invoke ExitProcess, NULL
end start
;finish
You already have a buffer for the output; you just need to figure out the offset into that buffer to store it, it seems. If the pixel you filtered is (I, J), which you read from pMemory + im_offset + J * im_width + I, then you want to write it out to pMemory_o + im_offset + J * im_width + I. (As an aside, your code seems to assume 8 bits per pixel; you may want to check that explicitly and quit with an error if it isn't true to avoid unexpected behavior on non-8bpp image files.)
To write the destination pixel correctly, you can do the same row/column multiplication as for reading to get an offset in eax, add im_offset, and add it to pMemory_o (those last two instead of using ebp as in the read case).
As well as writing the pixels to the new image, though, you want to copy the header from the source file (so that viewers etc. see it as a bitmap); do a memcpy (or appropriate rep movsb) from pMemory to pMemory_o, length im_offset, to do that. You also need to copy the first row and column (and, I would expect, the last row and column too, which would have the same problem as the first: you can't get surrounding pixels for some sides) to it. A "dumb" but effective method would just be to copy the entire contents of the old image to the new, header, pixels, and all, and then just change the interior pixels with your filter.
Your code to create and write the output buffer seems correct; presumably it now writes out a zero/random-filled file of the right length.
As an aside, once you get it correct, you can save a lot of work (presumably efficiency is one of the reasons to write in assembly in the first place?) using addition and subtraction instead of multiplying so much: adding im_width jumps to the next line, subtracting it to the previous, and so forth (you may have to calculate a stride instead of using im_width directly, rounding up to the nearest 32 bits; and if your sample image isn't a multiple of 4 pixels wide, you may see strange results due to not using a correct stride).
Useful references for others following along: the BITMAPFILEHEADER (starting at file offset 0) which contains the signature, file size (your FileSize), and image offset (im_offset), which is followed by the BITMAPINFOHEADER, which has information like the width, height, and bit depth.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With