MS Paint: rotate by scaling/shearing (stretching/skewing)

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

MS Paint: rotate by scaling/shearing (stretching/skewing)

12 May 2019, 12:52

Here is a script to rotate by an arbitrary angle in MS Paint.

It is based on this video by IronMortality:
Rotate an acute angle in MS Paint - YouTube
https://www.youtube.com/watch?v=wUKMIi0mInc

The script has been tested on MS Paint (Windows XP and Windows 7 versions).

Code: Select all

;==================================================

;MS Paint: rotate by scaling/shearing (stretching/skewing)
;tested on MS Paint (Windows XP version)
;tested on MS Paint (Windows 7 version)

;2 approaches are available, use q or w to trigger

;==================================================

;NOTES: DEFINITION

;Transformation matrix - Wikipedia
;https://en.wikipedia.org/wiki/Transformation_matrix#Examples_in_2D_computer_graphics
;Rotation matrix - Wikipedia
;https://en.wikipedia.org/wiki/Rotation_matrix

;anticlockwise rotation:
;R = [cos(x), -sin(x)]
;    [sin(x),  cos(x)]

;clockwise rotation:
;R = [ cos(x), sin(x)]
;    [-sin(x), cos(x)]

;==================================================

;NOTES: APPROACH 1

;approach 1 based on:
;[demonstrated in video]
;Rotate an acute angle in MS Paint - YouTube
;https://www.youtube.com/watch?v=wUKMIi0mInc
;IronMortality's Blog: MS Paint Rotate Notes
;http://ironmortal.blogspot.co.uk/2015/05/ms-paint-rotate-notes.html
;Step 1: Horizontal skew by 60 degrees
;Step 2: Vertical stretch by 400%
;Step 3: Vertical skew by -60 degrees
;Step 4: Horizontal and vertical stretch by 50%

;based on calculations by IronMortality:
;(note: matrix for clockwise rotation)
;R = [1, tan(x)][1/cos^2(x), 0][   1   , 0][cos(x),   0   ]
;    [0,   1   ][    0     , 1][-tan(x), 1][  0   , cos(x)]
;R = [ cos(x), sin(x)]
;    [-sin(x), cos(x)]

;simplify {{1,tan(x)},{0,1}} . {{1/cos^2(x),0},{0,1}} . {{1,0},{-tan(x),1}} . {{cos(x),0},{0,cos(x)}} - Wolfram|Alpha
;https://www.wolframalpha.com/input/?i=simplify+%7B%7B1,tan(x)%7D,%7B0,1%7D%7D+.+%7B%7B1%2Fcos%5E2(x),0%7D,%7B0,1%7D%7D+.+%7B%7B1,0%7D,%7B-tan(x),1%7D%7D+.+%7B%7Bcos(x),0%7D,%7B0,cos(x)%7D%7D
;does {{1,tan(x)},{0,1}} . {{1/cos^2(x),0},{0,1}} . {{1,0},{-tan(x),1}} . {{cos(x),0},{0,cos(x)}} = {{cos(x),sin(x)},{-sin(x),cos(x)}} - Wolfram|Alpha
;https://www.wolframalpha.com/input/?i=does+%7B%7B1,tan(x)%7D,%7B0,1%7D%7D+.+%7B%7B1%2Fcos%5E2(x),0%7D,%7B0,1%7D%7D+.+%7B%7B1,0%7D,%7B-tan(x),1%7D%7D+.+%7B%7Bcos(x),0%7D,%7B0,cos(x)%7D%7D+%3D+%7B%7Bcos(x),sin(x)%7D,%7B-sin(x),cos(x)%7D%7D

;==================================================

;NOTES: APPROACH 2

;approach 2 based on:
;[mentioned in video description]
;Rotate an acute angle in MS Paint - YouTube
;https://www.youtube.com/watch?v=wUKMIi0mInc
;Rotation matrix - Wikipedia
;https://en.wikipedia.org/wiki/Rotation_matrix#Decomposition_into_shears
;Rotation by Shearing
;http://www.ocf.berkeley.edu/~fricke/projects/israel/paeth/rotation_by_shearing.html
;(note: original text used theta, Chr(952), not 'x')
;Step 1: Horizontal skew by x/2
;Step 2: Vertical skew by -arctan[sin[x]]
;Step 3: Horizontal skew by x/2

;based on Wikipedia 'Rotation matrix' page:
;(note: matrix for anticlockwise rotation)
;decomposition into shears:
;R = [1, -tan(x/2)][  1   , 0][1, -tan(x/2)]
;    [0,     1    ][sin(x), 1][0,     1    ]
;R = [cos(x), -sin(x)]
;    [sin(x),  cos(x)]

;simplify {{1,-tan(x/2)},{0,1}} . {{1,0},{sin(x),1}} . {{1,-tan(x/2)},{0,1}} - Wolfram|Alpha
;https://www.wolframalpha.com/input/?i=simplify+%7B%7B1,-tan(x%2F2)%7D,%7B0,1%7D%7D+.+%7B%7B1,0%7D,%7Bsin(x),1%7D%7D+.+%7B%7B1,-tan(x%2F2)%7D,%7B0,1%7D%7D
;does {{1,-tan(x/2)},{0,1}} . {{1,0},{sin(x),1}} . {{1,-tan(x/2)},{0,1}} = {{cos(x),-sin(x)},{sin(x),cos(x)}} - Wolfram|Alpha
;https://www.wolframalpha.com/input/?i=does+%7B%7B1,-tan(x%2F2)%7D,%7B0,1%7D%7D+.+%7B%7B1,0%7D,%7Bsin(x),1%7D%7D+.+%7B%7B1,-tan(x%2F2)%7D,%7B0,1%7D%7D+%3D+%7B%7Bcos(x),-sin(x)%7D,%7Bsin(x),cos(x)%7D%7D

;==================================================

;SCRIPT

#SingleInstance force

#IfWinActive ahk_class MSPaintApp
q:: ;rotate by shear/scale/shear/scale
w:: ;rotate by 3 shears
WinGet, hWnd, ID, A
WinGetClass, vWinClass, % "ahk_id " hWnd
if !(vWinClass = "MSPaintApp")
	return

vApproach := InStr(A_ThisHotkey, "q") ? 1 : 2
vPi := 3.141592653589793
InputBox, vInput,, rotate by what angle?,,,,,,,, 23
if ErrorLevel
	return
;angle: put a for anticlockwise, p for pi, r for radians
;(no need for d=degrees or c=clockwise)

vAngle := StrReplace(vInput, "a")

if InStr(vInput, "a")
	vAngle *= -1
if InStr(vInput, "p")
	vAngle *= vPi
if InStr(vInput, "p") || InStr(vInput, "r")
	vAngle := Round(vAngle * (180/vPi))

WinGet, vPPath, ProcessPath, % "ahk_id " hWnd
FileGetVersion, vPVersion, % vPPath
vIsWinXP := (SubStr(vPVersion, 1, 3) = "5.1")

;click Select tool before begin:
;note: if you do 'Edit, Select All', or equivalent,
;before you begin, you may get problems,
;clicking the Select tool avoids this, e.g. problems:
;the selected image expands,
;but the canvas does not expand,
;unless you copy and paste the image
if vIsWinXP
	SendMessage, 0x111, 620,,, % "ahk_id " hWnd ;WM_COMMAND := 0x111 ;Select

;==============================

;step 1: rotate by between -45 and 45 degrees
vRotate1 := Mod(vAngle, 90)
if (vRotate1 > 45)
	vRotate1 -= 90

;D/R/C/A degrees/radians/clockwise/anticlockwise
vAngleDC := vRotate1
vAngleDA := vAngleDC * -1
vAngleRC := vAngleDC * (vPi/180)
vAngleRA := vAngleRC * -1

if !(vRotate1 = 0)
{
	if (vApproach = 1)
	{
		vMatrix1 := vAngleDC
		vMatrix2 := Round((1/(Cos(vAngleRC)**2)) * 100)
		vMatrix3 := -vAngleDC
		vMatrix4 := Round(Cos(vAngleRC) * 100)
		vList := "3,2,4,1"
	}
	else if (vApproach = 2)
	{
		vMatrix1 := -Round(-vAngleDC/2)
		vMatrix2 := -Round((ATan(Sin(vAngleRC)) * (180/vPi)))
		vMatrix3 := -Round(-vAngleDC/2)
		vList := "3,4,3"
	}

	WinActivate, % "ahk_id " hWnd

	Loop, Parse, vList, % ","
	{
		if !WinActive("ahk_id " hWnd)
			return

		if vIsWinXP
		{
			PostMessage, 0x111, 37681, 0,, % "ahk_id " hWnd ;Stretch/Skew...
			WinWaitActive, Stretch and Skew
			hDlg := WinExist()
			ControlSetText, % "Edit" A_LoopField, % vMatrix%A_Index%, % "ahk_id " hDlg
			if (vApproach = 2) && (A_Index = 4)
				ControlSetText, Edit2, % vMatrix4, % "ahk_id " hDlg
			ControlSend, ahk_parent, {Enter}, % "ahk_id " hDlg
		}
		else ;e.g. Windows 7
		{
			;ControlSend, ahk_parent, ^w, % "ahk_id " hWnd
			SendInput, ^w
			WinWaitActive, Resize and Skew
			hDlg := WinExist()
			ControlSetText, % "Edit" A_LoopField, % vMatrix%A_Index%, % "ahk_id " hDlg
			if (vApproach = 2) && (A_Index = 4)
				ControlSetText, Edit2, % vMatrix4, % "ahk_id " hDlg
			ControlSend, ahk_parent, {Enter}, % "ahk_id " hDlg
		}

		WinWaitActive, % "ahk_id " hWnd
	}
}

;==============================

;step 2: rotate by 0/90/180/270 degrees
vRotate2 := Mod(vAngle - vRotate1, 360)

;note: this stage is only supported on MS Paint (Windows XP version)
if RegExMatch(vRotate2, "^(90|180|270)$")
&& vIsWinXP
{
	PostMessage, 0x111, 37680, 0,, % "ahk_id " hWnd ;Flip/Rotate...
	WinWaitActive, Flip and Rotate
	hDlg := WinExist()

	Control, Check,, Button4, % "ahk_id " hDlg
	if (vRotate2 = 90)
		Control, Check,, Button5, % "ahk_id " hDlg
	else if (vRotate2 = 180)
		Control, Check,, Button6, % "ahk_id " hDlg
	else if (vRotate2 = 270)
		Control, Check,, Button7, % "ahk_id " hDlg

	ControlSend, ahk_parent, {Enter}, % "ahk_id " hDlg
}
return
#IfWinActive

;==================================================

Some common transformations in linear algebra, using a 2x2 matrix, on 2D graphics, are: reflection, rotation, scaling (stretching), shearing (skewing), but *not* translation. It is mathematically possible to exactly rotate by using shears only, or by combining shears and scalings. The rotations in MS Paint are not perfect because integers are rounded, and because square pixels are not a pure mathematical entity, but the principle is correct.

If you haven't already, I'd suggest getting a copy of MS Paint (Windows XP version).
C:\Windows\System32\mspaint.exe
(Other Windows XP candidates: notepad.exe, sndrec32.exe, wordpad.exe.)
(AFAIK, you only need the exe files.)

Links:
[an explanation of matrix multiplication]
matrix functions - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=64480

[I'd recommend using the Gdip library for doing image rotations]
GDI+ standard library 1.45 by tic - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=6&t=6517
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Vh_
Posts: 203
Joined: 17 Mar 2017, 22:06

Re: MS Paint: rotate by scaling/shearing (stretching/skewing)

15 May 2019, 10:39

Impressive! I tried this in windows 10 and it worked. Fun script, thanks for sharing! :)

edit: added a smiley. :D
User avatar
rommmcek
Posts: 1474
Joined: 15 Aug 2014, 15:18

Re: MS Paint: rotate by scaling/shearing (stretching/skewing)

21 May 2019, 02:40

Very nice extension of a rather simple tool (Paint)!

P.s.: I ran into trouble rotating big pics. So I made makeshift fix for rotate by 3 shears (w-Hotkey). Very little tested! (Win10, Dpi: 125%, HD-1920x1080 - if it matters)
Spoiler

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 134 guests