IntegerScaling/
IntegerScaling.rs

1/*! IntegerScaling by Marat Tanalin | http://tanalin.com/en/projects/integer-scaling/ */
2
3#![allow(non_snake_case)]
4
5pub struct Ratios {
6	pub x : u32,
7	pub y : u32
8}
9
10pub struct Size {
11	pub width  : u32,
12	pub height : u32
13}
14
15/// Calculates an integer scaling ratio common for X/Y axes (square pixels).
16pub fn calculateRatio(areaWidth : u32, areaHeight : u32,
17	imageWidth : u32, imageHeight : u32) -> u32
18{
19	let (areaSize, imageSize);
20
21	if areaHeight * imageWidth < areaWidth * imageHeight {
22		areaSize  = areaHeight;
23		imageSize = imageHeight;
24	}
25	else {
26		areaSize  = areaWidth;
27		imageSize = imageWidth;
28	}
29
30	let mut ratio = areaSize / imageSize;
31
32	if ratio < 1 {
33		ratio = 1;
34	}
35
36	ratio
37}
38
39/// Calculates integer scaling ratios potentially different for X/Y axes
40/// as a result of aspect-ratio correction (rectangular pixels).
41pub fn calculateRatios(areaWidth : u32, areaHeight : u32,
42	imageWidth : u32, imageHeight : u32,
43	aspectX : f64, aspectY : f64) -> Ratios
44{
45	if imageWidth as f64 * aspectY == imageHeight as f64 * aspectX {
46		let ratio = calculateRatio(areaWidth, areaHeight, imageWidth, imageHeight);
47
48		return Ratios {
49			x: ratio,
50			y: ratio
51		};
52	}
53
54	let maxRatioX        = areaWidth  / imageWidth;
55	let maxRatioY        = areaHeight / imageHeight;
56	let maxWidth         = imageWidth  * maxRatioX;
57	let maxHeight        = imageHeight * maxRatioY;
58	let maxWidthAspectY  = maxWidth  as f64 * aspectY;
59	let maxHeightAspectX = maxHeight as f64 * aspectX;
60
61	let mut ratioX : u32;
62	let mut ratioY : u32;
63
64	if maxWidthAspectY == maxHeightAspectX {
65		ratioX = maxRatioX;
66		ratioY = maxRatioY;
67	}
68	else {
69		let maxAspectLessThanTarget = maxWidthAspectY < maxHeightAspectX;
70
71		let (ratioA, maxSizeA, imageSizeB, aspectA, aspectB) : (u32, u32, u32, f64, f64);
72
73		if maxAspectLessThanTarget {
74			ratioA     = maxRatioX;
75			maxSizeA   = maxWidth;
76			imageSizeB = imageHeight;
77			aspectA    = aspectX;
78			aspectB    = aspectY;
79		}
80		else {
81			ratioA     = maxRatioY;
82			maxSizeA   = maxHeight;
83			imageSizeB = imageWidth;
84			aspectA    = aspectY;
85			aspectB    = aspectX;
86		}
87
88		let ratioBFract = maxSizeA as f64 * aspectB / aspectA / imageSizeB as f64;
89		let ratioBFloor = ratioBFract.floor();
90		let ratioBCeil  = ratioBFract.ceil();
91
92		let mut parFloor = ratioBFloor / ratioA as f64;
93		let mut parCeil  = ratioBCeil  / ratioA as f64;
94
95		if maxAspectLessThanTarget {
96			parFloor = 1.0 / parFloor;
97			parCeil  = 1.0 / parCeil;
98		}
99
100		let commonFactor = imageWidth as f64 * aspectY / aspectX / imageHeight as f64;
101		let errorFloor   = (1.0 - commonFactor * parFloor).abs();
102		let errorCeil    = (1.0 - commonFactor * parCeil).abs();
103
104		let ratioB : u32;
105
106		if (errorFloor - errorCeil).abs() < 0.001 {
107			ratioB = if (ratioA as f64 - ratioBFloor).abs() < (ratioA as f64 - ratioBCeil).abs()
108			         {ratioBFloor as u32}
109			         else
110			         {ratioBCeil as u32};
111		}
112		else {
113			ratioB = if errorFloor < errorCeil
114			         {ratioBFloor as u32}
115			         else
116			         {ratioBCeil as u32};
117		}
118
119		if maxAspectLessThanTarget {
120			ratioX = ratioA;
121			ratioY = ratioB;
122		}
123		else {
124			ratioX = ratioB;
125			ratioY = ratioA;
126		}
127	}
128
129	if ratioX < 1 {
130		ratioX = 1;
131	}
132
133	if ratioY < 1 {
134		ratioY = 1;
135	}
136
137	Ratios {
138		x: ratioX,
139		y: ratioY
140	}
141}
142
143/// Calculates size (width and height) of scaled image
144/// without aspect-ratio correction (square pixels).
145pub fn calculateSize(areaWidth : u32, areaHeight : u32,
146	imageWidth : u32, imageHeight : u32) -> Size
147{
148	let ratio = calculateRatio(areaWidth, areaHeight, imageWidth, imageHeight);
149
150	Size {
151		width  : imageWidth  * ratio,
152		height : imageHeight * ratio
153	}
154}
155
156/// Calculates size (width and height) of scaled image
157/// with aspect-ratio correction (rectangular pixels).
158pub fn calculateSizeCorrected(areaWidth : u32, areaHeight : u32,
159	imageWidth : u32, imageHeight : u32,
160	aspectX : f64, aspectY : f64) -> Size
161{
162	let ratios = calculateRatios(areaWidth, areaHeight, imageWidth, imageHeight, aspectX, aspectY);
163
164	Size {
165		width  : imageWidth  * ratios.x,
166		height : imageHeight * ratios.y
167	}
168}
169
170/**
171 * Calculates size (width and height) of scaled image with aspect-ratio
172 * correction with integer vertical scaling ratio, but fractional horizontal
173 * scaling ratio for the purpose of achieving precise aspect ratio while
174 * still having integer vertical scaling e.g. for uniform scanlines.
175 */
176pub fn calculateSizeCorrectedPerfectY(areaWidth : u32, areaHeight : u32,
177	imageHeight : u32,
178	aspectX : f64, aspectY : f64) -> Size
179{
180	let imageWidth = imageHeight as f64 * aspectX / aspectY;
181
182	let imageSize : f64;
183	let areaSize  : u32;
184
185	if areaHeight as f64 * imageWidth < areaWidth as f64 * imageHeight as f64 {
186		areaSize  = areaHeight;
187		imageSize = imageHeight as f64;
188	}
189	else {
190		areaSize  = areaWidth;
191		imageSize = imageWidth;
192	}
193
194	let mut ratio = areaSize / imageSize as u32;
195
196	if ratio < 1 {
197		ratio = 1;
198	}
199
200	let mut width = (imageWidth * ratio as f64).round() as u32;
201
202	if width > areaWidth {
203		width -= 1;
204	}
205
206	Size {
207		width  : width,
208		height : imageHeight * ratio
209	}
210}