1 
2 /****************************************************************************
3  *
4  * MODULE:       ximgview
5  * AUTHOR(S):    Glynn Clements
6  * PURPOSE:      View BMP images from the PNG driver
7  * COPYRIGHT:    (C) 2007 Glynn Clements
8  *
9  *               This program is free software under the GNU General Public
10  *               License (>=v2). Read the file COPYING that comes with GRASS
11  *               for details.
12  *
13  *****************************************************************************/
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <unistd.h>
18 #include <string.h>
19 #include <signal.h>
20 #include <errno.h>
21 
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <sys/mman.h>
25 #include <sys/stat.h>
26 #include <sys/time.h>
27 #include <sys/types.h>
28 
29 #include <X11/Xlib.h>
30 #include <X11/Xutil.h>
31 
32 #include <grass/gis.h>
33 #include <grass/glocale.h>
34 
35 #define HEADER_SIZE 64
36 
37 Display *dpy;
38 int scrn;
39 Window grwin;
40 XWindowAttributes xwa;
41 
42 static int evmask = ExposureMask | StructureNotifyMask;
43 static int w_width, w_height;
44 static int i_width, i_height;
45 static unsigned long last;
46 static double fraction;
47 
48 static void *imgbuf;
49 static void *xbuf;
50 static XImage *ximg;
51 static GC gc;
52 
53 extern Colormap InitColorTableFixed(Colormap cmap);
54 extern unsigned long find_color(unsigned int r, unsigned int g,
55 				unsigned int b);
56 
create_window(void)57 static void create_window(void)
58 {
59     XSetWindowAttributes xswa;
60     Colormap fixedcmap;
61 
62     dpy = XOpenDisplay(NULL);
63     if (!dpy)
64 	G_fatal_error(_("Unable to open display"));
65 
66     scrn = DefaultScreen(dpy);
67 
68     xswa.event_mask = evmask;
69     xswa.backing_store = NotUseful;
70     xswa.background_pixel = BlackPixel(dpy, scrn);
71 
72     grwin = XCreateWindow(dpy, RootWindow(dpy, scrn),
73 			  0, 0,
74 			  800, 600,
75 			  0,
76 			  DefaultDepth(dpy, scrn),
77 			  InputOutput,
78 			  DefaultVisual(dpy, scrn),
79 			  CWEventMask | CWBackingStore | CWBackPixel, &xswa);
80 
81     XMapWindow(dpy, grwin);
82 
83     if (!XGetWindowAttributes(dpy, grwin, &xwa))
84 	G_fatal_error(_("Unable to get window attributes"));
85 
86     fixedcmap = InitColorTableFixed(DefaultColormap(dpy, scrn));
87 
88     XSetWindowColormap(dpy, grwin, fixedcmap);
89 
90     gc = XCreateGC(dpy, grwin, 0UL, NULL);
91 
92     xbuf = G_malloc(i_width * i_height * 4);
93     ximg = XCreateImage(dpy, xwa.visual, xwa.depth, ZPixmap,
94 			0, xbuf, i_width, i_height, 32, 0);
95 
96     w_width = xwa.width;
97     w_height = xwa.height;
98 
99     XFlush(dpy);
100 }
101 
draw(void)102 static void draw(void)
103 {
104     int x0 = (w_width - i_width) / 2;
105     int y0 = (w_height - i_height) / 2;
106     const unsigned char *p = imgbuf;
107     int row, col;
108 
109     for (row = 0; row < i_height; row++) {
110 	for (col = 0; col < i_width; col++) {
111 	    unsigned char b = *p++;
112 	    unsigned char g = *p++;
113 	    unsigned char r = *p++;
114 	    unsigned char a = *p++;
115 	    unsigned long c = find_color(r, g, b);
116 
117 	    XPutPixel(ximg, col, row, c);
118 	}
119     }
120 
121     XPutImage(dpy, grwin, gc, ximg, 0, 0, x0, y0, i_width, i_height);
122     XSync(dpy, False);
123 }
124 
redraw(void)125 static void redraw(void)
126 {
127     struct timeval tv0, tv1;
128 
129     gettimeofday(&tv0, NULL);
130 
131     draw();
132 
133     gettimeofday(&tv1, NULL);
134     last = (tv1.tv_sec - tv0.tv_sec) * 1000000L + (tv1.tv_usec - tv0.tv_usec);
135 }
136 
dummy_handler(int sig)137 static void dummy_handler(int sig)
138 {
139 }
140 
main_loop(void)141 static void main_loop(void)
142 {
143     int xfd = ConnectionNumber(dpy);
144     struct sigaction act;
145 
146     act.sa_handler = &dummy_handler;
147     sigemptyset(&act.sa_mask);
148     act.sa_flags = 0;
149     sigaction(SIGUSR1, &act, NULL);
150 
151     for (;;) {
152 	fd_set waitset;
153 	struct timeval tv;
154 	unsigned long delay;
155 
156 	while (XPending(dpy) > 0) {
157 	    XEvent event;
158 
159 	    XNextEvent(dpy, &event);
160 
161 	    switch (event.type) {
162 	    case Expose:
163 		draw();
164 		break;
165 	    case ConfigureNotify:
166 		w_width = event.xconfigure.width;
167 		w_height = event.xconfigure.height;
168 		break;
169 	    }
170 	}
171 
172 	if (fraction > 0.001)
173 	    delay = (unsigned long)(last / fraction);
174 
175 	tv.tv_sec = delay / 1000000;
176 	tv.tv_usec = delay % 1000000;
177 
178 	FD_ZERO(&waitset);
179 	FD_SET(xfd, &waitset);
180 	errno = 0;
181 	if (select(FD_SETSIZE, &waitset, NULL, NULL, &tv) < 0 && errno != EINTR)
182 	    continue;
183 
184 	if (!FD_ISSET(xfd, &waitset) || errno == EINTR)
185 	    redraw();
186     }
187 }
188 
get_2(const unsigned char ** q)189 static unsigned int get_2(const unsigned char **q)
190 {
191     const unsigned char *p = *q;
192     unsigned int n = (p[0] << 0) | (p[1] << 8);
193 
194     *q += 2;
195     return n;
196 }
197 
get_4(const unsigned char ** q)198 static unsigned int get_4(const unsigned char **q)
199 {
200     const unsigned char *p = *q;
201     unsigned int n = (p[0] << 0) | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
202 
203     *q += 4;
204     return n;
205 }
206 
read_bmp_header(const unsigned char * p)207 static int read_bmp_header(const unsigned char *p)
208 {
209     int size;
210 
211     if (*p++ != 'B')
212 	return 0;
213     if (*p++ != 'M')
214 	return 0;
215 
216     size = get_4(&p);
217 
218     get_4(&p);
219 
220     if (get_4(&p) != HEADER_SIZE)
221 	return 0;
222 
223     if (get_4(&p) != 40)
224 	return 0;
225 
226     i_width = get_4(&p);
227     i_height = -get_4(&p);
228 
229     get_2(&p);
230     if (get_2(&p) != 32)
231 	return 0;
232 
233     if (get_4(&p) != 0)
234 	return 0;
235     if (get_4(&p) != i_width * i_height * 4)
236 	return 0;
237 
238     if (size != HEADER_SIZE + i_width * i_height * 4)
239 	return 0;
240 
241     get_4(&p);
242     get_4(&p);
243     get_4(&p);
244     get_4(&p);
245 
246     return 1;
247 }
248 
map_file(const char * filename)249 static void map_file(const char *filename)
250 {
251     char header[HEADER_SIZE];
252     size_t size;
253     void *ptr;
254     int fd;
255 
256     fd = open(filename, O_RDONLY);
257     if (fd < 0)
258 	G_fatal_error(_("Unable to open image file"));
259 
260     if (read(fd, header, sizeof(header)) != sizeof(header))
261 	G_fatal_error(_("Unable to read BMP header"));
262 
263     if (!read_bmp_header(header))
264 	G_fatal_error(_("Invalid BMP header"));
265 
266     size = HEADER_SIZE + i_width * i_height * 4;
267 
268     ptr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, (off_t) 0);
269     if (ptr == MAP_FAILED)
270 	G_fatal_error(_("Unable to map image file"));
271 
272     imgbuf = (char *)ptr + HEADER_SIZE;
273 
274     close(fd);
275 }
276 
main(int argc,char ** argv)277 int main(int argc, char **argv)
278 {
279     struct GModule *module;
280     struct
281     {
282 	struct Option *image, *percent;
283     } opt;
284     const char *filename;
285     int percent;
286 
287     G_gisinit(argv[0]);
288 
289     module = G_define_module();
290     G_add_keyword(_("display"));
291     G_add_keyword(_("graphics"));
292     G_add_keyword(_("raster"));
293     G_add_keyword(_("vector"));
294     G_add_keyword(_("visualization"));
295     module->description = _("View BMP images from the PNG driver.");
296 
297     opt.image = G_define_standard_option(G_OPT_F_INPUT);
298     opt.image->key = "image";
299     opt.image->required = YES;
300     opt.image->gisprompt = "old_file,file,file";
301     opt.image->description = _("Image file");
302 
303     opt.percent = G_define_option();
304     opt.percent->key = "percent";
305     opt.percent->type = TYPE_INTEGER;
306     opt.percent->required = NO;
307     opt.percent->multiple = NO;
308     opt.percent->description = _("Percentage of CPU time to use");
309     opt.percent->answer = "10";
310 
311     if (G_parser(argc, argv))
312 	exit(EXIT_FAILURE);
313 
314     filename = opt.image->answer;
315     percent = atoi(opt.percent->answer);
316     fraction = percent / 100.0;
317 
318     map_file(filename);
319     create_window();
320     main_loop();
321 
322     XCloseDisplay(dpy);
323 
324     return EXIT_SUCCESS;
325 }
326