0
|
1 /*
|
|
2 * BPG decoder command line utility
|
|
3 *
|
|
4 * Copyright (c) 2014 Fabrice Bellard
|
|
5 *
|
|
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7 * of this software and associated documentation files (the "Software"), to deal
|
|
8 * in the Software without restriction, including without limitation the rights
|
|
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10 * copies of the Software, and to permit persons to whom the Software is
|
|
11 * furnished to do so, subject to the following conditions:
|
|
12 *
|
|
13 * The above copyright notice and this permission notice shall be included in
|
|
14 * all copies or substantial portions of the Software.
|
|
15 *
|
|
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
22 * THE SOFTWARE.
|
|
23 */
|
|
24
|
|
25 #include <stdlib.h>
|
|
26 #include <stdio.h>
|
|
27 #include <math.h>
|
|
28 #include <getopt.h>
|
|
29 #include <inttypes.h>
|
|
30
|
|
31 /* define it to include PNG output */
|
|
32 #define USE_PNG
|
|
33
|
|
34 #ifdef USE_PNG
|
|
35 #include <png.h>
|
|
36 #endif
|
|
37
|
|
38 #include "libbpg.h"
|
|
39
|
|
40 static void ppm_save(BPGDecoderContext *img, const char *filename)
|
|
41 {
|
|
42 BPGImageInfo img_info_s, *img_info = &img_info_s;
|
|
43 FILE *f;
|
|
44 int w, h, y;
|
|
45 uint8_t *rgb_line;
|
|
46
|
|
47 bpg_decoder_get_info(img, img_info);
|
|
48
|
|
49 w = img_info->width;
|
|
50 h = img_info->height;
|
|
51
|
|
52 rgb_line = malloc(3 * w);
|
|
53
|
|
54 f = fopen(filename,"wb");
|
|
55 if (!f) {
|
|
56 fprintf(stderr, "%s: I/O error\n", filename);
|
|
57 exit(1);
|
|
58 }
|
|
59
|
|
60 fprintf(f, "P6\n%d %d\n%d\n", w, h, 255);
|
|
61
|
|
62 bpg_decoder_start(img, BPG_OUTPUT_FORMAT_RGB24);
|
|
63 for (y = 0; y < h; y++) {
|
|
64 bpg_decoder_get_line(img, rgb_line);
|
|
65 fwrite(rgb_line, 1, w * 3, f);
|
|
66 }
|
|
67 fclose(f);
|
|
68
|
|
69 free(rgb_line);
|
|
70 }
|
|
71
|
|
72 #ifdef USE_PNG
|
|
73 static void png_write_data (png_structp png_ptr, png_bytep data,
|
|
74 png_size_t length)
|
|
75 {
|
|
76 FILE *f;
|
|
77 int ret;
|
|
78
|
|
79 f = png_get_io_ptr(png_ptr);
|
|
80 ret = fwrite(data, 1, length, f);
|
|
81 if (ret != length)
|
|
82 png_error(png_ptr, "PNG Write Error");
|
|
83 }
|
|
84
|
|
85 static void png_save(BPGDecoderContext *img, const char *filename, int bit_depth)
|
|
86 {
|
|
87 BPGImageInfo img_info_s, *img_info = &img_info_s;
|
|
88 FILE *f;
|
|
89 png_structp png_ptr;
|
|
90 png_infop info_ptr;
|
|
91 png_bytep row_pointer;
|
|
92 int y, color_type, bpp;
|
|
93 BPGDecoderOutputFormat out_fmt;
|
|
94
|
|
95 if (bit_depth != 8 && bit_depth != 16) {
|
|
96 fprintf(stderr, "Only bit_depth = 8 or 16 are supported for PNG output\n");
|
|
97 exit(1);
|
|
98 }
|
|
99
|
|
100 bpg_decoder_get_info(img, img_info);
|
|
101
|
|
102 f = fopen(filename, "wb");
|
|
103 if (!f) {
|
|
104 fprintf(stderr, "%s: I/O error\n", filename);
|
|
105 exit(1);
|
|
106 }
|
|
107
|
|
108 png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING,
|
|
109 NULL,
|
|
110 NULL, /* error */
|
|
111 NULL, /* warning */
|
|
112 NULL,
|
|
113 NULL,
|
|
114 NULL);
|
|
115 info_ptr = png_create_info_struct(png_ptr);
|
|
116 png_set_write_fn(png_ptr, (png_voidp)f, &png_write_data, NULL);
|
|
117
|
|
118 if (setjmp(png_jmpbuf(png_ptr)) != 0) {
|
|
119 fprintf(stderr, "PNG write error\n");
|
|
120 exit(1);
|
|
121 }
|
|
122
|
|
123 if (img_info->has_alpha)
|
|
124 color_type = PNG_COLOR_TYPE_RGB_ALPHA;
|
|
125 else
|
|
126 color_type = PNG_COLOR_TYPE_RGB;
|
|
127
|
|
128 png_set_IHDR(png_ptr, info_ptr, img_info->width, img_info->height,
|
|
129 bit_depth, color_type, PNG_INTERLACE_NONE,
|
|
130 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
|
131
|
|
132 png_write_info(png_ptr, info_ptr);
|
|
133
|
|
134 #if __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__
|
|
135 if (bit_depth == 16) {
|
|
136 png_set_swap(png_ptr);
|
|
137 }
|
|
138 #endif
|
|
139
|
|
140 if (bit_depth == 16) {
|
|
141 if (img_info->has_alpha)
|
|
142 out_fmt = BPG_OUTPUT_FORMAT_RGBA64;
|
|
143 else
|
|
144 out_fmt = BPG_OUTPUT_FORMAT_RGB48;
|
|
145 } else {
|
|
146 if (img_info->has_alpha)
|
|
147 out_fmt = BPG_OUTPUT_FORMAT_RGBA32;
|
|
148 else
|
|
149 out_fmt = BPG_OUTPUT_FORMAT_RGB24;
|
|
150 }
|
|
151
|
|
152 bpg_decoder_start(img, out_fmt);
|
|
153
|
|
154 bpp = (3 + img_info->has_alpha) * (bit_depth / 8);
|
|
155 row_pointer = (png_bytep)png_malloc(png_ptr, img_info->width * bpp);
|
|
156 for (y = 0; y < img_info->height; y++) {
|
|
157 bpg_decoder_get_line(img, row_pointer);
|
|
158 png_write_row(png_ptr, row_pointer);
|
|
159 }
|
|
160 png_free(png_ptr, row_pointer);
|
|
161
|
|
162 png_write_end(png_ptr, NULL);
|
|
163
|
|
164 png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
165
|
|
166 fclose(f);
|
|
167 }
|
|
168 #endif /* USE_PNG */
|
|
169
|
|
170 static void bpg_show_info(const char *filename, int show_extensions)
|
|
171 {
|
|
172 uint8_t *buf;
|
|
173 int buf_len, ret, buf_len_max;
|
|
174 FILE *f;
|
|
175 BPGImageInfo p_s, *p = &p_s;
|
|
176 BPGExtensionData *first_md, *md;
|
|
177 static const char *format_str[6] = {
|
|
178 "Gray",
|
|
179 "4:2:0",
|
|
180 "4:2:2",
|
|
181 "4:4:4",
|
|
182 "4:2:0_video",
|
|
183 "4:2:2_video",
|
|
184 };
|
|
185 static const char *color_space_str[BPG_CS_COUNT] = {
|
|
186 "YCbCr",
|
|
187 "RGB",
|
|
188 "YCgCo",
|
|
189 "YCbCr_BT709",
|
|
190 "YCbCr_BT2020",
|
|
191 };
|
|
192 static const char *extension_tag_str[] = {
|
|
193 "Unknown",
|
|
194 "EXIF",
|
|
195 "ICC profile",
|
|
196 "XMP",
|
|
197 "Thumbnail",
|
|
198 "Animation control",
|
|
199 };
|
|
200
|
|
201 f = fopen(filename, "rb");
|
|
202 if (!f) {
|
|
203 fprintf(stderr, "Could not open %s\n", filename);
|
|
204 exit(1);
|
|
205 }
|
|
206
|
|
207 if (show_extensions) {
|
|
208 fseek(f, 0, SEEK_END);
|
|
209 buf_len_max = ftell(f);
|
|
210 fseek(f, 0, SEEK_SET);
|
|
211 } else {
|
|
212 /* if no extension are shown, just need the header */
|
|
213 buf_len_max = BPG_DECODER_INFO_BUF_SIZE;
|
|
214 }
|
|
215 buf = malloc(buf_len_max);
|
|
216 buf_len = fread(buf, 1, buf_len_max, f);
|
|
217
|
|
218 ret = bpg_decoder_get_info_from_buf(p, show_extensions ? &first_md : NULL,
|
|
219 buf, buf_len);
|
|
220 free(buf);
|
|
221 fclose(f);
|
|
222 if (ret < 0) {
|
|
223 fprintf(stderr, "Not a BPG image\n");
|
|
224 exit(1);
|
|
225 }
|
|
226 printf("size=%dx%d color_space=%s",
|
|
227 p->width, p->height,
|
|
228 p->format == BPG_FORMAT_GRAY ? "Gray" : color_space_str[p->color_space]);
|
|
229 if (p->has_w_plane) {
|
|
230 printf(" w_plane=%d", p->has_w_plane);
|
|
231 }
|
|
232 if (p->has_alpha) {
|
|
233 printf(" alpha=%d premul=%d",
|
|
234 p->has_alpha, p->premultiplied_alpha);
|
|
235 }
|
|
236 printf(" format=%s limited_range=%d bit_depth=%d animation=%d\n",
|
|
237 format_str[p->format],
|
|
238 p->limited_range,
|
|
239 p->bit_depth,
|
|
240 p->has_animation);
|
|
241
|
|
242 if (first_md) {
|
|
243 const char *tag_name;
|
|
244 printf("Extension data:\n");
|
|
245 for(md = first_md; md != NULL; md = md->next) {
|
|
246 if (md->tag <= 5)
|
|
247 tag_name = extension_tag_str[md->tag];
|
|
248 else
|
|
249 tag_name = extension_tag_str[0];
|
|
250 printf(" tag=%d (%s) length=%d\n",
|
|
251 md->tag, tag_name, md->buf_len);
|
|
252 }
|
|
253 bpg_decoder_free_extension_data(first_md);
|
|
254 }
|
|
255 }
|
|
256
|
|
257 static void help(void)
|
|
258 {
|
|
259 printf("BPG Image Decoder version " CONFIG_BPG_VERSION "\n"
|
|
260 "usage: bpgdec [options] infile\n"
|
|
261 "Options:\n"
|
|
262 "-o outfile.[ppm|png] set the output filename (default = out.png)\n"
|
|
263 "-b bit_depth PNG output only: use bit_depth per component (8 or 16, default = 8)\n"
|
|
264 "-i display information about the image\n");
|
|
265 exit(1);
|
|
266 }
|
|
267
|
|
268 int main(int argc, char **argv)
|
|
269 {
|
|
270 FILE *f;
|
|
271 BPGDecoderContext *img;
|
|
272 uint8_t *buf;
|
|
273 int buf_len, bit_depth, c, show_info;
|
|
274 const char *outfilename, *filename, *p;
|
|
275
|
|
276 outfilename = "out.png";
|
|
277 bit_depth = 8;
|
|
278 show_info = 0;
|
|
279 for(;;) {
|
|
280 c = getopt(argc, argv, "ho:b:i");
|
|
281 if (c == -1)
|
|
282 break;
|
|
283 switch(c) {
|
|
284 case 'h':
|
|
285 show_help:
|
|
286 help();
|
|
287 break;
|
|
288 case 'o':
|
|
289 outfilename = optarg;
|
|
290 break;
|
|
291 case 'b':
|
|
292 bit_depth = atoi(optarg);
|
|
293 break;
|
|
294 case 'i':
|
|
295 show_info = 1;
|
|
296 break;
|
|
297 default:
|
|
298 exit(1);
|
|
299 }
|
|
300 }
|
|
301
|
|
302 if (optind >= argc)
|
|
303 goto show_help;
|
|
304
|
|
305 filename = argv[optind++];
|
|
306
|
|
307 if (show_info) {
|
|
308 bpg_show_info(filename, 1);
|
|
309 return 0;
|
|
310 }
|
|
311
|
|
312 f = fopen(filename, "rb");
|
|
313 if (!f) {
|
|
314 fprintf(stderr, "Could not open %s\n", filename);
|
|
315 exit(1);
|
|
316 }
|
|
317
|
|
318 fseek(f, 0, SEEK_END);
|
|
319 buf_len = ftell(f);
|
|
320 fseek(f, 0, SEEK_SET);
|
|
321
|
|
322 buf = malloc(buf_len);
|
|
323 if (fread(buf, 1, buf_len, f) != buf_len) {
|
|
324 fprintf(stderr, "Error while reading file\n");
|
|
325 exit(1);
|
|
326 }
|
|
327
|
|
328 fclose(f);
|
|
329
|
|
330 img = bpg_decoder_open();
|
|
331
|
|
332 if (bpg_decoder_decode(img, buf, buf_len) < 0) {
|
|
333 fprintf(stderr, "Could not decode image\n");
|
|
334 exit(1);
|
|
335 }
|
|
336 free(buf);
|
|
337
|
|
338 #ifdef USE_PNG
|
|
339 p = strrchr(outfilename, '.');
|
|
340 if (p)
|
|
341 p++;
|
|
342
|
|
343 if (p && strcasecmp(p, "ppm") != 0) {
|
|
344 png_save(img, outfilename, bit_depth);
|
|
345 } else
|
|
346 #endif
|
|
347 {
|
|
348 ppm_save(img, outfilename);
|
|
349 }
|
|
350
|
|
351 bpg_decoder_close(img);
|
|
352
|
|
353 return 0;
|
|
354 }
|