News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Another little toy (barber pole)

Started by NoCforMe, January 01, 2023, 06:08:25 PM

Previous topic - Next topic

jj2007

#30
Quote from: NoCforMe on January 03, 2023, 12:57:19 PM
So now I have a packaging problem with this project.

Have fun with the attachments - just drag your bmp file over EmbedData.exe :biggrin:

Usage:
include \masm32\include\masm32rt.inc
.DATA
include \Masm32\Members\NoCForMe\BarberPole\_BPoutline.inc

.code
start:
  mov esi, offset BPoutline-4
  lodsd

  MsgBox 0, cat$(str$(eax), " is the file size"), "Embedded data:", MB_OK
  exit

end start


Thanks to the lodsd, esi now points to the "resource", and you know it has 1674 bytes :cool:

EDIT: Modified version generates the *.asm file for testing. For example, a 160k jpg image generates a 3,400 lines inc file :biggrin:

NoCforMe

Thanks, Fearless. Good ideas there.

So: I finally accomplished getting the control into one module; only one .obj file, so easier for users to deal with it. (There's an .inc file as well for the user.) I must say this was quite a painful process to get it to work. Here's what I had to do:

1. Create the bitmaps as data within the control code:
I used Hutch's bin2db tool which worked very nicely to create assembler DB text from both bitmaps, then just pasted them into the code. This was actually the easiest and most trivial part of the process.

2. Copy values out of the bitmaps into a BITMAP structure:
I found that the two bitmaps I was using had different header structures: one used the BITMAPCOREHEADER structure while the other used BITMAPINFOHEADER. (I have no idea why this is: both bitmaps are close in size and were created with the same tools, CorelDRAW and Paint Shop Pro.) So I needed to get the values into a consistent form. The way to tell these two structures apart, by the way, is to look at the biSize (or bcSize, same difference) element to get the size of the structure: BITMAPCOREHEADER is 12 bytes, while the other one is 40. First pain in the ass.

3. Create a bitmap:
Seems strange, doesn't it? I've got a bitmap here, all the bytes in memory, but I can't use it for anything. So I have to create a new bitmap. I first tried using CreateBitmapIndirect(), but that didn't work at all. So now what I have to do is this:

3a. Create a dummy window:
Since the next step requires a handle to a DC, I need to create a dummy window so I can get a DC from it. I created a zero-size window of type "static" (no need to mess with RegisterClassEx()).

3b. Get a DC:
That's easy, just use GetDC, dummyWinHandle.

3c. Copy values from BITMAP to BITMAPINFO:
Since the next step requires a BITMAPINFO structure, do another copy. (I just realized: why didn't I just copy to this structure in the first place? Will go back and simplify my code later.)

3d. Copy the palette from the in-memory bitmap to the BITMAPINFO struct:
I copy the RGBQUAD structures from the in-memory bitmap to the bmiColors element of the BITMAPINFO struct. Slight pain in the butt here: you can't create this as a local variable because bmiColors is defined as only a single RGBQUAD element; we need 16 of them in this case (since I know that the bitmaps will always be 16 colors). So I had to define a static variable of type BITMAPINFO, then follow it with 15 RGBQUAD structs so the data would fit.
Further pain in the ass: Depending on which type of header (BITMAPCOREHEADER or BITMAPINFOHEADER) the file had, the RGBQUAD entries are either 3 or 4 bytes, so I had to be sure to move the right number of bytes depending.

3e. Create the bitmap:
Phew! Finally I can create the bitmap, using CreateDIBSection(). After looking online and trying several other methods, this is the one that finally worked. It's easy; you pass it a pointer to the filled-out BITMAPINFO struct, with the palette entries copied into it, and it gives you back a pointer to where the actual bitmap data goes. It returns a handle to the bitmap, which I store for the paint handler.

4. Copy the bitmap bits into the new bitmap:
Easy peasy; you get a pointer where they should go, you know where they are in the in-memory bitmaps, and the count of bytes to move is just height X width.

All that's left at this point is to clean up (release the DC and destroy the dummy window).

Like I said, pain in the ass, but it works.

But wait, there's more. No project like this is complete without an ugly workaround. There's a gnarly problem I couldn't solve: I got both bitmaps to render, but for some reason the "animation" bitmap (the barber-pole stripes) had an ugly red blotch at the top. I could not figure out why this was happening: I tracked the bitmap data through my code, and it wasn't getting corrupted. I also discovered that the bitmap data in the file did not exactly correspond to the pixels I could see when I edited the images; I still am stumped by this.

Turned out that for some unknown reason, everything worked fine if I flipped the image vertically by setting the height value to a negative number; this changes the image from top-down to bottom-up. To compensate I had to flip the actual image from side to side (mirroring). So it's a kluge, but hey, it works.

The other strange thing is that if I take a screenshot of the control, the pixels I see on-screen are not at all the same pixels in the bitmap, as I would have thought. Obviously a lot of processing is going on there behind the scenes to render it, and I suspect that's maybe why I was getting that big red blotch at the top (the first non-white color in the image is red, and maybe it's using that as some kind of default fill color or something; who the hell knows?).
Assembly language programming should be fun. That's why I do it.

zedd151

Hi, NoCfor me. How's it going frying this fish? Yes, I know you got sidetracked  :tongue: . Any good progress here?

fearless

Can probably skip a good few of the steps just using the function that vortex has, or the one I adapted from it. I include a simple example to show how to use it.


NoCforMe

#34
99% done. I'm pretty sure. We'll see.
NOTE: .zip archive has been updated (forgot to include the control's .obj file)

Here's the whole package, the control module and the demo program, ready for use. There's even a readme file. I hope someone's silly enough to take the time to try this out. I'd love to see this in someone's program.

The demo program shows how you can vary the speed of the animation by setting the delay time. This can be set through messages to the control. You can also set the background color to something other than the default (which is the standard Windows dialog BG color).

Have fun! I did when I wrote this.
Assembly language programming should be fun. That's why I do it.

NoCforMe

Quote from: fearless on January 05, 2023, 05:16:40 AM
Can probably skip a good few of the steps just using the function that vortex has, or the one I adapted from it. I include a simple example to show how to use it.
Ack! You're absolutely right. I used the wrong function (CreateDIBSection() instead of CreateDIBitmap(), which copies all the data for you). My code works, but with unnecessary effort copying the RGBQUADS and bitmap bits. Will change it at some point. For now, since it ain't broke I don't want to fix it.

I also didn't realize you could use CreateDC() to create a DC out of thin air without having to create a dummy window like I did. Live and learn.
Assembly language programming should be fun. That's why I do it.

zedd151

 :tongue:  When you are finishing that 1%, probably would be a good time to try fearless's improvements. I have yet to look at your current progress. Will do so when I'm back in the house.  :biggrin:

NoCforMe

So Fearless, regarding your suggestion to try using patterned brushes instead of a bitmap: interesting idea. I'm familiar with patterned brushes, but I wanted to check something with you.

Currently my animation has 13 iterations; does this mean I would need to create 13 brushes and alternate them? So far as I know you can't change the starting position of a brush like you can a bitmap with BitBlt(). Would I still get a performance improvement over using a bitmap?
Assembly language programming should be fun. That's why I do it.

zedd151

Quote from: NoCforMe on January 05, 2023, 06:51:40 AM
I hope someone's silly enough to take the time to try this out.

Fantastic! "Look ma, no resources!"
Your little testpiece works well. I cannot detect any flicker in the animations.  :thumbsup:


later: Played with the delay as well, works good.

jj2007

QuoteThere are two files for this control:
   - BPcontrol.obj:  link this with your program
   - BarberPole.inc: include file for your .ASM source file
No BPcontrol.obj in the archive :rolleyes:

fearless

Quote from: NoCforMe on January 05, 2023, 07:12:41 AM
So far as I know you can't change the starting position of a brush like you can a bitmap with BitBlt(). Would I still get a performance improvement over using a bitmap?
You can use SetBrushOrgEx to change the start position of the brush if its just the full bitmap image, so you probably want to account for the size of the bitmap to account for that for the animation.

    Invoke SetBrushOrgEx, hdcMem, dwXOrg, dwYOrg, 0; //Set the brush origin (relative placement)     
    Invoke FillRect, hdcMem, Addr rect, hBackBrush
    Invoke SetBrushOrgEx, hdcMem, 0, 0, 0; // reset the brush origin (relative placement)


I think patblt will repeat the brush. Might have to try out both options to see what works. I imagine if the patblt fills the entire control size, then you can set the start coords position outside of the control (say -12) and increment the position to make the animation so that the last position is 0 if you follow my thought.

Ive used SetBrushOrgEx before to fake a transparent background for a child control - based on a large bitmap for the background dialog (was a picture of a old yellowed pages of a book). I had to calc the window rect of the child control relative to the parent to use that for the brush x y coords, which then paints that backbrush starting at those coords onto child control, thus making it look like the child control background is transparent.



NoCforMe

Two things:
1. I updated the .zip archive in reply #34 to include BPcontrol.obj. Glad someone's interested in it!
2. fearless: Thank you. I reduced that mess o'code I had to just 16 lines. So much simpler! Here's what I ended up with:


;====================================
; GetBP_BMPdata (bmpPtr)
;
; "Opens" BMP "file" (from memory), massages it.
;
; Returns handle to bitmap.
;
; Hat tip to "Fearless" of the MASM forum for
; vastly simplifying this process!
;====================================

GetBP_BMPdata PROC bmpPtr:DWORD
LOCAL hDC:HDC

INVOKE CreateDC, OFFSET CreateDCdeviceName, NULL, NULL, NULL
MOV hDC, EAX
MOV EDX, bmpPtr
LEA ECX, [EDX + SIZEOF BITMAPFILEHEADER] ;Point to BITMAPINFOHEADER header.
MOV EAX, [EDX].BITMAPFILEHEADER.bfOffBits ;Offset of bitmap bits.
ADD EDX, EAX ;Pointer to bitmap bits.
INVOKE CreateDIBitmap, hDC, ECX, CBM_INIT, EDX, ECX, DIB_RGB_COLORS
PUSH EAX
INVOKE DeleteDC, hDC
POP EAX
RET ;Return w/handle.

GetBP_BMPdata ENDP
Assembly language programming should be fun. That's why I do it.

jj2007

Very nice :thumbsup:

GuiParas equ "Barber Pole", w200, h444, m0, b LiteGrey
include \masm32\MasmBasic\Res\MbGui.asm
  include BarberPole.inc
  invoke InitBarberPole, rv(GetModuleHandle, 0)
  SetGlobals hCurrent
  GuiControl MyStatic, "static", "Left-click to start, right-click to stop", y50, x600, w400, h0+80
  GuiControl bp1, "barberpole", y50, x50, w0+32, h0+96
  GuiControl bp2, "barberpole", y150, x200, w0+32, h0+96
  GuiControl bp3, "barberpole", y250, x350, w0+32, h0+96
  GuiControl bp4, "barberpole", y350, x500, w0+32, h0+96
  GuiControl bp5, "barberpole", y450, x650, w0+32, h0+96
  GuiControl bp6, "barberpole", y550, x800, w0+32, h0+96

Event Message
  .if uMsg_==WM_SETCURSOR
        m2m hCurrent, wParam_
  .elseif uMsg_==WM_PARENTNOTIFY
        mov eax, WM_BARBERPOLE_ON
        .if word ptr wParam_==WM_RBUTTONDOWN
                inc eax
        .endif
        invoke SendMessage, hCurrent, eax, 0, 0
  .endif
GuiEnd         ; OPT_DebugL    BPcontrol.obj

zedd151


Quote from: NoCforMe on January 05, 2023, 11:47:08 AM
I reduced that mess o'code I had to just 16 lines. So much simpler! Here's what I ended up with: ...
Congrats!
Quote from: jj2007 on January 05, 2023, 11:57:06 AMVery nice :thumbsup:
I second that... 
jj, nice example.

NoCforMe

JJ--very cuuute! (And yes, that's a compliment; I likes cute.)

Now we just need to design a whole steampunk-looking interface to incorporate that. I actually looked into that a little; turns out that that topic has already been pretty well covered by game designers.

BTW, do you know why barber-pole stripes are red and blue? The answer: blood. Back in the day, barbers were also surgeons and doctors of a sort. Amputated limbs and stuff. (Sweeney Todd is a good look at that time ...)
Assembly language programming should be fun. That's why I do it.