1 /* z.lib example code for C API.
2  *
3  * Example code demonstrates the use of z.lib to scale a YV12/I420 image.
4  */
5 
6 #ifndef _WIN32
7   #define _POSIX_C_SOURCE 200112L
8 #endif
9 
10 #include <stddef.h>
11 #include <stdio.h>
12 
13 #include <zimg.h>
14 
15 #include "aligned_malloc.h"
16 #include "argparse.h"
17 
18 struct Arguments {
19 	const char *inpath;
20 	const char *outpath;
21 	unsigned in_w;
22 	unsigned in_h;
23 	unsigned out_w;
24 	unsigned out_h;
25 };
26 
27 static const ArgparseOption program_positional[] = {
28 	{ OPTION_STRING, 0, "inpath",     offsetof(struct Arguments, inpath),  0, "input path" },
29 	{ OPTION_STRING, 0, "outpath",    offsetof(struct Arguments, outpath), 0, "output path" },
30 	{ OPTION_UINT,   0, "in_width",   offsetof(struct Arguments, in_w),    0, "input width" },
31 	{ OPTION_UINT,   0, "in_height",  offsetof(struct Arguments, in_h),    0, "input height" },
32 	{ OPTION_UINT,   0, "out_width",  offsetof(struct Arguments, out_w),   0, "output width" },
33 	{ OPTION_UINT,   0, "out_height", offsetof(struct Arguments, out_h),   0, "output height" },
34 	{ OPTION_NULL }
35 };
36 
37 static const ArgparseCommandLine program_def = { 0, program_positional, "api_example_c", "resize 4:2:0 images" };
38 
width_to_stride(unsigned w)39 static ptrdiff_t width_to_stride(unsigned w)
40 {
41 	return ((size_t)w + 31) & ~31;
42 }
43 
44 /* Allocate an image buffer and initialize plane pointers and strides. */
init_image(unsigned w,unsigned h,void * data[3],ptrdiff_t stride[3])45 static void *init_image(unsigned w, unsigned h, void *data[3], ptrdiff_t stride[3])
46 {
47 	size_t rowsize = width_to_stride(w);
48 	size_t rowsize_uv = width_to_stride(w / 2);
49 	size_t size = 0;
50 	void *ptr;
51 	char *pptr;
52 	unsigned p;
53 
54 	size += rowsize * h;
55 	size += rowsize * (h / 2);
56 	size += rowsize * (h / 2);
57 
58 	if (!(ptr = aligned_malloc(size, 32)))
59 		return 0;
60 
61 	pptr = ptr;
62 	for (p = 0; p < 3; ++p) {
63 		size_t rowsize_p = p ? rowsize_uv : rowsize;
64 		size_t plane_sz = rowsize_p * (p ? h / 2 : h);
65 
66 		data[p] = pptr;
67 		stride[p] = rowsize_p;
68 
69 		pptr += plane_sz;
70 	}
71 	return ptr;
72 }
73 
read_image_from_file(const char * path,unsigned w,unsigned h,void * const data[3],const ptrdiff_t stride[3])74 static int read_image_from_file(const char *path, unsigned w, unsigned h, void * const data[3], const ptrdiff_t stride[3])
75 {
76 	FILE *file;
77 	unsigned p, i;
78 	int ret = 1;
79 
80 	if (!(file = fopen(path, "rb")))
81 		goto fail;
82 
83 	for (p = 0; p < 3; ++p) {
84 		unsigned width = p ? w / 2 : w;
85 		unsigned height = p ? h / 2 : h;
86 		char *ptr = data[p];
87 
88 		for (i = 0; i < height; ++i) {
89 			char *row_ptr = ptr;
90 			size_t to_read = width;
91 
92 			while (to_read) {
93 				size_t n = fread(row_ptr, 1, to_read, file);
94 
95 				if (n != to_read) {
96 					if (ferror(file)) {
97 						perror("error reading file");
98 						goto fail;
99 					}
100 					if (feof(file)) {
101 						fprintf(stderr, "unexpected end of file at: p=%u, i=%u\n", p, i);
102 						goto fail;
103 					}
104 				}
105 
106 				row_ptr += n;
107 				to_read -= n;
108 			}
109 			ptr += stride[p];
110 		}
111 	}
112 
113 	ret = 0;
114 fail:
115 	if (file)
116 		fclose(file);
117 	return ret;
118 }
119 
write_image_to_file(const char * path,unsigned w,unsigned h,const void * const data[3],const ptrdiff_t stride[3])120 static int write_image_to_file(const char *path, unsigned w, unsigned h, const void * const data[3], const ptrdiff_t stride[3])
121 {
122 	FILE *file;
123 	unsigned p, i;
124 	int ret = 1;
125 
126 	if (!(file = fopen(path, "wb")))
127 		goto fail;
128 
129 	for (p = 0; p < 3; ++p) {
130 		unsigned width = p ? w / 2 : w;
131 		unsigned height = p ? h / 2 : h;
132 		const char *ptr = data[p];
133 
134 		for (i = 0; i < height; ++i) {
135 			const char *row_ptr = ptr;
136 			size_t to_write = width;
137 
138 			while (to_write) {
139 				size_t n = fwrite(row_ptr, 1, to_write, file);
140 
141 				if (n != to_write && ferror(file)) {
142 					perror("error writing file");
143 					goto fail;
144 				}
145 
146 				row_ptr += n;
147 				to_write -= n;
148 			}
149 			ptr += stride[p];
150 		}
151 	}
152 
153 	ret = 0;
154 fail:
155 	if (file)
156 		fclose(file);
157 	return ret;
158 }
159 
print_zimg_error(void)160 static void print_zimg_error(void)
161 {
162 	char err_msg[1024];
163 	int err_code = zimg_get_last_error(err_msg, sizeof(err_msg));
164 
165 	fprintf(stderr, "zimg error %d: %s\n", err_code, err_msg);
166 }
167 
process(const struct Arguments * args,const void * const src_p[3],void * const dst_p[3],const ptrdiff_t src_stride[3],const ptrdiff_t dst_stride[3])168 static int process(const struct Arguments *args, const void * const src_p[3], void * const dst_p[3], const ptrdiff_t src_stride[3], const ptrdiff_t dst_stride[3])
169 {
170 	zimg_filter_graph *graph = 0;
171 	zimg_image_buffer_const src_buf = { ZIMG_API_VERSION };
172 	zimg_image_buffer dst_buf = { ZIMG_API_VERSION };
173 	zimg_image_format src_format;
174 	zimg_image_format dst_format;
175 	size_t tmp_size;
176 	void *tmp = 0;
177 	unsigned p;
178 	int ret = 1;
179 
180 	/* (1) Initialize structures with default values. */
181 	zimg_image_format_default(&src_format, ZIMG_API_VERSION);
182 	zimg_image_format_default(&dst_format, ZIMG_API_VERSION);
183 
184 	/* (2) Fill the format descriptors for the input and output images. */
185 	src_format.width = args->in_w;
186 	src_format.height = args->in_h;
187 	src_format.pixel_type = ZIMG_PIXEL_BYTE;
188 
189 	src_format.subsample_w = 1;
190 	src_format.subsample_h = 1;
191 
192 	src_format.color_family = ZIMG_COLOR_YUV;
193 
194 	dst_format.width = args->out_w;
195 	dst_format.height = args->out_h;
196 	dst_format.pixel_type = ZIMG_PIXEL_BYTE;
197 
198 	dst_format.subsample_w = 1;
199 	dst_format.subsample_h = 1;
200 
201 	dst_format.color_family = ZIMG_COLOR_YUV;
202 
203 	/* (3) Build the processing context. */
204 	if (!(graph = zimg_filter_graph_build(&src_format, &dst_format, 0))) {
205 		print_zimg_error();
206 		goto fail;
207 	}
208 	if ((ret = zimg_filter_graph_get_tmp_size(graph, &tmp_size))) {
209 		print_zimg_error();
210 		goto fail;
211 	}
212 
213 	printf("heap usage: %lu\n", (unsigned long)tmp_size);
214 
215 	/* (4) Allocate a temporary buffer for use during processing. If additional
216 	 * images need to be processed, the same temporary buffer can be used in
217 	 * subsequent calls.
218 	 */
219 	if (!(tmp = aligned_malloc(tmp_size, 32)))
220 		goto fail;
221 
222 	/* (5) Set the buffer pointers and strides. In this example, the input and
223 	 * output images are already in planar format, so the images are used
224 	 * directly as the scanline buffers. To indicate this, the buffer mask is
225 	 * set to ZIMG_BUFFER_MAX.
226 	 */
227 	for (p = 0; p < 3; ++p) {
228 		src_buf.plane[p].data = src_p[p];
229 		src_buf.plane[p].stride = src_stride[p];
230 		src_buf.plane[p].mask = ZIMG_BUFFER_MAX;
231 
232 		dst_buf.plane[p].data = dst_p[p];
233 		dst_buf.plane[p].stride = dst_stride[p];
234 		dst_buf.plane[p].mask = ZIMG_BUFFER_MAX;
235 	}
236 
237 	/* (6) Perform the image scaling operation. */
238 	if ((ret = zimg_filter_graph_process(graph, &src_buf, &dst_buf, tmp, 0, 0, 0, 0))) {
239 		print_zimg_error();
240 		goto fail;
241 	}
242 
243 	ret = 0;
244 fail:
245 	zimg_filter_graph_free(graph);
246 	aligned_free(tmp);
247 	return ret;
248 }
249 
execute(const struct Arguments * args)250 static int execute(const struct Arguments *args)
251 {
252 	void *src_handle = 0;
253 	void *dst_handle = 0;
254 	void *src_planes[3];
255 	void *dst_planes[3];
256 	const void *src_planes_const[3];
257 	const void *dst_planes_const[3];
258 	ptrdiff_t src_stride[3];
259 	ptrdiff_t dst_stride[3];
260 	int ret = 1;
261 
262 	if (!(src_handle = init_image(args->in_w, args->in_h, src_planes, src_stride)))
263 		goto fail;
264 	if (!(dst_handle = init_image(args->out_w, args->out_h, dst_planes, dst_stride)))
265 		goto fail;
266 
267 	src_planes_const[0] = src_planes[0];
268 	src_planes_const[1] = src_planes[1];
269 	src_planes_const[2] = src_planes[2];
270 
271 	dst_planes_const[0] = dst_planes[0];
272 	dst_planes_const[1] = dst_planes[1];
273 	dst_planes_const[2] = dst_planes[2];
274 
275 	if ((ret = read_image_from_file(args->inpath, args->in_w, args->in_h, src_planes, src_stride)))
276 		goto fail;
277 	if ((ret = process(args, src_planes_const, dst_planes, src_stride, dst_stride)))
278 		goto fail;
279 	if ((ret = write_image_to_file(args->outpath, args->out_w, args->out_h, dst_planes_const, dst_stride)))
280 		goto fail;
281 
282 	ret = 0;
283 fail:
284 	aligned_free(src_handle);
285 	aligned_free(dst_handle);
286 	return ret;
287 }
288 
main(int argc,char ** argv)289 int main(int argc, char **argv)
290 {
291 	struct Arguments args = { 0 };
292 	int ret;
293 
294 	if ((ret = argparse_parse(&program_def, &args, argc, argv)) < 0)
295 		return ret == ARGPARSE_HELP_MESSAGE ? 0 : ret;
296 
297 	if ((ret = execute(&args)))
298 		fprintf(stderr, "error: %d\n", ret);
299 
300 	return ret;
301 }
302