1 /* sane-airscan image decoders test
2  *
3  * Copyright (C) 2019 and up by Alexander Pevzner (pzz@apevzner.com)
4  * See LICENSE for license terms and conditions
5  */
6 
7 #include "airscan.h"
8 
9 #include <errno.h>
10 #include <stdarg.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 
15 #include <png.h>
16 
17 /* save_file represents a PNG output file, used for
18  * saving decoded image
19  */
20 typedef struct {
21     const char      *name;     /* Output file name */
22     FILE            *fp;       /* Output file handle */
23     png_struct      *png_ptr;  /* Underlying libpng encoder */
24     png_info        *info_ptr; /* libpng info struct */
25 } save_file;
26 
27 /* Print error message and exit
28  */
29 void __attribute__((noreturn))
die(const char * format,...)30 die (const char *format, ...)
31 {
32     va_list ap;
33 
34     va_start(ap, format);
35     vprintf(format, ap);
36     printf("\n");
37     va_end(ap);
38 
39     exit(1);
40 }
41 
42 /* libpng write callback
43  */
44 void
png_write_fn(png_struct * png_ptr,png_bytep data,size_t size)45 png_write_fn (png_struct *png_ptr, png_bytep data, size_t size)
46 {
47     save_file *file = png_get_io_ptr(png_ptr);
48 
49     if (size != fwrite(data, 1, size, file->fp)) {
50         die("%s: %s", file->name, strerror(errno));
51     }
52 }
53 
54 /* libpng error callback
55  */
56 void
png_error_fn(png_struct * png_ptr,const char * message)57 png_error_fn (png_struct *png_ptr, const char *message)
58 {
59     save_file *file = png_get_error_ptr(png_ptr);
60     die("%s: %s", file->name, message);
61 }
62 
63 /* Open the save_file
64  */
65 save_file*
save_open(const char * name,const SANE_Parameters * params)66 save_open (const char *name, const SANE_Parameters *params)
67 {
68     save_file *save = mem_new(save_file, 1);
69     int       color_type;
70 
71     save->name = str_dup(name);
72     save->fp = fopen(name, "wb");
73     if (save->fp == NULL) {
74         die("%s: %s", name, strerror(errno));
75     }
76 
77     save->png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
78         NULL, NULL, NULL);
79     if (save->png_ptr == NULL) {
80         die("%s: png_create_write_struct() failed", name);
81     }
82 
83     png_set_write_fn(save->png_ptr, save, png_write_fn, NULL);
84     png_set_error_fn(save->png_ptr, save, png_error_fn, NULL);
85 
86     save->info_ptr = png_create_info_struct(save->png_ptr);
87     if (save->info_ptr == NULL) {
88         die("%s: png_create_info_struct() failed", name);
89     }
90 
91     if (params->format == SANE_FRAME_GRAY) {
92         color_type = PNG_COLOR_TYPE_GRAY;
93     } else {
94         color_type = PNG_COLOR_TYPE_RGB;
95     }
96 
97     png_set_IHDR(save->png_ptr, save->info_ptr,
98         params->pixels_per_line, params->lines, params->depth,
99         color_type, PNG_INTERLACE_NONE,
100         PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
101 
102     png_set_sRGB(save->png_ptr, save->info_ptr, PNG_sRGB_INTENT_PERCEPTUAL);
103 
104     png_write_info(save->png_ptr, save->info_ptr);
105 
106     return save;
107 }
108 
109 /* Close the save file
110  */
111 void
save_close(save_file * save)112 save_close (save_file *save)
113 {
114     png_write_end(save->png_ptr, NULL);
115     png_destroy_write_struct(&save->png_ptr, &save->info_ptr);
116     fclose(save->fp);
117     mem_free((char*) save->name);
118     mem_free(save);
119 }
120 
121 /* Write a row of image data
122  */
123 void
save_write(save_file * save,void * data)124 save_write (save_file *save, void *data)
125 {
126     png_write_row(save->png_ptr, data);
127 }
128 
129 /* The main function
130  */
131 int
main(int argc,char ** argv)132 main (int argc, char **argv)
133 {
134     const char      *file, *ext;
135     image_decoder   *decoder = NULL;
136     FILE            *fp;
137     long            size;
138     int             rc;
139     void            *data, *line;
140     error           err;
141     SANE_Parameters params;
142     int             i;
143     save_file       *save;
144 
145     /* Parse command-line arguments */
146     if (argc != 2) {
147         die("usage: %s file", argv[0]);
148     }
149 
150     file = argv[1];
151     ext = strrchr(file, '.');
152     ext = ext ? ext + 1 : "";
153 
154     /* Create decoder */
155     if (!strcmp(ext, "jpeg") || !strcmp(ext, "jpg")) {
156         decoder = image_decoder_jpeg_new();
157     } else if (!strcmp(ext, "png")) {
158         decoder = image_decoder_png_new();
159     } else if (!strcmp(ext, "bmp")) {
160         decoder = image_decoder_bmp_new();
161     }
162 
163     if (decoder == NULL) {
164         die("can't guess image format");
165     }
166 
167     /* Load the file */
168     fp = fopen(file, "rb");
169     if (fp == NULL) {
170         die("%s: %s", file, strerror(errno));
171     }
172 
173     rc = fseek(fp, 0, SEEK_END);
174     if (rc < 0) {
175         die("%s: %s", file, strerror(errno));
176     }
177 
178     size = ftell(fp);
179     if (size < 0) {
180         die("%s: %s", file, strerror(errno));
181     }
182 
183     rc = fseek(fp, 0, SEEK_SET);
184     if (rc < 0) {
185         die("%s: %s", file, strerror(errno));
186     }
187 
188     data = mem_new(char, size);
189     if ((size_t) size != fread(data, 1, size, fp)) {
190         die("%s: read error", file);
191     }
192 
193     fclose(fp);
194 
195     /* Decode the image */
196     err = image_decoder_begin(decoder, data, size);
197     if (err != NULL) {
198         die("%s", err);
199     }
200 
201 
202     image_decoder_get_params(decoder, &params);
203     printf("format:      %s\n",   image_content_type(decoder));
204     printf("width:       %d\n",   params.pixels_per_line);
205     printf("height:      %d\n",   params.lines);
206     printf("bytes/line:  %d\n", params.bytes_per_line);
207     printf("bytes/pixel: %d\n", params.bytes_per_line / params.pixels_per_line);
208 
209     save = save_open("decoded.png", &params);
210 
211     line = mem_new(char, params.bytes_per_line);
212     for (i = 0; i < params.lines; i ++) {
213         err = image_decoder_read_line(decoder, line);
214         if (err != NULL) {
215             die("line %d: %s", i, err);
216         }
217 
218         save_write(save, line);
219     }
220 
221     mem_free(line);
222     save_close(save);
223 
224     return 0;
225 }
226 
227 /* vim:ts=8:sw=4:et
228  */
229