News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Operations on bitmap file

Started by ktosik2, July 13, 2015, 07:25:32 PM

Previous topic - Next topic

dedndave

well - that may not be the only mistake
and - i am not sure that pBitmap points to a BITMAPFILEHEADER
i only made a guess based on the previous code

again, guessing....
pBitmap is probably a pointer to a buffer
i.e., it is a DWORD that holds an address of a buffer
i also guess you read an entire BMP file into the buffer

it's a reference pointer, that's why we use the term "dereference"
intel processors do not have instructions that allow you to access the buffer from a pointer in memory
(can't think of any processors that do, actually)
so, to access the buffer, you must first dereference it by loading the address into a register

dedndave

here is a little example of creating a 24-bit DIB section
in the WM_CREATE code, i set a few pixels at the bottom of the image for test purposes

dedndave

2 small improvements in the CreateDIB24 PROC...

1) it allows for negative height values, creating "upside-down" bitmaps
2) it returns bytes per line in ECX

;***********************************************************************************************

CreateDIB24 PROC USES EDI dwDibWidth:DWORD,dwDibHeight:DWORD

;  Returns: EAX = HBITMAP, DIB section handle
;           ECX = dwBytesPerLine, bytes per image line
;           EDX = pvBits, pointer to DIB section pixel data

;------------------------------

    LOCAL   _bmi24      :BITMAPINFOHEADER

;BITMAPINFOHEADER <>
;  biSize          dd ?               ;BITMAPINFOHEADER structure size
;  biWidth         dd ?               ;image width in pixels
;  biHeight        dd ?               ;signed image height in pixels
;  biPlanes        dw ?               ;= 1
;  biBitCount      dw ?               ;= 24 for 24-bit images
;  biCompression   dd ?               ;= BI_RGB
;  biSizeImage     dd ?               ;image data bytes
;  biXPelsPerMeter dd ?               ;= 0
;  biYPelsPerMeter dd ?               ;= 0
;  biClrUsed       dd ?               ;= 0
;  biClrImportant  dd ?               ;= 0

;------------------------------

    xor     edi,edi                                           ;EDI = 0
    mov     edx,dwDibHeight
    mov     eax,dwDibWidth
    mov     _bmi24.biSize,sizeof BITMAPINFOHEADER
    mov     _bmi24.biWidth,eax
    mov     _bmi24.biHeight,edx
    inc     eax
    imul    eax,3
    and     al,-4
    or      edx,edx
    push    eax                                               ;save bytes per line
    .if SIGN?
        neg     edx
    .endif
    mov     _bmi24.biPlanes,1
    mul     edx
    mov     _bmi24.biBitCount,24
    mov     _bmi24.biCompression,edi                          ;BI_RGB = 0
    mov     _bmi24.biSizeImage,eax
    mov     _bmi24.biXPelsPerMeter,edi
    mov     _bmi24.biYPelsPerMeter,edi
    mov     _bmi24.biClrUsed,edi
    mov     _bmi24.biClrImportant,edi
    push    edi                                               ;initialize pvBits to 0
    INVOKE  GetDC,edi                                         ;HWND_DESKTOP = 0
    xchg    eax,edi                                           ;EDI = hdcDesktop, EAX = 0
    mov     edx,esp                                           ;EDX = pointer to pvBits
    INVOKE  CreateDIBSection,edi,addr _bmi24,DIB_PAL_COLORS,edx,eax,eax
    push    eax                                               ;save hbmpDIBSection
    INVOKE  ReleaseDC,HWND_DESKTOP,edi
    pop     eax                                               ;EAX = hbmpDIBSection
    pop     edx                                               ;EDX = pvBits
    pop     ecx                                               ;ECX = dwBytesPerLine
    ret

CreateDIB24 ENDP

;***********************************************************************************************

ktosik2

At the beginning I want to apologise that I didn't respond for so long. This is because I hadn't time to work on my project. Now I have a little more time.

Yesterday I was trying to make function SaveDIB32 from Edgar's Library working (we discuss about it few posts earlier) and I managed it! Here is code:
INVOKE  CreateFile,OFFSET newFileName,GENERIC_ALL,FILE_SHARE_READ OR FILE_SHARE_WRITE, NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL
mov     hFile,eax
INVOKE ConvertToDIB32, hBitmapNegatyw
mov hBitmap32Save, eax
INVOKE  SaveDIB32, hBitmap32Save, hFile
INVOKE  CloseHandle, hFile

I think you see that SaveDIB32 need hBitmap object as first parameter, NOT pvBits, as I thought earlier. It works perfectly.

DednDave, thanks for help, I tested your sample of code:
mov     ecx,pBitmap
lea     edx,[ecx+sizeof(BITMAPFILEHEADER)] ; start of the BITMAPINFOHEADER header

INVOKE CreateDIB24, BITMAPINFOHEADER.biWidth[edx], BITMAPINFOHEADER.biHeight[edx]
mov hBitmapNegatyw, eax ;hBMP

mov dword ptr [edx],0FFh       ;blue
mov dword ptr [edx+3],0FF00h   ;green
mov dword ptr [edx+6],0FF0000h ;red

yesterday and it worked, but when I write:
mov     ecx,pBitmap
lea     edx,[ecx+sizeof(BITMAPFILEHEADER)] ; start of the BITMAPINFOHEADER header

INVOKE CreateDIB24, BITMAPINFOHEADER.biWidth[edx], BITMAPINFOHEADER.biHeight[edx]
mov hBitmapNegatyw, eax ;hBMP
mov pvBits, edx ;<<<<<<<<<
mov dword ptr [pvBits],0FFh       ;blue
mov dword ptr [pvBits+3],0FF00h   ;green
mov dword ptr [pvBits+6],0FF0000h ;red

the bitmap is created, but the pixels are not set. What am I doing wrong?

Next, is there a possibility to write something like this?:

.DATA
imageWidth DD 0
imageHeight DD 0
(...)
mov     ecx,pBitmap
lea     edx,[ecx+sizeof(BITMAPFILEHEADER)] ; start of the BITMAPINFOHEADER header
;mov imageWidth, BITMAPINFOHEADER.biWidth[edx]
;mov imageHeight, BITMAPINFOHEADER.biHeight[edx]

I get error:  "error A2070: invalid instruction operands"  in commented lines. If there is such possibility, it would simplify getting size of the image.

dedndave

mov pvBits, edx ;<<<<<<<<<
mov dword ptr [pvBits],0FFh       ;blue
mov dword ptr [pvBits+3],0FF00h   ;green
mov dword ptr [pvBits+6],0FF0000h ;red


that is invalid addressing
[pvBits] is a dword - the address of it is assigned by the assembler in your .DATA section
[pvBits+3] is that address, plus 3 (bad)

in order to address the bits, the value pvBits needs to be in a register

MichaelW

Most of the instructions, and this includes MOV, support only one memory operand. A common way around this limitation is to do the move through a suitable register, in two instructions.

include \masm32\include\masm32rt.inc
.data
    bmih          BITMAPINFOHEADER <SIZEOF BITMAPINFOHEADER,1,2>
    imageWidth    DD 0
    imageHeight   DD 0   
.code
start:
    lea   edx, bmih
    mov   eax, [edx].BITMAPINFOHEADER.biWidth
    mov   imageWidth, eax
    mov   eax, [edx].BITMAPINFOHEADER.biHeight
    mov   imageHeight, eax
    printf( "%d\n%d\n\n", imageWidth, imageHeight)
    inkey
    exit
end start
Well Microsoft, here's another nice mess you've gotten us into.

ktosik2

Thanks for your replies!

Next I try to create dib section and get pvBits to the image that I load to my application.
Load an image: (thanks Vortex for these functions)

INVOKE GetOpenFileName, ADDR ofn
.IF eax==TRUE
INVOKE RtlZeroMemory, ADDR FilePath, MAXSIZE
INVOKE lstrcat, ADDR FilePath, ofn.lpstrFile
INVOKE  ReadFileToMem, ADDR FilePath, ADDR pBitmap, ADDR pNumbOfBytesRead
INVOKE CreateBmpFromMem, hWnd, pBitmap, ADDR hDC1
mov    hBitmap, eax
.ENDIF


This code:

mov     ecx,pBitmap
lea     edx,[ecx+sizeof(BITMAPFILEHEADER)] ; start of the BITMAPINFOHEADER header
mov eax, BITMAPINFOHEADER.biWidth[edx]
mov imageWidth, eax
mov eax, BITMAPINFOHEADER.biHeight[edx]
mov imageHeight, eax

generates image size properly, so I think that pBitmap is right generated by ReadFileToMem function.

Then I wrote CreateDIBSection call:

mov     ecx,pBitmap
lea     edx,[ecx+sizeof(BITMAPFILEHEADER)] ; start of the BITMAPINFOHEADER header
mov eax, edx
xor edx, edx

INVOKE CreateDIBSection, 0, eax, DIB_RGB_COLORS, edx, 0, 0

and when I call these instructions:

mov dword ptr [edx],0FF00h
mov dword ptr [edx + 3],0FF00h
mov dword ptr [edx + 6],0FF00h
mov dword ptr [edx + 9],0FF00h

the program stops working and I have to close it. What am I doing wrong?
These lines:

.DATA?
pvBits              LPVOID ?
(...)
INVOKE CreateDIBSection, 0, eax, DIB_RGB_COLORS, OFFSET pvBits, 0, 0
mov edx, pvBits
mov dword ptr [edx],0FF00h
mov dword ptr [edx + 3],0FF00h
mov dword ptr [edx + 6],0FF00h
mov dword ptr [edx + 9],0FF00h

don't work too. Program stops working and I have to close it.
These lines:

INVOKE CreateDIBSection, 0, eax, DIB_RGB_COLORS, ADDR pvBits, 0, 0 ;ADDR instead of OFFSET
mov edx, pvBits
mov dword ptr [edx],0FF00h
mov dword ptr [edx + 3],0FF00h
mov dword ptr [edx + 6],0FF00h
mov dword ptr [edx + 9],0FF00h

don't work. The application is working, but there is no effect on the image.

rrr314159

The last version with "addr pvBits" is right, except you're not using a device context. Normally you'd use CreateCompatibleDC to get a handle to a device context, then supply that as the first argument to CreateDIBSection. After writing to the address contained in pvBits, as you are doing, that DC handle is used to send the bitmap to the screen's DC - normally using BitBlt - then you can finally see the image.

It's a little complicated, ask for further help if this doesn't make sense yet. Or just wait for dedndave to post the complete program
I am NaN ;)

ktosik2

I tried this: (this is modificaton of CreateDIB24 function from DednDave)

xor     edi,edi                                                          ;EDI = 0
mov     _bmi24.biSize,sizeof BITMAPINFOHEADER
mov     eax,imageWidth
mov     _bmi24.biWidth,eax
mov     edx,imageHeight
mov     _bmi24.biHeight,edx
inc     eax
imul    eax,3
and     al,-4
mov     _bmi24.biPlanes,1
mul     edx
mov     _bmi24.biBitCount,24
mov     _bmi24.biCompression,edi                          ;BI_RGB = 0
mov     _bmi24.biSizeImage,eax
mov     _bmi24.biXPelsPerMeter,edi
mov     _bmi24.biYPelsPerMeter,edi
mov     _bmi24.biClrUsed,edi
mov     _bmi24.biClrImportant,edi

push edi
INVOKE GetDC,edi
xchg eax,edi
mov     edx,esp
INVOKE  CreateDIBSection,edi,addr _bmi24,DIB_PAL_COLORS,edx,eax,eax
push    eax                                                             ;save hbmpDIBSection
INVOKE  ReleaseDC,hwnd,edi
pop     eax                                                              ;EAX = hbmpDIBSection
pop     edx                                                              ;EDX = pvBits

mov dword ptr [edx],0FF00h
mov dword ptr [edx + 3],0FF00h
mov dword ptr [edx + 6],0FF00h
mov dword ptr [edx + 9],0FF00h



The program is running, there are not any errors, but the four pixels are not set into green.
I tried a lot of possibilities and none of them worked fine. Can someone help?

dedndave

you really need to post a complete working program
we need to see, not only how the DIB section is created,
but also, how it is displayed - we need to see your WM_PAINT code
if you show us partial code, we have to write a working program to test it   :(

rrr314159

@ktosik2,

You're still not using the "proper" approach. AFAIK you can't write directly to screen video memory as you're trying to do. (I should review my notes but am busy; could be wrong about that.) Anyway one way that works (could be others) is, as I said, use CreateCompatibleDC to create a DC (the "background" or "in-memory" DC). Then write to that, finally use BitBlt to put it "forward" to the screen (which is the DC you're using here). This is more flexible technique anyway, and avoids flicker. Apart from that your code appears correct, but dedndave is (of course) right, post the complete prog including WM_PAINT code. Another thing u could do, hope I get around to posting the code to perform the above steps, after cleaning the gutters and mowing the lawn :icon_confused:
I am NaN ;)

jj2007

Quote from: rrr314159 on August 28, 2015, 06:18:19 AMyou can't write directly to screen video memory

Indeed. Search the old and new forums for setdibits - there is more than enough material to keep you busy for a week.

rrr314159

No, that's not "writing directly to the screen" as I meant it (let's not split hairs). It's just another way to BitBlt, evidently. The actual modifying of pixels - as, for instance, "mov dword ptr [edx],0FF00h" must be done to a "drawing surface", a DIB, in memory, then sent to the screen. The only way to "write directly to the screen" would be SetPixel and similar functions; for real image manipulation of course they're out of the question being so slow. Saw this comment in old thread

Quote from: MichaelW, long agoI posted an example here that uses a DIB as a drawing surface for a fast set-pixel routine, and SetDIBits to copy the bitmap bits from the DIB to a DDB for display.

- And many other comments, plus MSDN, make the point clear. In fact zemtex in that thread gives a chart showing a memory DIB being modified and then BitBlt'ed to the screen. It's not clear, he may be doing exactly my technique or a slight mod of that.

- I studied SetDIBits many months ago and wound up with my approach ... one big advantage, there's no flicker this way; pretty sure there were other advantages as well. They're very similar techniques, equal amounts of work; perhaps SetDIBits has advantages in some applications

- Finally - you left out of my quote both "AFAIK" and "could be wrong about that". But I'm still pretty sure my (or, call it zemtek's since apparently he was using it long before me) way is as good as any ... based on research which I don't have time to look up now. That's why I have to be a bit vague at the moment
I am NaN ;)

jj2007

Quote from: rrr314159 on August 28, 2015, 09:36:07 AM
No, that's not "writing directly to the screen"

I didn't write that ::)

SetDiBits is certainly not writing directly to the screen, but you can POKE pixels via mov byte ptr [reg], al or similar. That is as fast as writing directly to the screen, except that you need to BitBlt it afterwards. If you know a better alternative to awfully slow SetPixel, let us know.

rrr314159

you can POKE pixels via mov byte ptr [reg], al or similar

- we only "POKE" with Basic :biggrin:

But seriously, you're talking about "poking" into a bitmap in memory then BitBlt (or StretchBlt, whatever) to the screen. The problem is that initial bitmap must be set up with API functions as a "compatible bitmap" in a device context. If you already have the pixels you want to send to the screen in memory, 2 bad, they can't be sent directly to the screen. Instead u must make a DIB: and it allocates its own memory, you can't tell it to use yours. Then you first transfer your existing picture into that in-memory DIB, finally to the screen. There are actually good reasons for this but it's frustrating for us old guys, who used to write direct to video. Anyway the right thing to do is make the DIB in a DC - it will give you a handle to mem - then "poke" (draw your picture) into it, and send to screen. Assuming you're doing a pretty large picture, like the whole screen, it's actually fast enough (for a few pixels it's a lot of overhead of course, SetPixel is better). The whole mechanism (to use the example of my prog, "MathMovie") takes as little as a couple milliseconds, while the drawing takes at least 5 or 10, up to 50 and more for a "really cool" picture - so the whole DC / DIB / Blt business is pretty negligible. I believe that couple milliseconds is as fast as possible

That's assuming your picture is "free-form". If it's composed of triangles, squares, fill areas, bmps, 3-d "reality" constructs, various other things, then there are many specialized APIs in GDI, DirectDraw to do it faster. But for a non-standard, unique picture zemtek's technique is the best, AFAIK
I am NaN ;)