aboutsummaryrefslogtreecommitdiffstats
path: root/src/scale.c
blob: 6555af363547c0f19bbeda93027cb4898a678c53 (plain)
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
#include <assert.h>
#include <string.h>
#include <stdint.h>
#include <sys/time.h>
#include "meh.h"


#define GETVAL0(c) ((ibuf[x0 + (c)] * (ur) + ibuf[x1 + (c)] * (u)) * (vr) >> 20)
#define GETVAL(c) (( \
			( \
				ibuf[x0 + (c)] * (ur) + \
				ibuf[x1 + (c)] * (u) \
			) * (vr) + \
			( \
				(ibufn[x0 + (c)]) * (ur) + \
				(ibufn[x1 + (c)]) * (u)\
			) * (v)) >> 20)

#define XLOOP(F) \
	for(x = 0; x < width*4;){ \
		const unsigned int x0 = a[x++];\
		const unsigned int x1 = a[x++];\
		const unsigned int u  = a[x++];\
		const unsigned int ur = a[x++];\
		*newBuf++ = F(2);\
		*newBuf++ = F(1);\
		*newBuf++ = F(0);\
		newBuf++;\
	}

#define YITER \
	const unsigned int bufy = (y << 10) * img->bufheight / height;\
	const unsigned int v = (bufy & 1023);\
	const unsigned int vr = 1023^v;\
	ibuf = &img->buf[y * img->bufheight / height * img->bufwidth * 3];\
	ibufn = ibuf + dy;

/*
 * Super sampling scale. Down only.
 */
static void superscale(struct image *img, unsigned int width, unsigned int height, unsigned int bytesperline, char* __restrict__ newBuf){
	uint32_t x, y, i;
	unsigned char * __restrict__ ibuf;
	ibuf = &img->buf[0];

	TDEBUG_START

	int divx[bytesperline];
	int divy[bytesperline];
	memset(divx, 0, sizeof divx);
	memset(divy, 0, sizeof divy);
	for(x = 0; x < img->bufwidth; x++){
		 divx[x * width / img->bufwidth]++;
	}
	for(y = 0; y < img->bufheight; y++){
		 divy[y * height / img->bufheight]++;
	}

	int xoff[img->bufwidth];
	for(x = 0; x < img->bufwidth; x++){
		xoff[x] = (x * width / img->bufwidth) * 3;
	}

	int tmp[width * 4];
	unsigned int y0;
	for(y = 0; y < img->bufheight;){
		int ydiv = divy[y * height / img->bufheight];
		char * __restrict__ ydest = &newBuf[bytesperline * (y * height / img->bufheight)];
		memset(tmp, 0, sizeof tmp);
		ibuf = &img->buf[y * img->bufwidth * 3];
		for(y0 = y; y < y0 + ydiv; y++){
			for(x = 0; x < img->bufwidth; x++){
				int * __restrict__ dest = tmp + xoff[x];
				for(i = 0; i < 3; i++){
					*dest++ += *ibuf++;
				}
			}
		}
		int * __restrict__ src = tmp;
		for(x = 0; x < width; x++){
			ydest[2] = *src++ / ydiv / divx[x];
			ydest[1] = *src++ / ydiv / divx[x];
			ydest[0] = *src++ / ydiv / divx[x];
			ydest += 4;
		}
	}

	TDEBUG_END("superscale")
}

/*
 * Bilinear scale. Used for up only.
 */
static void bilinearscale(struct image *img, unsigned int width, unsigned int height, unsigned int bytesperline, char* __restrict__ newBuf){
	unsigned int x, y;
	const unsigned char * __restrict__ ibuf;
	const unsigned char * __restrict__ ibufn;
	const unsigned char * const bufend = &img->buf[img->bufwidth * img->bufheight * 3];
	const unsigned int jdy = bytesperline / 4 - width;
	const unsigned int dy = img->bufwidth * 3;

	TDEBUG_START

	unsigned int a[width * 4];
	{
		unsigned int dx = (img->bufwidth << 10) / width;
		unsigned int bufx = img->bufwidth / width;
		for(x = 0; x < width * 4;){
			if((bufx >> 10) >= img->bufwidth - 1){
				a[x++] = (img->bufwidth - 1) * 3;
				a[x++] = (img->bufwidth - 1) * 3;
				a[x++] = 0;
				a[x++] = 1023 ^ (bufx & 1023);
			}else{
				a[x++] = (bufx >> 10) * 3;
				a[x++] = ((bufx >> 10) + 1) * 3;
				a[x++] = (bufx & 1023);
				a[x++] = 1023 ^ (bufx & 1023);
			}
			bufx += dx;
		}
	}
	y = 0;
	ibuf = img->buf;
	ibufn = img->buf + dy;
	for(;;){
		YITER
		if(ibufn + dy > bufend){
			break;
		}
		XLOOP(GETVAL)
		newBuf += jdy;
		y++;
	}
	for(;;){
		YITER
		if(ibufn > bufend){
			break;
		}
		XLOOP(GETVAL0)
		newBuf += jdy;
		y++;
	}

	TDEBUG_END("bilinearscale")
}

void scale(struct image *img, unsigned int width, unsigned int height, unsigned int bytesperline, char* __restrict__ newBuf){
	if(width < img->bufwidth){
		superscale(img, width, height, bytesperline, newBuf);
	}else{
		bilinearscale(img, width, height, bytesperline, newBuf);
	}
}

/*
 * Nearest neighbour. Fast up and down.
 */
void nearestscale(struct image *img, unsigned int width, unsigned int height, unsigned int bytesperline, char* __restrict__ newBuf){
	unsigned int x, y;
	unsigned char * __restrict__ ibuf;
	unsigned int jdy = bytesperline / 4 - width;
	unsigned int dx = (img->bufwidth << 10) / width;

	TDEBUG_START

	for(y = 0; y < height; y++){
		unsigned int bufx = img->bufwidth / width;
		ibuf = &img->buf[y * img->bufheight / height * img->bufwidth * 3];

		for(x = 0; x < width; x++){
			*newBuf++ = (ibuf[(bufx >> 10)*3+2]);
			*newBuf++ = (ibuf[(bufx >> 10)*3+1]);
			*newBuf++ = (ibuf[(bufx >> 10)*3+0]);
			newBuf++;
			bufx += dx;
		}
		newBuf += jdy;
	}

	TDEBUG_END("nearestscale")
}