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