The MASM Forum

General => The Workshop => Topic started by: NoCforMe on October 15, 2022, 07:47:50 AM

Title: Rotated text
Post by: NoCforMe on October 15, 2022, 07:47:50 AM
Ever since I had to rotate some text at 90° I've been playing with it, trying to figure it out. Here's a screenshot of a little testbed I put together. I was surprised when I saw that the two sets of text shared a common axis, even though I gave them separate positions, one through a RECT, the other just by X/Y positioning. (The first set is drawn using TextOut(), the second using DrawText(). Both of them look, to use a phrase from someone I used to work for, like hammered dogshit.)

To do the rotation I used SetWorldTransform() with an XFORM matrix (4 members of which are for rotation: cosine, sine, -sine and cosine, plus 2 members for translation which are zero). It don't work so good (intentionally bad English there). The other way to rotate text, which I'll play with next, is to create a rotated font (with both lfEscapement and lfOrientation set to some non-zero value). That may yield better results. (Petzold uses that method in one of his example programs, and the output looks OK.)

Since the text looks so horrible, I don't think I'll be using this method at all. I'm just curious about it. I wonder how exactly one would go about changing the axis of rotation. I tried using SetWindowOrgEx() and SetViewportOrgEx() to change the origin, thinking that would do the trick, but they had no effect.

This is all GDI stuff, remember. There's always GDI+, I guess ...
Title: Re: Rotated text
Post by: jj2007 on October 15, 2022, 09:33:50 AM
The other way to rotate text, which I'll play with next, is to create a rotated font (with both lfEscapement and lfOrientation set to some non-zero value).

That looks equally ugly, unfortunately :sad:
Title: Re: Rotated text
Post by: NoCforMe on October 15, 2022, 11:06:36 AM
So overall it's quite an imperfect technology.

Heh; might be better to render the text as graphics and then rotate it ...

(The multiples of 30° seem to look the least bad.)
Title: Re: Rotated text
Post by: Mikl__ on October 23, 2022, 10:42:12 AM
Hi NoCforMe!
Win64 Tutorial #5a: Painting with Rotation Text

tiny theory in Russian (https://wasm.in/threads/skazki-djadjushki-rimusa-o-x64.31832/#post-383765)

Code: [Select]
; GUI #
include win64a.inc
.code
WinMain proc
local msg:MSG
 
 xor ebx,ebx
 mov esi,IMAGE_BASE
 mov edi,offset ClassName
 mov ecx,offset FileName
 invoke LoadCursorFromFile
 mov r12,rax
 mov ecx,0FF0000h
 invoke CreateSolidBrush
 push r12 ;hIconSm
 push rdi ;lpszClassName
 push rbx ;lpszMenuName
 push rax ;hbrBackground
 push 10003h ;hCursor
 push r12 ;hIcon
 push rsi ;hInstance
 push rbx ;cbClsExtra & cbWndExtra
 pushaddr WndProc ;lpfnWndProc
 push sizeof WNDCLASSEX;cbSize & style
 invoke RegisterClassEx,esp ;addr WNDCLASSEX
 push rbx
 push rsi ;rsi=400000h
 shr esi,7 ;Special CreateWindow position value CW_USEDEFAULT=8000h
 push rbx
 push rbx
 push rsi
 push rsi
 push rsi
 push rsi
 sub esp,20h
 invoke CreateWindowEx,0,edi,edi,WS_OVERLAPPEDWINDOW or WS_VISIBLE
;создаем таймер #0 на 50mSec
 invoke SetTimer,eax,0,50,0
; +---------------------------+
; | entering the message loop |
; +---------------------------+
 lea edi,msg
@@: invoke GetMessage,edi,0,0,0
 invoke DispatchMessage,edi
 jmp @b
WinMain endp
WndProc proc hwnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
local ps:PAINTSTRUCT
local newFont:qword
local oldFont:qword
local x1:dword
local y1:dword
local hDC:qword
local lpPoint:POINT
 
 mov hwnd,rcx
 cmp edx,WM_DESTROY
 je wmDESTROY
 cmp edx,WM_PAINT
 je wmPAINT
 cmp edx,WM_SIZE
 je wmSIZE
 cmp edx,WM_TIMER
 je wmTIMER
 leave
 jmp NtdllDefWindowProc_
wmDESTROY:invoke KillTimer,,0;уничтожаем таймер #0
 invoke RtlExitUserProcess,0
wmSIZE: mov edx,offset expRect
 invoke GetClientRect ;получаем размеры клиентской области.
;Размеры возвращаются в переменную expRect
 mov eax,expRect.bottom
 shr eax,1
 mov y,eax ;y-координата середины экрана
 mov eax,expRect.right
 shr eax,1
 mov x,eax ;x-координата середины экрана
 jmp wmBYE
wmTIMER:fninit
 fld angle2;угол наклона строки в радианах
 fadd pi_mul_1_6_div_180 ;увеличиваем угол наклона строки на 1,6 градуса
 fst angle2 fmul _1800_div_pi ;переводим радианы в градусы
 fsub _1800 ;угол наклона букв в строке отстает от угла наклона строки на 90 градусов
 fld st(0)
 fmul one_div_3600
 fsubp st(1),st(0)
 fistp angle ;делим угол наклона букв на 360 градусов и запоминаем остаток
 invoke InvalidateRect,,0,TRUE;перерисовываем текст с текущим значением угла
 jmp wmBYE
wmPAINT:lea edx,ps
 invoke BeginPaint
 mov hDC,rax
 invoke SetTextColor,hDC,32C8C8h;RGB=50,200,200 золотистые буквы
 invoke SetBkColor,hDC,0FF0000h;RGB=0,0,255 на синем фоне
;создаем шрифт с углом вращения, указанным в lfEscapement и lfOrientation
 pushaddr expFont
 push DEFAULT_PITCH or FF_SCRIPT
 push rbx ;DEFAULT_QUALITY=0
 push rbx ;CLIP_DEFAULT_PRECIS=0
 push rbx ;OUT_DEFAULT_PRECIS=0
 push OEM_CHARSET
 push rbx
 push rbx
 push rbx
 push 400
 sub esp,20h
 invoke CreateFont,26,12,angle,r8
 mov newFont,rax
 invoke SelectObject,hDC,eax
 mov oldFont,rax
;---------вывожу текст
 mov qword ptr [rsp+20h],sizeof expTxt;длина строки
 mov r9d,offset expTxt ;адрес строки
;---------рассчитываю положение начала текста
 fld angle2;угол наклона строки в радианах
 fsincos ;в st(0) синус угла, в st(1) косинус
 mov y1,227 ;половина гипотенузы
 fimul y1 ;гипотенуза * sin = x
 fistp x1
 mov edx,x
 add edx,x1
 fimul y1 ;гипотенуза * cos = y
 fistp y1 ;-y
 mov r8d,y
 sub r8d,y1
 invoke TextOut,hDC
 invoke DeleteObject,newFont ;delete the new font
 invoke SelectObject,hDC,oldFont;return the old font to the system
 invoke EndPaint,hwnd,&ps
wmBYE:leave
 retn
WndProc endp
;---------------------------------------
ClassName db "Uncle Remus tales:#5 Painting with Rotation Text",0
expTxt db "Win64 assembly with MASM is great and easy!",0
expFont db "script",0
angle dq 0
angle2 dq 3.14159265358979323846264338328
pi_mul_1_6_div_180 dq 0.02792526803190927323077905229;pi*1,6/180
_1800_div_pi dq 572.957795130823208767981548141;1800/pi
_1800 dq 1800.0
one_div_3600 dq 0.00027777777777777777777777778;1/3600 ;
hIcon dq ?
FileName db "br_Rabbit3.cur",0
expRect RECT <>
x dd ?
y dd ?
end

(https://wasm.in/attachments/image01-png.4713/)

tut06.zip (https://wasm.in/attachments/tut_06-zip.344/)
Title: Re: Rotated text
Post by: NoCforMe on October 23, 2022, 02:13:54 PM
Thanks for that; however, I can't tell: does your text look better than mine? Hard to tell because of the script font you used. It seems to be smoother.
Title: Re: Rotated text
Post by: jj2007 on October 23, 2022, 07:23:20 PM
There's always GDI+, I guess ...

I've always avoided GdipDrawString & friends (there are roughly 100 font-related Gdi+ functions), for two reasons:
1. It's programmatically challenging
2. I doubt it helps, since the normal Gdi functions use the available fonts and do anti-aliasing quite well.

(https://i.stack.imgur.com/bRcps.png)

SOF: (https://stackoverflow.com/questions/7268238/drawing-text-in-net)
Quote
  • The first string is a native Label with the FlatStyle set to System
  • The second string is drawn using Graphics.DrawString() method
  • The last one is drawn using TextRenderer.DrawText() method
All cases use the default Windows Vista/7 font: Segoe UI, 9

As you can see, there is a difference between the second string and the others (it has less quality, and the anti alias is different). I have tried to configure anti-alias and the smoothing mode in the Graphics object, without any result.

Answer by Hans Passant:
Quote
GDI+ was Microsoft's first attempt at rendering resolution independent text. And the only way to render text in .NET 1.x. It got widely panned for its quality issues, inspiring the introduction of TextRenderer and Application.SetCompatibleTextRenderingDefault() in .NET 2.0. It uses GDI for drawing text, effectively solving the problems. You should only use Graphics.DrawString() on high resolution devices. Printers.