News:

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

Main Menu

Progress with GDI+

Started by NoCforMe, December 07, 2018, 03:03:03 PM

Previous topic - Next topic

NoCforMe

In our last episode, I was struggling to do a simple task: display a JPEG image using GDI+. I managed to show an image, but at a reduced size. Well, I'm happy to report that I now know how to scale images, and have thereby revealed another piece of the GDI+ puzzle that I'll share here.

The key is GdipGdipScaleWorldTransform(), which turns out to be in the following form:
GdipGdipScaleWorldTransform (graphicsHandle, Xscale, Yscale, matrixOrder
o graphicsHandle is the Graphics "object" obtained from GdipCreateFromHDC (hDC)
o Xscale and Yscale are scaling factors as floating-point numbers (32-bit, REAL4)
o matrixOrder is an optional pre- or post-multiplication order parameter

I set the scaling factors to 2, and the picture was about double the previous size, so this seems to work OK. (I set matrixOrder to 0 for the default behavior.)

I'm going to keep slogging through this stuff, as paltry as the documentation is (thanks to those here who provided pointers to what little exists). More questions keep popping up:

Question: How do I deal with 32-bit floats? (Yes, I know this is not a GDI+ question, but I need to figure this out to move forward here.) I can't use our FPUlib because that deals with REAL10s. What form are these numbers in? Are there any library functions here to manipulate these types? or anything in MASM itself? I was able to define some 32-bit float constants to use in this code, but that's about it.

Question: What format is a GDI+ graphics matrix in? It appears to be a list of 6 numbers, I'm going to assume DWORDS in some form (32-bit float I'd guess). Is that correct? What order are they in--row, then column? I thought there'd be a structure definition in one of the include files, but haven't found one.

So Hutch: I read a posting of yours from earlier this year asking for help with GDI+. I'm hoping that my blunderings about in this area might be of some help to you. I figure if I can't find the documentation I need, I can always try the alternate method of just prodding and poking and seeing what happens. Maybe we can eventually document this stuff ourselves ...

;============================================
; -- JPEG testbed, using GDI+ --
;
;============================================


include \masm32\include\masm32rt.inc
include \masm32\include\gdiplus.inc
includelib \masm32\lib\gdiplus.lib


;============================================
; Defines, macros, prototypes, etc.
;============================================

WinMain PROTO :DWORD

$mainWinWidth EQU 600
$mainWinHeight EQU 500

;===== Window styles: =====
$mainWinStyles EQU WS_OVERLAPPEDWINDOW OR WS_CLIPCHILDREN OR WS_VISIBLE

;===== Window background colors: =====
$bkRED EQU 254
$bkGRN EQU 243
$bkBLUE EQU 199

$BkColor EQU $bkRED OR ($bkGRN SHL 8) OR ($bkBLUE SHL 16)

;============================================
; HERE BE DATA
;============================================
.data

WC WNDCLASSEX < SIZEOF WNDCLASSEX, \
CS_HREDRAW or CS_VREDRAW or CS_BYTEALIGNWINDOW, \
NULL, \ ;lpfnWndProc
NULL, \ ;cbClsExtra
NULL, \ ;cbWndExtra
NULL, \ ;hInstance
NULL, \ ;hIcon
NULL, \ ;hCursor
NULL, \ ;hbrBackground
NULL, \ ;lpszMenuName
NULL, \ ;lpszClassName
NULL > ;hIconSm


GDIinputStruct GdiplusStartupInput <1, NULL, FALSE, 0>

Xscale REAL4 2.0
Yscale REAL4 2.0

MainClassName DB "JPEGtest", 0
MainTitleText DB "JPEG Testbed", 0

; Name is in Unicode:
JPEGfilename DB 't', 0, 'e', 0, 's', 0, 't', 0, '.', 0, 'j', 0, 'p', 0, 'g', 0, 0, 0


;============================================
; UNINITIALIZED DATA
;============================================
.data?

MainWinHandle HWND ?
GDItoken DD ?


;============================================
; CODE LIVES HERE
;============================================
.code


start: INVOKE GetModuleHandle, NULL
MOV WC.hInstance, EAX

INVOKE WinMain, EAX
INVOKE ExitProcess, EAX


;====================================================================
; Mainline proc
;====================================================================

WinMain PROC hInst:DWORD
LOCAL msg:MSG, brush:HBRUSH, wX:DWORD, wY:DWORD, gpRect:RECT


; Create  brush to set background color:
INVOKE CreateSolidBrush, $BkColor
MOV brush, EAX

; Register class for parent window:
MOV WC.lpfnWndProc, OFFSET MainWindowProc
MOV EAX, brush
MOV WC.hbrBackground, EAX
MOV WC.lpszClassName, OFFSET MainClassName
MOV WC.hIcon, NULL
INVOKE LoadCursor, NULL, IDC_ARROW
MOV WC.hCursor, EAX
INVOKE RegisterClassEx, OFFSET WC

INVOKE GetSystemMetrics, SM_CXSCREEN
MOV EDX, $mainWinWidth
CALL CenterDim
MOV wX, EAX
INVOKE GetSystemMetrics, SM_CYSCREEN
MOV EDX, $mainWinHeight
CALL CenterDim
MOV wY, EAX

; Create our main window:
INVOKE CreateWindowEx, WS_EX_OVERLAPPEDWINDOW, OFFSET MainClassName,
OFFSET MainTitleText, $mainWinStyles, wX, wY, $mainWinWidth,
$mainWinHeight, NULL, NULL, hInst, NULL
MOV MainWinHandle, EAX

;********  Initialize GDI+:  ********
INVOKE GdiplusStartup, OFFSET GDItoken, OFFSET GDIinputStruct, NULL


;============= Message loop ===================
msgloop:
INVOKE GetMessage, ADDR msg, NULL, 0, 0
OR EAX, EAX ;EAX = 0 = exit
JZ exit99
INVOKE TranslateMessage, ADDR msg
INVOKE DispatchMessage, ADDR msg
JMP msgloop

exit99: INVOKE GdiplusShutdown, GDItoken
MOV EAX, msg.wParam
RET

WinMain ENDP


;====================================================================
; Main Window Proc
;====================================================================

MainWindowProc PROC hWin:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
LOCAL hDC:HDC, gdiHgraphics:DWORD, gdiHbitmap:DWORD
LOCAL ps:PAINTSTRUCT

MOV EAX, uMsg
CMP EAX, WM_PAINT
JE do_paint
CMP EAX, WM_CLOSE
JE do_close

dodefault:
; use DefWindowProc for all other messages:
INVOKE DefWindowProc, hWin, uMsg, wParam, lParam
RET

do_paint:
INVOKE BeginPaint, hWin, ADDR ps
MOV hDC, EAX

; Get graphics "object" from DC handle:
INVOKE GdipCreateFromHDC, hDC, ADDR gdiHgraphics

; Open JPEG file:
INVOKE GdipLoadImageFromFile, OFFSET JPEGfilename, ADDR gdiHbitmap

; Set scale:
INVOKE GdipScaleWorldTransform, gdiHgraphics, Xscale, Yscale, 0

; Display image:
INVOKE GdipDrawImageI, gdiHgraphics, gdiHbitmap, 50, 50

INVOKE EndPaint, hWin, ADDR ps
XOR EAX, EAX
RET


do_close:
INVOKE PostQuitMessage, NULL
MOV EAX, TRUE
RET


MainWindowProc ENDP


;====================================================================
; CenterDim
;
; Returns half screen dimension (X or Y) minus half window dimension
;
; On entry,
; EAX = screen dimension
; EDX = window dimension
;
; Returns:
; sdim/2 - wdim/2
;====================================================================

CenterDim PROC

SHR EAX, 1 ;divide screen dimension by 2
SHR EDX, 1 ;divide window dimension by 2
SUB EAX, EDX
RET ;Return w/# in EAX

CenterDim ENDP


END start

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

hutch--

You look like you are doing OK, for the 32 bit FLOAT values, you can prototype them as REAL4.

jj2007

Quote from: NoCforMe on December 07, 2018, 03:03:03 PMQuestion: How do I deal with 32-bit floats?

There is a sophisticated SetFloat macro in MB, but in general this is easy to achieve:
invoke gdisomething, bla, bla, FP4(123.456)  ; if your float is a constant

For converting an integer to a REAL4, you can use one of these:
push eax  ; the integer is in eax, so we use the stack
fild dword ptr [esp]
pop edx
fstp MyReal4


fild MyDword  ; direct conversion
fstp MyReal4

aw27

#3
I don't know what you want, hopefully you do, but you can use SetWorldTransform to produce, hummm..., the transformations you want.
For example, for a 15º rotation with 2x scale in both axis, 100 pixels X axis translation, and -100 pixels Y axis translation, you feed the XFORM structure with this:
   mov eax, FP4(2.0) ; scale horizontal 2x
   mov xform.eM11, eax
   mov eax, FP4(0.259) ; sin 15º
   mov xform.eM12, eax
   mov eax, FP4(-0.259) ; -sin 15º
   mov xform.eM21, eax
   mov eax, FP4(2.0) ; scale vertical 2x
   mov xform.eM22, eax
   mov eax, FP4(100.0) ; Translation X axis
   mov xform.ex, eax
   mov eax, FP4(-100.0) ; Translation Y axis
   mov xform.ey, eax
   invoke SetWorldTransform, hDC, addr xform



* Edited because past values not meaningful

aw27

#4
Another way to do it:

doXFM macro a,b,c,d,e,f
   local myaddr
   .data
   myaddr XFORM <a,b,c,d,e,f>
   .code
   exitm <myaddr>
endm   

invoke SetWorldTransform, hDC, addr doXFM (2.0,0.259,-0.259,2.0,100.0,-100.0)


or even better, you could simply declare it in the data section:
myaddr2 XFORM <2.0,0.259,-0.259,2.0,100.0,-100.0>
then in the code section
invoke SetWorldTransform, hDC, addr myaddr2

If you don't know at assemble time what the final float/real4 values will be you must use either coprocessor or vector instructions.

* Edited because past values not meaningful

TimoVJL

Why this is in paint routine?; Open JPEG file:
INVOKE GdipLoadImageFromFile, OFFSET JPEGfilename, ADDR gdiHbitmap
May the source be with you

NoCforMe

Quote from: TimoVJL on December 08, 2018, 12:39:59 AM
Why this is in paint routine?; Open JPEG file:
INVOKE GdipLoadImageFromFile, OFFSET JPEGfilename, ADDR gdiHbitmap

Just the way I decided to get a quick-and-dirty testbed up and running. Normally wouldn't do things that way, but it saved the trouble of shuttling values back and forth between the paint handler and other parts of the code.
Assembly language programming should be fun. That's why I do it.

NoCforMe

More progress: I can now save a JPEG image from one in memory, using GdipSaveImageToFile(). (Yeah, I know, everything's in the paint handler where it doesn't belong, but it's just a testbed.) I had to figure out how to deal with the CLSID corresponding to the codec I wanted to use (JPEG); that's handled by the routine GetCodecCLSID(), which takes a MIME type (like "image/jpeg") and returns the corresponding CLSID.

What gets saved is the original image at the original size, not the reduced-size image displayed. I'm not sure how to manipulate the image to resize it. In fact, I'm not sure whether one wants to manipulate the image through GDI+, using matrices, etc., or whether it can be done through plain old GDI (through an HDC), using StretchBlt() or some such.

I'm going to read and digest the replies above. Thanks to all who contributed! I've still got questions about floating-point; back to the lab ...


;============================================
; GDI+ Testbed
;
;============================================


include \masm32\include\masm32rt.inc
include \masm32\include\gdiplus.inc
includelib \masm32\lib\gdiplus.lib


;============================================
; Defines, macros, prototypes, etc.
;============================================

; Define this to enable logging:
;$logging EQU 1

WinMain PROTO :DWORD
GetCodecCLSID PROTO mimeTypePtr:DWORD, clsidPtr:DWORD
strcmpW PROTO compare1:DWORD, compare2:DWORD
LogEncoder PROTO encoderPtr:DWORD
writeLogFile PROTO bufferPtr:DWORD, bufferLen:DWORD

$mainWinWidth EQU 600
$mainWinHeight EQU 500

;===== Window styles: =====
$mainWinStyles EQU WS_OVERLAPPEDWINDOW OR WS_CLIPCHILDREN OR WS_VISIBLE

;===== Window background colors: =====
$bkRED EQU 254
$bkGRN EQU 243
$bkBLUE EQU 199

$BkColor EQU $bkRED OR ($bkGRN SHL 8) OR ($bkBLUE SHL 16)

Ok EQU 0 ;GDI+ OK status value.

$CRLF EQU <13, 10>
$tab EQU 9

$$Unicode MACRO name, string
&name LABEL BYTE
FORC char, string
DB '&char', 0
ENDM
DB 0, 0
ENDM


;============================================
; HERE BE DATA
;============================================
.data

WC WNDCLASSEX < SIZEOF WNDCLASSEX, \
CS_HREDRAW or CS_VREDRAW or CS_BYTEALIGNWINDOW, \
NULL, \ ;lpfnWndProc
NULL, \ ;cbClsExtra
NULL, \ ;cbWndExtra
NULL, \ ;hInstance
NULL, \ ;hIcon
NULL, \ ;hCursor
NULL, \ ;hbrBackground
NULL, \ ;lpszMenuName
NULL, \ ;lpszClassName
NULL > ;hIconSm


GDIinputStruct GdiplusStartupInput <1, NULL, FALSE, 0>

Xscale REAL4 2.0
Yscale REAL4 2.0

MainClassName DB "JPEGtest", 0
MainTitleText DB "JPEG Testbed", 0

;***** Names in Unicode: *****
$$Unicode JPEGfilenameW, test.jpg
$$Unicode SaveFilenameW, save.jpg
$$Unicode MIMEtypeStrW, image/jpeg

IFDEF $logging
LogFilename DB "GDIplusTest.log", 0
ENDIF

IFDEF $logging
;***** wsprintf() format strings: *****
Enc_CodecNameFmt DB "Codec name: %ls", $CRLF, 0
Enc_DllNameFmt DB $tab, "DLL name: %ls", $CRLF, 0
Enc_FormatDescriptionFmt DB $tab, "Format description: %ls", $CRLF, 0
Enc_FilenameExtensionFmt DB $tab, "File extension: %ls", $CRLF, 0
Enc_MimeTypeFmt DB $tab, "MIME type: %ls", $CRLF, 0
Enc_ClassIDFmt DB $tab, "Class ID: %08X-%04X-%04X-%08X", $CRLF, 0
ENDIF

;***** Error messages: *****
ErrMsgNoSize DB "Couldn't get encoders size.", 0
ErrMsgNoEncoder DB "Couldn't get encoder CLSID.", 0


;============================================
; UNINITIALIZED DATA
;============================================
.data?

MainWinHandle HWND ?
LogFileHandle HFILE ?
GDItoken DD ?


;============================================
; CODE LIVES HERE
;============================================
.code


start: INVOKE GetModuleHandle, NULL
MOV WC.hInstance, EAX

INVOKE WinMain, EAX
INVOKE ExitProcess, EAX


;====================================================================
; Mainline proc
;====================================================================

WinMain PROC hInst:DWORD
LOCAL msg:MSG, brush:HBRUSH, wX:DWORD, wY:DWORD, gpRect:RECT
LOCAL numEncoders:DWORD, sizeEncoder:DWORD, sizeEncoders:DWORD
LOCAL heapHandle:HANDLE, encoderHeap:HANDLE


; Create  brush to set background color:
INVOKE CreateSolidBrush, $BkColor
MOV brush, EAX

; Register class for parent window:
MOV WC.lpfnWndProc, OFFSET MainWindowProc
MOV EAX, brush
MOV WC.hbrBackground, EAX
MOV WC.lpszClassName, OFFSET MainClassName
MOV WC.hIcon, NULL
INVOKE LoadCursor, NULL, IDC_ARROW
MOV WC.hCursor, EAX
INVOKE RegisterClassEx, OFFSET WC

INVOKE GetSystemMetrics, SM_CXSCREEN
MOV EDX, $mainWinWidth
CALL CenterDim
MOV wX, EAX
INVOKE GetSystemMetrics, SM_CYSCREEN
MOV EDX, $mainWinHeight
CALL CenterDim
MOV wY, EAX

; Create our main window:
INVOKE CreateWindowEx, WS_EX_OVERLAPPEDWINDOW, OFFSET MainClassName,
OFFSET MainTitleText, $mainWinStyles, wX, wY, $mainWinWidth,
$mainWinHeight, NULL, NULL, hInst, NULL
MOV MainWinHandle, EAX

;********  Initialize GDI+:  ********
INVOKE GdiplusStartup, OFFSET GDItoken, OFFSET GDIinputStruct, NULL

IFDEF $logging
INVOKE CreateFile, OFFSET LogFilename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL
MOV LogFileHandle, EAX

; Let's see what the encoder list looks like:
INVOKE GdipGetImageEncodersSize, ADDR numEncoders, ADDR sizeEncoder
INVOKE GetProcessHeap
MOV heapHandle, EAX
INVOKE HeapAlloc, EAX, 0, sizeEncoder
MOV encoderHeap, EAX
INVOKE GdipGetImageEncoders, numEncoders, sizeEncoder, encoderHeap

MOV EDX, encoderHeap
MOV ECX, numEncoders
eloop: PUSH ECX
PUSH EDX
INVOKE LogEncoder, EDX
POP EDX
POP ECX
ADD EDX, SIZEOF ImageCodecInfo
LOOP eloop

INVOKE HeapFree, heapHandle, 0, encoderHeap
INVOKE CloseHandle, LogFileHandle
ENDIF

;============= Message loop ===================
msgloop:
INVOKE GetMessage, ADDR msg, NULL, 0, 0
OR EAX, EAX ;EAX = 0 = exit
JZ exit99
INVOKE TranslateMessage, ADDR msg
INVOKE DispatchMessage, ADDR msg
JMP msgloop

exit99: INVOKE GdiplusShutdown, GDItoken
MOV EAX, msg.wParam
RET

WinMain ENDP


;====================================================================
; Main Window Proc
;====================================================================

MainWindowProc PROC hWin:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
LOCAL hDC:HDC, gdiHgraphics:DWORD, gdiHbitmap:DWORD
LOCAL ps:PAINTSTRUCT, clsid:CLSID

MOV EAX, uMsg
CMP EAX, WM_PAINT
JE do_paint
CMP EAX, WM_CLOSE
JE do_close

dodefault:
; use DefWindowProc for all other messages:
INVOKE DefWindowProc, hWin, uMsg, wParam, lParam
RET

do_paint:
INVOKE BeginPaint, hWin, ADDR ps
MOV hDC, EAX

; Get graphics "object" from DC handle:
INVOKE GdipCreateFromHDC, hDC, ADDR gdiHgraphics

; Open JPEG file:
INVOKE GdipLoadImageFromFile, OFFSET JPEGfilenameW, ADDR gdiHbitmap

; Set scale:
; INVOKE GdipScaleWorldTransform, gdiHgraphics, Xscale, Yscale, 0

; Display image:
INVOKE GdipDrawImageI, gdiHgraphics, gdiHbitmap, 50, 50

; Save image to another file:
INVOKE GetCodecCLSID, OFFSET MIMEtypeStrW, ADDR clsid
CMP EAX, TRUE
JE save
INVOKE MessageBox, NULL, OFFSET ErrMsgNoEncoder, NULL, MB_OK
JMP SHORT skip

save: INVOKE GdipSaveImageToFile, gdiHbitmap, OFFSET SaveFilenameW, ADDR clsid, NULL

skip: INVOKE EndPaint, hWin, ADDR ps
XOR EAX, EAX
RET


do_close:
INVOKE PostQuitMessage, NULL
MOV EAX, TRUE
RET


MainWindowProc ENDP


;====================================================================
; GetCodecCLSID (mimeTypePtr, clsidPtr)
;
; Given a MIME type string to select a certain codec, returns that
; codec's CLSID data to a location specified by clsidPtr.
;
; Returns TRUE if successful, FALSE if an error occurred.
;====================================================================

GetCodecCLSID PROC mimeTypePtr:DWORD, clsidPtr:DWORD
LOCAL result:DWORD, numEncoders:DWORD, sizeEncoder:DWORD
LOCAL heapHandle:HANDLE, encoderHeap:HANDLE

MOV result, FALSE ;Default = error.
INVOKE GdipGetImageEncodersSize, ADDR numEncoders, ADDR sizeEncoder
CMP EAX, Ok
JNE rexit ;Error, exit.

INVOKE GetProcessHeap
MOV heapHandle, EAX
INVOKE HeapAlloc, EAX, 0, sizeEncoder
; todo: error checking

MOV encoderHeap, EAX
INVOKE GdipGetImageEncoders, numEncoders, sizeEncoder, encoderHeap
CMP EAX, Ok
JNE done ;Error, free mem. & exit.

MOV EDX, encoderHeap
MOV ECX, numEncoders
eloop: PUSH ECX
PUSH EDX
INVOKE strcmpW, mimeTypePtr, [EDX].ImageCodecInfo.MimeType
POP EDX
POP ECX
CMP EAX, TRUE
JNE next

; Found match: copy CLSID to caller's pointer:
MOV ECX, SIZEOF CLSID
PUSH ESI
PUSH EDI
LEA ESI, [EDX].ImageCodecInfo.ClassID
MOV EDI, clsidPtr
REP MOVSB
POP EDI
POP ESI
MOV result, TRUE
JMP SHORT done

next: ADD EDX, SIZEOF ImageCodecInfo
LOOP eloop

done: INVOKE HeapFree, heapHandle, 0, encoderHeap
rexit: MOV EAX, result
RET


GetCodecCLSID ENDP


;====================================================================
; strcmpW (compare1, compare2)
;
; Unicode string comparer.
; Returns TRUE if strings match, FALSE otherwise.
;====================================================================

strcmpW PROC compare1:DWORD, compare2:DWORD

MOV ECX, compare1
MOV EDX, compare2

cloop: MOV AX, [ECX]
OR AX, AX ;Hit the double 0?
JZ maybe ;  Yep, see if other string matches.
CMP AX, [EDX]
JNE fail
ADD ECX, 2
ADD EDX, 2
JMP cloop

maybe: CMP WORD PTR [EDX], NULL ;Need a double 0 on other one.
JNE fail
MOV EAX, TRUE
RET

fail: XOR EAX, EAX
RET


strcmpW ENDP


IFDEF $logging
;====================================================================
; LogEncoder (encoderPtr)
;
; Writes all known stuff about a GDI+ encoder to a log file.
;====================================================================

LogEncoder PROC encoderPtr:DWORD
LOCAL dummy:DWORD, buffer[512]:BYTE

PUSH EBX
MOV EBX, encoderPtr

INVOKE wsprintf, ADDR buffer, OFFSET Enc_CodecNameFmt, [EBX].ImageCodecInfo.CodecName
LEA EAX, buffer
CALL strlen
MOV EDX, EAX
INVOKE WriteFile, LogFileHandle, ADDR buffer, EDX, ADDR dummy, NULL

INVOKE wsprintf, ADDR buffer, OFFSET Enc_DllNameFmt, [EBX].ImageCodecInfo.DllName
LEA EAX, buffer
CALL strlen
MOV EDX, EAX
INVOKE WriteFile, LogFileHandle, ADDR buffer, EDX, ADDR dummy, NULL

INVOKE wsprintf, ADDR buffer, OFFSET Enc_FormatDescriptionFmt, [EBX].ImageCodecInfo.FormatDescription
LEA EAX, buffer
CALL strlen
MOV EDX, EAX
INVOKE WriteFile, LogFileHandle, ADDR buffer, EDX, ADDR dummy, NULL

INVOKE wsprintf, ADDR buffer, OFFSET Enc_FilenameExtensionFmt, [EBX].ImageCodecInfo.FilenameExtension
LEA EAX, buffer
CALL strlen
MOV EDX, EAX
INVOKE WriteFile, LogFileHandle, ADDR buffer, EDX, ADDR dummy, NULL

INVOKE wsprintf, ADDR buffer, OFFSET Enc_MimeTypeFmt, [EBX].ImageCodecInfo.MimeType
LEA EAX, buffer
CALL strlen
MOV EDX, EAX
INVOKE WriteFile, LogFileHandle, ADDR buffer, EDX, ADDR dummy, NULL

INVOKE wsprintf, ADDR buffer, OFFSET Enc_ClassIDFmt,
[EBX].ImageCodecInfo.ClassID.Data1,
[EBX].ImageCodecInfo.ClassID.Data2,
[EBX].ImageCodecInfo.ClassID.Data3,
[EBX].ImageCodecInfo.ClassID.Data4
LEA EAX, buffer
CALL strlen
MOV EDX, EAX
INVOKE WriteFile, LogFileHandle, ADDR buffer, EDX, ADDR dummy, NULL

POP EBX
RET


LogEncoder ENDP
ENDIF


;====================================================================
; strlen()
;
; On entry,
; EAX--> string to be measured
; On exit,
; EAX = length of string
;====================================================================

strlen PROC

XOR EDX, EDX ;Len. counter
sl10: CMP BYTE PTR [EAX + EDX], NULL
JE sl88
INC EDX
JMP sl10
sl88: MOV EAX, EDX
RET

strlen ENDP


;====================================================================
; CenterDim
;
; Returns half screen dimension (X or Y) minus half window dimension
;
; On entry,
; EAX = screen dimension
; EDX = window dimension
;
; Returns:
; sdim/2 - wdim/2
;====================================================================

CenterDim PROC

SHR EAX, 1 ;divide screen dimension by 2
SHR EDX, 1 ;divide window dimension by 2
SUB EAX, EDX
RET ;Return w/# in EAX

CenterDim ENDP


END start

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

jj2007

Quote from: NoCforMe on December 09, 2018, 03:20:21 PMI've still got questions about floating-point

Just ask them ;)

Compliments, you have made huge progress; don't forget the cleaning up (check for memleaks, move stuff to the right places, ...) :t

aw27

I have edited the values and image to provide meaningful values.
For complex transformations we can combine partial transformations using CombineTransform.
And I am done with this thread.

NoCforMe

Quote from: AW on December 10, 2018, 01:59:43 AM
I have edited the values and image to provide meaningful values.
For complex transformations we can combine partial transformations using CombineTransform.
And I am done with this thread.
Well, since your edit to your previous posting was helpful, you're excused.
Assembly language programming should be fun. That's why I do it.