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
You look like you are doing OK, for the 32 bit FLOAT values, you can prototype them as REAL4.
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
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
(https://www.dropbox.com/s/ipq6vom5frux8k2/gdi3.png?dl=1)
* Edited because past values not meaningful
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
Why this is in paint routine?; Open JPEG file:
INVOKE GdipLoadImageFromFile, OFFSET JPEGfilename, ADDR gdiHbitmap
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.
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
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
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.
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.