News:

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

Main Menu

Rotated text

Started by NoCforMe, October 15, 2022, 07:47:50 AM

Previous topic - Next topic

NoCforMe

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 ...
Assembly language programming should be fun. That's why I do it.

jj2007

Quote from: NoCforMe on October 15, 2022, 07:47:50 AMThe 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:

NoCforMe

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.)
Assembly language programming should be fun. That's why I do it.

Mikl__

Hi NoCforMe!
Win64 Tutorial #5a: Painting with Rotation Text

tiny theory in Russian

; 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




tut06.zip

NoCforMe

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.
Assembly language programming should be fun. That's why I do it.

jj2007

Quote from: NoCforMe on October 15, 2022, 07:47:50 AMThere'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.



SOF:
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:
QuoteGDI+ 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.