]> mj.ucw.cz Git - libucw.git/blob - images/image-tool.c
do not ignore incomplete blocks near the edges
[libucw.git] / images / image-tool.c
1 /*
2  *      Simple image manupulation utility
3  *
4  *      (c) 2006 Pavel Charvat <pchar@ucw.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU General Public License.
8  */
9
10 #include "lib/lib.h"
11 #include "lib/fastbuf.h"
12 #include "images/images.h"
13 #include "images/color.h"
14
15 #include <getopt.h>
16 #include <stdlib.h>
17 #include <fcntl.h>
18 #include <errno.h>
19 #include <stdio.h>
20
21 static void NONRET
22 usage(void)
23 {
24   fputs("\
25 Usage: image-tool [options] infile [outfile]\n\
26 \n\
27 -q --quiet               no progress messages\n\
28 -f --input-format        input image format (jpeg, gif, png)\n\
29 -F --output-format       output image format\n\
30 -s --size                force output dimensions (100x200)\n\
31 -b --fit-to-box          scale to fit the box (100x200)\n\
32 -c --colorspace          force output colorspace (Gray, GrayAlpha, RGB, RGBAlpha)\n\
33 -Q --jpeg-quality        JPEG quality (1..100)\n\
34 -g --background          background color (hexadecimal RRGGBB)\n\
35 -G --default-background  background applied only if the image contains no background info (RRGGBB, default=FFFFFF)\n\
36 -a --remove-alpha        remove alpha channel\n\
37 ", stderr);
38   exit(1);
39 }
40
41 static char *shortopts = "qf:F:s:b:c:Q:g:G:a";
42 static struct option longopts[] =
43 {
44   { "quiet",                    0, 0, 'q' },
45   { "input-format",             0, 0, 'f' },
46   { "output-format",            0, 0, 'F' },
47   { "size",                     0, 0, 's' },
48   { "fit-to-box",               0, 0, 'b' },
49   { "colorspace",               0, 0, 'c' },
50   { "jpeg-quality",             0, 0, 'Q' },
51   { "background",               0, 0, 'g' },
52   { "default-background",       0, 0, 'G' },
53   { "remove-alpha",             0, 0, 'a' },
54   { NULL,                       0, 0, 0 }
55 };
56                                                           
57 static uns verbose = 1;
58 static byte *input_file_name;
59 static enum image_format input_format;
60 static byte *output_file_name;
61 static enum image_format output_format;
62 static uns cols;
63 static uns rows;
64 static uns fit_to_box;
65 static uns channels_format;
66 static uns jpeg_quality;
67 static struct color background_color;
68 static struct color default_background_color;
69 static uns remove_alpha;
70
71 static void
72 parse_color(struct color *color, byte *s)
73 {
74   if (strlen(s) != 6)
75     usage();
76   errno = 0;
77   char *end;
78   long int v = strtol(s, &end, 16);
79   if (errno || *end || v < 0)
80     usage();
81   color_make_rgb(color, (v >> 16) & 255, (v >> 8) & 255, v & 255);
82 }
83
84 #define MSG(x...) do{ if (verbose) log(L_INFO, ##x); }while(0)
85
86 int
87 main(int argc, char **argv)
88 {
89   log_init(argv[0]);
90   int opt;
91   default_background_color = color_white;
92   while ((opt = getopt_long(argc, argv, shortopts, longopts, NULL)) >= 0)
93     switch (opt)
94       {
95         case 'q':
96           verbose = 0;
97           break;
98         case 'f':
99           if (!(input_format = image_extension_to_format(optarg)))
100             usage();
101           break;
102         case 'F':
103           if (!(output_format = image_extension_to_format(optarg)))
104             usage();
105           break;
106         case 's':
107           {
108             byte *r = strchr(optarg, 'x');
109             if (!r)
110               usage();
111             *r++ = 0;
112             if (!(cols = atoi(optarg)) || !(rows = atoi(r)))
113               usage();
114             fit_to_box = 0;
115             break;
116           }
117         case 'b':
118           {
119             byte *r = strchr(optarg, 'x');
120             if (!r)
121               usage();
122             *r++ = 0;
123             if (!(cols = atoi(optarg)) || !(rows = atoi(r)))
124               usage();
125             fit_to_box = 1;
126             break;
127           }
128         case 'c':
129           if (!(channels_format = image_name_to_channels_format(optarg)))
130             usage();
131           break;
132         case 'Q':
133           if (!(jpeg_quality = atoi(optarg)))
134             usage();
135           break;
136         case 'g':
137           parse_color(&background_color, optarg);
138           break;
139         case 'G':
140           parse_color(&default_background_color, optarg);
141           break;
142         case 'a':
143           remove_alpha++;
144           break;
145         default:
146           usage();
147       }
148
149   if (argc != optind + 1 && argc != optind + 2)
150     usage();
151   input_file_name = argv[optind++];
152   if (argc > optind)
153     output_file_name = argv[optind];
154   
155 #define TRY(x) do{ if (!(x)) die("Error: %s", it.err_msg); }while(0)
156   MSG("Initializing image library");
157   struct image_thread it;
158   struct image_io io;
159   image_thread_init(&it);
160   if (!image_io_init(&it, &io))
161     die("Cannot initialize image I/O (%s)", it.err_msg);
162
163   MSG("Reading %s", input_file_name);
164   io.fastbuf = bopen(input_file_name, O_RDONLY, 1 << 18);
165   io.format = input_format ? : image_file_name_to_format(input_file_name);
166   TRY(image_io_read_header(&io));
167   if (!output_file_name)
168     {
169       bclose(io.fastbuf);
170       printf("Format:      %s\n", image_format_to_extension(io.format) ? : (byte *)"?");
171       printf("Dimensions:  %dx%d\n", io.cols, io.rows);
172       printf("Colorspace:  %s\n", (io.flags & IMAGE_IO_HAS_PALETTE) ? (byte *)"Palette" : image_channels_format_to_name(io.flags & IMAGE_CHANNELS_FORMAT));
173       printf("NumColors:   %d\n", io.number_of_colors);
174       if (io.background_color.color_space)
175         {
176           byte rgb[3];
177           color_put_rgb(rgb, &io.background_color);
178           printf("Background:  %02x%02x%02x\n", rgb[0], rgb[1], rgb[2]);
179         }
180     }
181   else
182     {
183       MSG("%s %dx%d %s", image_format_to_extension(io.format) ? : (byte *)"?", io.cols, io.rows,
184           (io.flags & IMAGE_IO_HAS_PALETTE) ? (byte *)"Palette" : image_channels_format_to_name(io.flags & IMAGE_CHANNELS_FORMAT));
185       if (cols)
186         if (fit_to_box)
187           {
188             image_dimensions_fit_to_box(&io.cols, &io.rows, MIN(cols, 0xffff), MIN(rows, 0xffff), 0);
189           }
190         else
191           {
192             io.cols = cols;
193             io.rows = rows;
194           }
195       if (background_color.color_space)
196         io.background_color = background_color;
197       else if (!io.background_color.color_space)
198         io.background_color = default_background_color;
199       if (remove_alpha)
200         io.flags &= ~IMAGE_ALPHA;
201       if (channels_format)
202         io.flags = io.flags & ~IMAGE_PIXEL_FORMAT | channels_format;
203       if (!(io.flags & IMAGE_ALPHA))
204         io.flags |= IMAGE_IO_USE_BACKGROUND;
205       if (jpeg_quality)
206         io.jpeg_quality = jpeg_quality;
207       TRY(image_io_read_data(&io, 0));
208       bclose(io.fastbuf);
209       MSG("Writing %s", output_file_name);
210       io.fastbuf = bopen(output_file_name, O_WRONLY | O_CREAT | O_TRUNC, 1 << 18);
211       io.format = output_format ? : image_file_name_to_format(output_file_name);
212       MSG("%s %dx%d %s", image_format_to_extension(io.format) ? : (byte *)"?", io.cols, io.rows,
213           image_channels_format_to_name(io.flags & IMAGE_CHANNELS_FORMAT));
214       TRY(image_io_write(&io));
215       bclose(io.fastbuf);
216     }
217   
218   image_io_cleanup(&io);
219   image_thread_cleanup(&it);
220   MSG("Done.");
221   return 0;
222 }