summaryrefslogtreecommitdiffstats
path: root/src/LinearUpscale.h
blob: b75cb4f82b8b0214f155df01ca3efc9a3639e00e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246

// LinearUpscale.h

// Declares the functions for linearly upscaling arrays

/*
Upscaling means that the array is divided into same-size "cells", and each cell is
linearly interpolated between its corners. The array's dimensions are therefore
1 + CellSize * NumCells, for each direction.

Upscaling is more efficient than linear interpolation, because the cell sizes are integral
and therefore the cells' boundaries are on the array points.

However, upscaling usually requires generating the "1 +" in each direction.

Upscaling is implemented in templates, so that it's compatible with multiple datatypes.
Therefore, there is no cpp file.

InPlace upscaling works on a single array and assumes that the values to work on have already
been interspersed into the array to the cell boundaries.
Specifically, a_Array[x * AnchorStepX + y * AnchorStepY] contains the anchor value.

Regular upscaling takes two arrays and "moves" the input from src to dst; src is expected packed.
*/




/**
Linearly interpolates values in the array between the equidistant anchor points (upscales).
Works in-place (input is already present at the correct output coords)
Uses templates to make it possible for the compiler to further optimizer the loops
*/
template <
	int SizeX, int SizeY,  // Dimensions of the array
	int AnchorStepX, int AnchorStepY,
	typename TYPE
>
void LinearUpscale2DArrayInPlace(TYPE * a_Array)
{
	// First interpolate columns where the anchor points are:
	int LastYCell = SizeY - AnchorStepY;
	for (int y = 0; y < LastYCell; y += AnchorStepY)
	{
		int Idx = SizeX * y;
		for (int x = 0; x < SizeX; x += AnchorStepX)
		{
			TYPE StartValue = a_Array[Idx];
			TYPE EndValue   = a_Array[Idx + SizeX * AnchorStepY];
			TYPE Diff = EndValue - StartValue;
			for (int CellY = 1; CellY < AnchorStepY; CellY++)
			{
				a_Array[Idx + SizeX * CellY] = StartValue + Diff * CellY / AnchorStepY;
			}  // for CellY
			Idx += AnchorStepX;
		}  // for x
	}  // for y

	// Now interpolate in rows, each row has values in the anchor columns
	int LastXCell = SizeX - AnchorStepX;
	for (int y = 0; y < SizeY; y++)
	{
		int Idx = SizeX * y;
		for (int x = 0; x < LastXCell; x += AnchorStepX)
		{
			TYPE StartValue = a_Array[Idx];
			TYPE EndValue   = a_Array[Idx + AnchorStepX];
			TYPE Diff = EndValue - StartValue;
			for (int CellX = 1; CellX < AnchorStepX; CellX++)
			{
				a_Array[Idx + CellX] = StartValue + CellX * Diff / AnchorStepX;
			}  // for CellY
			Idx += AnchorStepX;
		}
	}
}





/**
Linearly interpolates values in the array between the equidistant anchor points (upscales).
Works on two arrays, input is packed and output is to be completely constructed.
*/
template <typename TYPE> void LinearUpscale2DArray(
	TYPE * a_Src,                    ///< Source array of size a_SrcSizeX x a_SrcSizeY
	int a_SrcSizeX, int a_SrcSizeY,  ///< Dimensions of the src array
	TYPE * a_Dst,                    ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1)
	int a_UpscaleX, int a_UpscaleY   ///< Upscale factor for each direction
)
{
	// For optimization reasons, we're storing the upscaling ratios in a fixed-size arrays of these sizes
	// Feel free to enlarge them if needed, but keep in mind that they're on the stack
	const int MAX_UPSCALE_X = 129;
	const int MAX_UPSCALE_Y = 129;

	ASSERT(a_Src != nullptr);
	ASSERT(a_Dst != nullptr);
	ASSERT(a_SrcSizeX > 0);
	ASSERT(a_SrcSizeY > 0);
	ASSERT(a_UpscaleX > 0);
	ASSERT(a_UpscaleY > 0);
	ASSERT(a_UpscaleX < MAX_UPSCALE_X);
	ASSERT(a_UpscaleY < MAX_UPSCALE_Y);

	// Pre-calculate the upscaling ratios:
	TYPE RatioX[MAX_UPSCALE_X];
	TYPE RatioY[MAX_UPSCALE_Y];
	for (int x = 0; x <= a_UpscaleX; x++)
	{
		RatioX[x] = static_cast<TYPE>(x) / a_UpscaleX;
	}
	for (int y = 0; y <= a_UpscaleY; y++)
	{
		RatioY[y] = static_cast<TYPE>(y) / a_UpscaleY;
	}

	// Interpolate each XY cell:
	int DstSizeX = (a_SrcSizeX - 1) * a_UpscaleX + 1;
	int DstSizeY = (a_SrcSizeY - 1) * a_UpscaleY + 1;
	for (int y = 0; y < (a_SrcSizeY - 1); y++)
	{
		int DstY = y * a_UpscaleY;
		int idx = y * a_SrcSizeX;
		for (int x = 0; x < (a_SrcSizeX - 1); x++, idx++)
		{
			int DstX = x * a_UpscaleX;
			TYPE LoXLoY = a_Src[idx];
			TYPE LoXHiY = a_Src[idx + a_SrcSizeX];
			TYPE HiXLoY = a_Src[idx + 1];
			TYPE HiXHiY = a_Src[idx + 1 + a_SrcSizeX];
			for (int CellY = 0; CellY <= a_UpscaleY; CellY++)
			{
				int DestIdx = (DstY + CellY) * DstSizeX + DstX;
				ASSERT(DestIdx + a_UpscaleX < DstSizeX * DstSizeY);
				TYPE LoXInY = LoXLoY + (LoXHiY - LoXLoY) * RatioY[CellY];
				TYPE HiXInY = HiXLoY + (HiXHiY - HiXLoY) * RatioY[CellY];
				for (int CellX = 0; CellX <= a_UpscaleX; CellX++, DestIdx++)
				{
					a_Dst[DestIdx] = LoXInY + (HiXInY - LoXInY) * RatioX[CellX];
				}
			}  // for CellY
		}  // for x
	}  // for y
}





/**
Linearly interpolates values in the array between the equidistant anchor points (upscales).
Works on two arrays, input is packed and output is to be completely constructed.
*/
template <typename TYPE> void LinearUpscale3DArray(
	TYPE * a_Src,                                    ///< Source array of size a_SrcSizeX x a_SrcSizeY x a_SrcSizeZ
	int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ,  ///< Dimensions of the src array
	TYPE * a_Dst,                                    ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1) x (a_SrcSizeZ * a_UpscaleZ + 1)
	int a_UpscaleX, int a_UpscaleY, int a_UpscaleZ   ///< Upscale factor for each direction
)
{
	// For optimization reasons, we're storing the upscaling ratios in a fixed-size arrays of these sizes
	// Feel free to enlarge them if needed, but keep in mind that they're on the stack
	const int MAX_UPSCALE_X = 128;
	const int MAX_UPSCALE_Y = 128;
	const int MAX_UPSCALE_Z = 128;

	ASSERT(a_Src != nullptr);
	ASSERT(a_Dst != nullptr);
	ASSERT(a_SrcSizeX > 0);
	ASSERT(a_SrcSizeY > 0);
	ASSERT(a_SrcSizeZ > 0);
	ASSERT(a_UpscaleX > 0);
	ASSERT(a_UpscaleY > 0);
	ASSERT(a_UpscaleZ > 0);
	ASSERT(a_UpscaleX <= MAX_UPSCALE_X);
	ASSERT(a_UpscaleY <= MAX_UPSCALE_Y);
	ASSERT(a_UpscaleZ <= MAX_UPSCALE_Z);

	// Pre-calculate the upscaling ratios:
	TYPE RatioX[MAX_UPSCALE_X];
	TYPE RatioY[MAX_UPSCALE_Y];
	TYPE RatioZ[MAX_UPSCALE_Z];
	for (int x = 0; x <= a_UpscaleX; x++)
	{
		RatioX[x] = static_cast<TYPE>(x) / a_UpscaleX;
	}
	for (int y = 0; y <= a_UpscaleY; y++)
	{
		RatioY[y] = static_cast<TYPE>(y) / a_UpscaleY;
	}
	for (int z = 0; z <= a_UpscaleZ; z++)
	{
		RatioZ[z] = static_cast<TYPE>(z) / a_UpscaleZ;
	}

	// Interpolate each XYZ cell:
	int DstSizeX = (a_SrcSizeX - 1) * a_UpscaleX + 1;
	int DstSizeY = (a_SrcSizeY - 1) * a_UpscaleY + 1;
	int DstSizeZ = (a_SrcSizeZ - 1) * a_UpscaleZ + 1;
	for (int z = 0; z < (a_SrcSizeZ - 1); z++)
	{
		int DstZ = z * a_UpscaleZ;
		for (int y = 0; y < (a_SrcSizeY - 1); y++)
		{
			int DstY = y * a_UpscaleY;
			int idx = y * a_SrcSizeX + z * a_SrcSizeX * a_SrcSizeY;
			for (int x = 0; x < (a_SrcSizeX - 1); x++, idx++)
			{
				int DstX = x * a_UpscaleX;
				TYPE LoXLoYLoZ = a_Src[idx];
				TYPE LoXLoYHiZ = a_Src[idx + a_SrcSizeX * a_SrcSizeY];
				TYPE LoXHiYLoZ = a_Src[idx + a_SrcSizeX];
				TYPE LoXHiYHiZ = a_Src[idx + a_SrcSizeX + a_SrcSizeX * a_SrcSizeY];
				TYPE HiXLoYLoZ = a_Src[idx + 1];
				TYPE HiXLoYHiZ = a_Src[idx + 1 + a_SrcSizeX * a_SrcSizeY];
				TYPE HiXHiYLoZ = a_Src[idx + 1 + a_SrcSizeX];
				TYPE HiXHiYHiZ = a_Src[idx + 1 + a_SrcSizeX + a_SrcSizeX * a_SrcSizeY];
				for (int CellZ = 0; CellZ <= a_UpscaleZ; CellZ++)
				{
					TYPE LoXLoYInZ = LoXLoYLoZ + (LoXLoYHiZ - LoXLoYLoZ) * RatioZ[CellZ];
					TYPE LoXHiYInZ = LoXHiYLoZ + (LoXHiYHiZ - LoXHiYLoZ) * RatioZ[CellZ];
					TYPE HiXLoYInZ = HiXLoYLoZ + (HiXLoYHiZ - HiXLoYLoZ) * RatioZ[CellZ];
					TYPE HiXHiYInZ = HiXHiYLoZ + (HiXHiYHiZ - HiXHiYLoZ) * RatioZ[CellZ];
					for (int CellY = 0; CellY <= a_UpscaleY; CellY++)
					{
						int DestIdx = (DstZ + CellZ) * DstSizeX * DstSizeY + (DstY + CellY) * DstSizeX + DstX;
						ASSERT(DestIdx + a_UpscaleX < DstSizeX * DstSizeY * DstSizeZ);
						TYPE LoXInY = LoXLoYInZ + (LoXHiYInZ - LoXLoYInZ) * RatioY[CellY];
						TYPE HiXInY = HiXLoYInZ + (HiXHiYInZ - HiXLoYInZ) * RatioY[CellY];
						for (int CellX = 0; CellX <= a_UpscaleX; CellX++, DestIdx++)
						{
							a_Dst[DestIdx] = LoXInY + (HiXInY - LoXInY) * RatioX[CellX];
						}
					}  // for CellY
				}  // for CellZ
			}  // for x
		}  // for y
	}  // for z
}