0

i'm programming in assembly and i want to open an .bmp image and show it in the screen, but i have no idea how to do this. I don't want to use any external lib to do this. Does anyone know articles or sites where i can study this? Thanks.

Obs. I'm using Linux and NASM.

user1150235
  • 11
  • 1
  • 2

3 Answers3

1

If you don't want to use any external libraries, you'll need to implement the whole BMP file format yourself, so the BMP specification would be a good starting point.

As for displaying an image, how do you plan to control the display? You probably don't want to talk directly to the hardware; that'd basically mean writing your own video driver. You could connect to an X11 display, but that involves external libraries (Xlib or XCB).

At any rate, this will be rather tedious to do in assembly. Typically, assembly is reserved for specialized tasks that can't be done (fast enough, or at all) in higher-level languages. Displaying an image is not one of those tasks.

Wyzard
  • 33,849
  • 3
  • 67
  • 87
0

This sample program create a bitmap and draw a line in there.

; Program to draw lines to 1000x1000 Bitmap File
; Compile with:
;     nasm -f elf64 -o drawLines64.o drawLines64.asm
; Link with:
;     ld -m elf_x86_64 -o drawLines64 drawLines64.o
; Run with:
;     ./drawLines64
;==============================================================================
; Author : Rommel Samanez
; https://github.com/rsamanez/assemblerx64
;==============================================================================

global _start


section .data
  fileName:  db "lines.bmp",0
  fileFlags: dq 0102o         ; create file + read and write mode
  fileMode:  dq 00600o        ; user has read write permission
  fileDescriptor: dq 0
  bitmapImage: times 3000000 db 0
section .rodata    ; read only data section
    bmpWidth   equ  1000
    bmpHeight   equ  1000
    bmpSize equ 3 * bmpWidth * bmpHeight
    headerLen equ 54
    header: db "BM"         ; BM  Windows 3.1x, 95, NT, ... etc.
    bfSize: dd bmpSize + headerLen   ; The size of the BMP file in bytes
            dd 0                 ; reserved
    bfOffBits: dd headerLen   ; The offset, i.e. starting address, of the byte where the bitmap image data (pixel array) can be found
    biSize:   dd 40    ; header size, min = 40
    biWidth:  dd bmpWidth
    biHeight: dd bmpHeight
    biPlanes:       dw 1     ; must be 1
    biBitCount:     dw 24    ; bits per pixel: 1, 4, 8, 16, 24, or 32
    biCompression:  dd 0     ; uncompressed = 0
    biSizeImage:    dd bmpSize  ; Image Size - may be zero for uncompressed images
    hresolution:    dd 0
    vresolution:    dd 0
    palettecolors:  dd 0
    importantcolors: dd 0

section .text

; drawPixel(x,y,color{RGB}) ( R8,R9,R10) 
drawPixel:
    push rax
    push rbx
    ;---------------------------------
    mov rax,3000
    mul r9
    push rax
    mov rax,3
    mul r8
    pop rbx
    add rax,rbx         ; offset:  rax = 3*x+3000*y
    push r10
    pop rbx
    mov word[bitmapImage+rax],bx
    shr rbx,16
    mov byte[bitmapImage+rax+2],bl
    ;----------------------------------
    pop rbx
    pop rax
    ret
; drawLine(x1,y1,x2,y2,color) color = R10  x1=r8 y1=r9 x2=r11 y2=r12    
drawLine:
    push rax
    push rbx
    push rcx                  ;  line   y = M*x/1000   M=w*1000/q  w=y2-y1  q=x2-x1 
    push rdx
    ;-----------------------------------
    mov rax,r12             ; rax <- y2
    sub rax,r9              ; rax <- y2 - y1
    mov rbx,1000
    mul rbx                 ; rax <- rax*1000
    mov rbx,r11             ; rbx <- x2
    sub rbx,r8              ; rbx <- x2 - x1
    xor rdx,rdx             ; rdx = 0
    div rbx                 ; RAX / RBX  RAX= value  M
    push rax
    pop rcx                 ; rcx <- M
    mov rbx,r8              ; rbx <- x1
    ;-----------------------------------------
    push r8
    push r9
nextPixel:
    mov rax,rbx             ; rbx = x
    mul rcx                 ; rax = M*x
    push rcx
    xor rdx,rdx
    mov rcx,1000
    div rcx                 ; rax = M*x/1000
    pop rcx
    mov r8,rbx              ; r8 <- x
    mov r9,rax              ; r9 <- y
    call drawPixel
    inc rbx
    cmp rbx,r11
    jnz nextPixel
    pop r9
    pop r8
    ;-----------------------------------
    pop rdx
    pop rcx
    pop rbx
    pop rax
    ret


drawLines:
    mov r8,0
    mov r9,0
    mov r10,0xff0000        ; color RED
    mov r11,900
    mov r12,900
    call drawLine
    ret  

_start:
    mov rax,2                 ;   sys_open
    mov rdi,fileName          ;   const char *filename
    mov rsi,[fileFlags]       ;   int flags
    mov rdx,[fileMode]        ;   int mode
    syscall
    mov [fileDescriptor],rax

    ; write the header to file
    mov rax,1                 ; sys_write
    mov rdi,[fileDescriptor]
    mov rsi,header
    mov rdx,54
    syscall

    call drawLines

    ; write the Image to file
    mov rax,1                 ; sys_write
    mov rdi,[fileDescriptor]
    mov rsi,bitmapImage
    mov rdx,3000000
    syscall

    ; close file Descriptor
    mov rax,3                 ; sys_close
    mov rdi,[fileDescriptor]
    syscall

    ; EXIT to OS  sys_exit
    mov rax,60
    mov rdi,0
    syscall
0

This is not an answer for this specific question, but it provides details on how to transform any image into an assembly variable which then can be used in an assembly program and rendered with a library that is capable of rendering images.

Convert an image to an assembly variable using Python and PIL. In short, open the image, get the image pixel values which are touples of color channels: (R, G, B, A). Take these values and transform them in hexadecimal values. With this content an assembly variable can be created.

from PIL import Image

def getHexValues(imgPath):
    im = Image.open(imgPath)
    im = im.convert('RGB')
    pixels = im.load()
    x = im.size[0]
    y = im.size[1]
    hexels = get_pixels_to_hex(pixels, x, y)

def get_pixels_to_hex(pixels, width, height):
    hexels = []
    
    for x in range(width):
        for y in range(height):
            pixel = pixels[x, y]
            hexel = "0"
            try:
                for color in pixel[:3]:
                    hexVal = "{:02x}".format(color)
                    hexel += hexVal
            except IndexError as ie:
                raise Exception("Error: Color index out of bounds. Image is not RGB") from ie
            hexel += 'h'
            hexels.append(hexel)
    return hexels

The generated variable (I.E. var1_0) can be imported in an assembly program and loaded in memory to be drawn:

; arg1 - pointer to the pixel vector
; arg2 - x of drawing start position
; arg3 - y of drawing start position
make_text proc
    push ebp
    mov ebp, esp
    pusha

    lea esi, var1_0
    
draw_image:
    mov ecx, image_height
loop_draw_lines:
    mov edi, [ebp+arg1] ; pointer to pixel area
    mov eax, [ebp+arg3] ; pointer to coordinate y
    
    add eax, image_height 
    sub eax, ecx ; current line to draw (total - ecx)
    
    mov ebx, area_width
    mul ebx ; get to current line
    
    add eax, [ebp+arg2] ; get to coordinate x in current line
    shl eax, 2 ; multiply by 4 (DWORD per pixel)
    add edi, eax
    
    push ecx
    mov ecx, image_width ; store drawing width for drawing loop
    
loop_draw_columns:

    push eax
    mov eax, dword ptr[esi] 
    mov dword ptr [edi], eax ; take data from variable to canvas
    pop eax
    
    add esi, 4
    add edi, 4 ; next dword (4 Bytes)
    
    loop loop_draw_columns
    
    pop ecx
    loop loop_draw_lines
    popa
    
    mov esp, ebp
    pop ebp
    ret
make_text endp

You can see an example of this in practice by checking this tool that transforms images in assembly variable declarations.

OldDew
  • 57
  • 10