1 /*
2  * Copyright © 2016 Jerome Flesch
3  *
4  * Pypillowfight is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, version 2 of the License.
7  *
8  * Pypillowfight is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 #include <assert.h>
18 #include <errno.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 #include <pillowfight/util.h>
24 
25 const union pf_pixel g_pf_default_white_pixel = {
26 	PF_WHOLE_WHITE,
27 };
28 
29 #ifndef NO_PYTHON
30 
from_py_buffer(const Py_buffer * buffer,int x,int y)31 struct pf_bitmap from_py_buffer(const Py_buffer *buffer, int x, int y)
32 {
33 	struct pf_bitmap out;
34 
35 	assert(x * y * 4 == buffer->len);
36 
37 	out.size.x = x;
38 	out.size.y = y;
39 	out.pixels = buffer->buf;
40 
41 	return out;
42 }
43 
44 #endif
45 
46 
pf_clear_rect(struct pf_bitmap * img,int left,int top,int right,int bottom)47 void pf_clear_rect(struct pf_bitmap *img, int left, int top, int right, int bottom)
48 {
49 	int x;
50 	int y;
51 
52 	if (left < 0)
53 		left = 0;
54 	if (top < 0)
55 		top = 0;
56 	if (right > img->size.x)
57 		right = img->size.x;
58 	if (bottom > img->size.y)
59 		bottom = img->size.y;
60 
61 	for (y = top; y < bottom; y++) {
62 		for (x = left; x < right; x++) {
63 			PF_SET_PIXEL(img, x, y, PF_WHOLE_WHITE);
64 		}
65 	}
66 }
67 
68 
pf_count_pixels_rect(int left,int top,int right,int bottom,int max_brightness,const struct pf_bitmap * img)69 int pf_count_pixels_rect(int left, int top, int right, int bottom,
70 		int max_brightness, const struct pf_bitmap *img)
71 {
72 	int x;
73 	int y;
74 	int count = 0;
75 	int pixel;
76 
77 	for (y = top; y <= bottom; y++) {
78 		for (x = left; x <= right; x++) {
79 			pixel = PF_GET_PIXEL_GRAYSCALE(img, x, y);
80 			if ((pixel >= 0) && (pixel <= max_brightness)) {
81 				count++;
82 			}
83 		}
84 	}
85 	return count;
86 }
87 
88 
pf_apply_mask(struct pf_bitmap * img,const struct pf_rectangle * mask)89 void pf_apply_mask(struct pf_bitmap *img, const struct pf_rectangle *mask) {
90 	int x;
91 	int y;
92 
93 	for (y=0 ; y < img->size.y ; y++) {
94 		for (x=0 ; x < img->size.x ; x++) {
95 			if (!(PF_IS_IN(x, mask->a.x, mask->b.x)
96 					&& PF_IS_IN(y, mask->a.y, mask->b.y))) {
97 				PF_SET_PIXEL(img, x, y, PF_WHOLE_WHITE);
98 			}
99 		}
100 	}
101 }
102 
103 
pf_dbl_matrix_new(int x,int y)104 struct pf_dbl_matrix pf_dbl_matrix_new(int x, int y)
105 {
106 	struct pf_dbl_matrix out;
107 	out.size.x = x;
108 	out.size.y = y;
109 	out.values = calloc(x * y, sizeof(out.values[0]));
110 	return out;
111 }
112 
113 
pf_dbl_matrix_free(struct pf_dbl_matrix * matrix)114 void pf_dbl_matrix_free(struct pf_dbl_matrix *matrix)
115 {
116 	free(matrix->values);
117 }
118 
119 
pf_dbl_matrix_copy(const struct pf_dbl_matrix * in)120 struct pf_dbl_matrix pf_dbl_matrix_copy(const struct pf_dbl_matrix *in)
121 {
122 	struct pf_dbl_matrix out;
123 
124 	out = pf_dbl_matrix_new(in->size.x, in->size.y);
125 	memcpy(out.values, in->values, in->size.x * in->size.y * sizeof(out.values[0]));
126 
127 	return out;
128 }
129 
130 
dbl_matrix_transpose(const struct pf_dbl_matrix * in)131 struct pf_dbl_matrix dbl_matrix_transpose(const struct pf_dbl_matrix *in)
132 {
133 	struct pf_dbl_matrix out;
134 	int x, y;
135 	double val;
136 
137 	out = pf_dbl_matrix_new(in->size.y, in->size.x);
138 	for (x = 0 ; x < in->size.x ; x++) {
139 		for (y = 0; y < in->size.y ; y++) {
140 			val = PF_MATRIX_GET(in, x, y);
141 			PF_MATRIX_SET(&out, y, x, val);
142 		}
143 	}
144 
145 	return out;
146 }
147 
148 /*!
149  * Ref: https://en.wikipedia.org/wiki/Kernel_%28image_processing%29#Convolution
150  * Ref: http://www.songho.ca/dsp/convolution/convolution2d_example.html
151  */
pf_dbl_matrix_convolution(const struct pf_dbl_matrix * img,const struct pf_dbl_matrix * kernel)152 struct pf_dbl_matrix pf_dbl_matrix_convolution(
153 		const struct pf_dbl_matrix *img,
154 		const struct pf_dbl_matrix *kernel)
155 {
156 	struct pf_dbl_matrix out;
157 	int img_y, kernel_y;
158 	int img_x, kernel_x;
159 	double img_val, kernel_val;
160 	double val;
161 
162 	out = pf_dbl_matrix_new(img->size.x, img->size.y);
163 
164 	for (img_x = 0 ; img_x < img->size.x ; img_x++) {
165 		for (img_y = 0 ; img_y < img->size.y ; img_y++) {
166 
167 			val = 0;
168 
169 			for (kernel_x = 0 ; kernel_x < kernel->size.x ; kernel_x++) {
170 				if (((img_x - kernel_x + (kernel->size.x / 2)) < 0)
171 						|| ((img_x - kernel_x + (kernel->size.x / 2)) >= img->size.x))
172 					break;
173 
174 				for (kernel_y = 0 ; kernel_y < kernel->size.y ; kernel_y++) {
175 
176 					if (((img_y - kernel_y + (kernel->size.y / 2)) < 0)
177 							|| ((img_y - kernel_y + (kernel->size.y / 2)) >= img->size.y))
178 						break;
179 
180 					img_val = PF_MATRIX_GET(
181 							img,
182 							img_x - kernel_x + (kernel->size.x / 2),
183 							img_y - kernel_y + (kernel->size.y / 2)
184 						);
185 
186 					kernel_val = PF_MATRIX_GET(
187 							kernel,
188 							kernel_x,
189 							kernel_y
190 						);
191 
192 					val += (img_val * kernel_val);
193 
194 				}
195 			}
196 
197 			PF_MATRIX_SET(&out, img_x, img_y, val);
198 		}
199 	}
200 
201 	return out;
202 }
203 
pf_rgb_bitmap_to_grayscale_dbl_matrix(const struct pf_bitmap * in,struct pf_dbl_matrix * out)204 void pf_rgb_bitmap_to_grayscale_dbl_matrix(const struct pf_bitmap *in, struct pf_dbl_matrix *out)
205 {
206 	int x, y;
207 	int value;
208 
209 	assert(out->size.x == in->size.x);
210 	assert(out->size.y == in->size.y);
211 
212 	for (x = 0 ; x < in->size.x ; x++) {
213 		for (y = 0 ; y < in->size.y ; y++) {
214 			value = PF_GET_PIXEL_GRAYSCALE(in, x, y);
215 			PF_MATRIX_SET(
216 				out, x, y,
217 				value
218 			);
219 		}
220 	}
221 }
222 
pf_grayscale_dbl_matrix_to_rgb_bitmap(const struct pf_dbl_matrix * in,struct pf_bitmap * out)223 void pf_grayscale_dbl_matrix_to_rgb_bitmap(const struct pf_dbl_matrix *in, struct pf_bitmap *out)
224 {
225 	int x, y;
226 	int value;
227 
228 	assert(out->size.x == in->size.x);
229 	assert(out->size.y == in->size.y);
230 
231 	for (x = 0 ; x < in->size.x ; x++) {
232 		for (y = 0 ; y < in->size.y ; y++) {
233 			value = PF_MATRIX_GET(in, x, y);
234 			if (value < 0)
235 				value = 0;
236 			if (value >= 256)
237 				value = 255;
238 			PF_SET_COLOR(out, x, y, COLOR_R, value);
239 			PF_SET_COLOR(out, x, y, COLOR_G, value);
240 			PF_SET_COLOR(out, x, y, COLOR_B, value);
241 			PF_SET_COLOR(out, x, y, COLOR_A, 0xFF);
242 		}
243 	}
244 }
245 
246 
pf_bitmap_channel_to_dbl_matrix(const struct pf_bitmap * in,struct pf_dbl_matrix * out,enum pf_color color)247 void pf_bitmap_channel_to_dbl_matrix(
248 		const struct pf_bitmap *in, struct pf_dbl_matrix *out, enum pf_color color
249 )
250 {
251 	int x, y;
252 	int value;
253 
254 	assert(out->size.x == in->size.x);
255 	assert(out->size.y == in->size.y);
256 
257 	for (x = 0 ; x < in->size.x ; x++) {
258 		for (y = 0 ; y < in->size.y ; y++) {
259 			value = PF_GET_COLOR(in, x, y, color);
260 			PF_MATRIX_SET(
261 				out, x, y,
262 				value
263 			);
264 		}
265 	}
266 }
267 
268 
pf_matrix_to_rgb_bitmap(const struct pf_dbl_matrix * in,struct pf_bitmap * out,enum pf_color color)269 void pf_matrix_to_rgb_bitmap(const struct pf_dbl_matrix *in, struct pf_bitmap *out, enum pf_color color)
270 {
271 	int x, y;
272 	int value;
273 
274 	assert(out->size.x == in->size.x);
275 	assert(out->size.y == in->size.y);
276 
277 	for (x = 0 ; x < out->size.x ; x++) {
278 		for (y = 0 ; y < out->size.y ; y++) {
279 			value = PF_MATRIX_GET(in, x, y);
280 			if (value < 0)
281 				value = 0;
282 			if (value >= 256)
283 				value = 255;
284 			PF_SET_COLOR(out, x, y, color, value);
285 			PF_SET_COLOR(out, x, y, COLOR_A, 0xFF);
286 		}
287 	}
288 }
289 
pf_write_bitmap_to_ppm(const char * filepath,const struct pf_bitmap * in)290 void pf_write_bitmap_to_ppm(const char *filepath, const struct pf_bitmap *in)
291 {
292 	FILE *fp;
293 	int x, y;
294 	int pixel;
295 
296 	fp = fopen(filepath, "w");
297 	if (fp == NULL) {
298 		fprintf(stderr, "Failed to write [%s]: %d, %s\n",
299 				filepath, errno, strerror(errno));
300 	}
301 
302 	fprintf(fp, "P6\n");
303 	fprintf(fp, "%d %d\n", in->size.x, in->size.y);
304 	fprintf(fp, "255\n");
305 
306 	for (y = 0 ; y < in->size.y ; y++) {
307 		for (x = 0 ; x < in->size.x ; x++) {
308 			pixel = PF_GET_PIXEL(in, x, y).whole;
309 			fwrite(&pixel, 3, 1, fp);
310 		}
311 	}
312 
313 	fclose(fp);
314 }
315 
pf_write_matrix_to_pgm(const char * filepath,const struct pf_dbl_matrix * in,double factor)316 void pf_write_matrix_to_pgm(const char *filepath, const struct pf_dbl_matrix *in, double factor)
317 {
318 	FILE *fp;
319 	int x, y;
320 	double val;
321 	uint8_t val8;
322 
323 	fp = fopen(filepath, "w");
324 	if (fp == NULL) {
325 		fprintf(stderr, "Failed to write [%s]: %d, %s\n",
326 				filepath, errno, strerror(errno));
327 	}
328 
329 	fprintf(fp, "P5\n");
330 	fprintf(fp, "%d %d\n", in->size.x, in->size.y);
331 	fprintf(fp, "255\n");
332 
333 	for (y = 0 ; y < in->size.y ; y++) {
334 		for (x = 0 ; x < in->size.x ; x++) {
335 			val = PF_MATRIX_GET(in, x, y);
336 			val *= factor;
337 			if (val > 255.0)
338 				val = 255.0;
339 			else if (val < 0)
340 				val = 0.0;
341 			val8 = val;
342 			fwrite(&val8, 1, 1, fp);
343 		}
344 	}
345 
346 	fclose(fp);
347 }
348 
pf_normalize(const struct pf_dbl_matrix * in,double factor,double out_min,double out_max)349 struct pf_dbl_matrix pf_normalize(const struct pf_dbl_matrix *in, double factor, double out_min, double out_max)
350 {
351 	struct pf_dbl_matrix out;
352 	int x, y;
353 	double val;
354 	double in_min = out_min, in_max = out_max;
355 
356 	if (factor == 0.0) {
357 		in_min = MAXDOUBLE;
358 		in_max = -MAXDOUBLE;
359 		for (x = 0; x < in->size.x ; x++) {
360 			for (y = 0 ; y < in->size.y ; y++) {
361 				val = PF_MATRIX_GET(in, x, y);
362 				in_min = MIN(in_min, val);
363 				in_max = MAX(in_max, val);
364 			}
365 		}
366 
367 		factor = (out_max - out_min) / (in_max - in_min);
368 	}
369 
370 	out = pf_dbl_matrix_new(in->size.x, in->size.y);
371 	for (x = 0; x < in->size.x ; x++) {
372 		for (y = 0 ; y < in->size.y ; y++) {
373 			val = PF_MATRIX_GET(in, x, y);
374 			val -= in_min;
375 			val *= factor;
376 			val += out_min;
377 			PF_MATRIX_SET(&out, x, y, val);
378 		}
379 	}
380 
381 	return out;
382 }
383 
pf_grayscale_reverse(const struct pf_dbl_matrix * in)384 struct pf_dbl_matrix pf_grayscale_reverse(const struct pf_dbl_matrix *in)
385 {
386 	struct pf_dbl_matrix out;
387 	int x, y;
388 	double val;
389 	double in_min, in_max;
390 	double factor;
391 
392 	in_min = DBL_MAX;
393 	in_max = -DBL_MAX;
394 	for (x = 0; x < in->size.x ; x++) {
395 		for (y = 0 ; y < in->size.y ; y++) {
396 			val = PF_MATRIX_GET(in, x, y);
397 			in_min = MIN(in_min, val);
398 			in_max = MAX(in_max, val);
399 		}
400 	}
401 
402 	factor = (in_min - in_max) / (in_max - in_min);
403 
404 	out = pf_dbl_matrix_new(in->size.x, in->size.y);
405 	for (x = 0; x < in->size.x ; x++) {
406 		for (y = 0 ; y < in->size.y ; y++) {
407 			val = PF_MATRIX_GET(in, x, y);
408 			val *= factor;
409 			val += in_max;
410 			PF_MATRIX_SET(&out, x, y, val);
411 		}
412 	}
413 
414 	return out;
415 }
416