General > The Workshop

Rotated text

(1/2) > >>

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 ...


--- Quote from: NoCforMe on October 15, 2022, 07:47: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).
--- End quote ---

That looks equally ugly, unfortunately :sad:

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.)

Hi NoCforMe!
Win64 Tutorial #5a: Painting with Rotation Text
tiny theory in Russian

--- Code: ---; GUI #
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 newFont:qword
local oldFont:qword
local x1:dword
local y1:dword
local hDC:qword
local lpPoint:POINT
 mov hwnd,rcx
 cmp edx,WM_DESTROY
 cmp edx,WM_PAINT
 je wmPAINT
 cmp edx,WM_SIZE
 je wmSIZE
 cmp edx,WM_TIMER
 je wmTIMER
 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
 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 rbx ;DEFAULT_QUALITY=0
 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
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 code ---

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.


[0] Message Index

[#] Next page

Go to full version