Added missing newline in NEDsim error message.
[screensavers] / screenhack / ximage-loader.c
CommitLineData
3144ee8a
AT
1/* ximage-loader.c --- converts image files or data to XImages or Pixmap.
2 * xscreensaver, Copyright (c) 1998-2020 Jamie Zawinski <jwz@jwz.org>
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
10 * implied warranty.
11 */
12
13#ifdef HAVE_CONFIG_H
14# include "config.h"
15#endif
16
17#include <stdlib.h>
18#include <stdio.h>
19#include <string.h>
20
21#ifdef HAVE_JWXYZ
22# include "jwxyz.h"
23#else
24# include <X11/Xlib.h>
25# include <X11/Xutil.h>
26#endif
27
28#include "ximage-loader.h"
29
30#if defined(HAVE_GDK_PIXBUF) || defined(HAVE_COCOA) || defined(HAVE_ANDROID)
31# undef HAVE_LIBPNG
32#endif
33
34#ifdef HAVE_COCOA
35# include "grabscreen.h" /* for osx_load_image_file() */
36#endif
37
38#ifdef HAVE_GDK_PIXBUF
39
40# if (__GNUC__ >= 4) /* Ignore useless warnings generated by GTK headers */
41# pragma GCC diagnostic push
42# pragma GCC diagnostic ignored "-Wlong-long"
43# pragma GCC diagnostic ignored "-Wvariadic-macros"
44# pragma GCC diagnostic ignored "-Wpedantic"
45# endif
46
47# include <gdk-pixbuf/gdk-pixbuf.h>
48# ifdef HAVE_GTK2
49# include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
50# else /* !HAVE_GTK2 */
51# include <gdk-pixbuf/gdk-pixbuf-xlib.h>
52# endif /* !HAVE_GTK2 */
53
54# if (__GNUC__ >= 4)
55# pragma GCC diagnostic pop
56# endif
57
58#endif /* HAVE_GDK_PIXBUF */
59
60#ifdef HAVE_LIBPNG
61# include <png.h>
62#endif
63
64#ifdef HAVE_ANDROID
65 /* So that debug output shows up in logcat... */
66extern void Log(const char *format, ...);
67# undef fprintf
68# define fprintf(S, ...) Log(__VA_ARGS__)
69#endif
70
71extern char *progname;
72
73static Bool
74bigendian (void)
75{
76 union { int i; char c[sizeof(int)]; } u;
77 u.i = 1;
78 return !u.c[0];
79}
80
81
82#ifdef HAVE_GDK_PIXBUF
83
84/* Loads the image to an XImage, RGBA -- GDK Pixbuf version.
85 */
86static XImage *
87make_ximage (Display *dpy, Visual *visual, const char *filename,
88 const unsigned char *image_data, unsigned long data_size)
89{
90 GdkPixbuf *pb;
91 static int initted = 0;
92# ifdef HAVE_GTK2
93 GError *gerr = NULL;
94# endif
95
96 if (!initted)
97 {
98# ifdef HAVE_GTK2
99# if !GLIB_CHECK_VERSION(2, 36 ,0)
100 g_type_init ();
101# endif
102# endif
103 if (dpy)
104 {
105 /* Turns out gdk-pixbuf works even if you don't have display
106 connection, which is good news for analogtv-cli. */
107 gdk_pixbuf_xlib_init (dpy, DefaultScreen (dpy));
108 xlib_rgb_init (dpy, DefaultScreenOfDisplay (dpy));
109 }
110 initted = 1;
111 }
112
113 if (filename)
114 {
115# ifdef HAVE_GTK2
116 pb = gdk_pixbuf_new_from_file (filename, &gerr);
117 if (!pb)
118 {
119 fprintf (stderr, "%s: %s\n", progname, gerr->message);
120 return 0;
121 }
122# else
123 pb = gdk_pixbuf_new_from_file (filename);
124 if (!pb)
125 {
126 fprintf (stderr, "%s: GDK unable to load %s: %s\n",
127 progname, filename, (gerr ? gerr->message : "?"));
128 return 0;
129 }
130# endif /* HAVE_GTK2 */
131 }
132 else
133 {
134# ifdef HAVE_GTK2
135 GInputStream *s =
136 g_memory_input_stream_new_from_data (image_data, data_size, 0);
137 pb = gdk_pixbuf_new_from_stream (s, 0, &gerr);
138
139 g_input_stream_close (s, NULL, NULL);
140 /* #### valgrind on xflame says there's a small leak in s? */
141 g_object_unref (s);
142
143 if (! pb)
144 {
145 /* fprintf (stderr, "%s: GDK unable to parse image data: %s\n",
146 progname, (gerr ? gerr->message : "?")); */
147 return 0;
148 }
149# else /* !HAVE_GTK2 */
150 fprintf (stderr, "%s: image loading not supported with GTK 1.x\n",
151 progname);
152 return 0;
153# endif /* !HAVE_GTK2 */
154 }
155
156 if (!pb) abort();
157
158 {
159 XImage *image;
160 int w = gdk_pixbuf_get_width (pb);
161 int h = gdk_pixbuf_get_height (pb);
162 guchar *row = gdk_pixbuf_get_pixels (pb);
163 int stride = gdk_pixbuf_get_rowstride (pb);
164 int chan = gdk_pixbuf_get_n_channels (pb);
165 int x, y;
166
167 image = XCreateImage (dpy, visual, 32, ZPixmap, 0, 0, w, h, 32, 0);
168 image->data = (char *) malloc(h * image->bytes_per_line);
169
170 /* Set the bit order in the XImage structure to whatever the
171 local host's native bit order is.
172 */
173 image->bitmap_bit_order =
174 image->byte_order =
175 (bigendian() ? MSBFirst : LSBFirst);
176
177 if (!image->data)
178 {
179 fprintf (stderr, "%s: out of memory (%d x %d)\n", progname, w, h);
180 return 0;
181 }
182
183 for (y = 0; y < h; y++)
184 {
185 guchar *i = row;
186 for (x = 0; x < w; x++)
187 {
188 unsigned long rgba = 0;
189 switch (chan) {
190 case 1:
191 rgba = ((0xFF << 24) |
192 (*i << 16) |
193 (*i << 8) |
194 *i);
195 i++;
196 break;
197 case 3:
198 rgba = ((0xFF << 24) |
199 (i[2] << 16) |
200 (i[1] << 8) |
201 i[0]);
202 i += 3;
203 break;
204 case 4:
205 rgba = ((i[3] << 24) |
206 (i[2] << 16) |
207 (i[1] << 8) |
208 i[0]);
209 i += 4;
210 break;
211 default:
212 abort();
213 break;
214 }
215 XPutPixel (image, x, y, rgba);
216 }
217 row += stride;
218 }
219
220 /* #### valgrind on xflame says there's a small leak in pb? */
221 g_object_unref (pb);
222 return image;
223 }
224}
225
226#elif defined(HAVE_JWXYZ) /* MacOS, iOS or Android */
227
228/* Loads the image to an XImage, RGBA -- MacOS, iOS or Android version.
229 */
230static XImage *
231make_ximage (Display *dpy, Visual *visual, const char *filename,
232 const unsigned char *image_data, unsigned long data_size)
233{
234 XImage *ximage = 0;
235
236 if (filename)
237 {
238# ifdef HAVE_COCOA /* MacOS */
239 XRectangle geom;
240 Screen *screen = DefaultScreenOfDisplay (dpy);
241 Window window = RootWindowOfScreen (screen);
242 XWindowAttributes xgwa;
243 XGetWindowAttributes (dpy, window, &xgwa);
244 Pixmap pixmap =
245 XCreatePixmap (dpy, window, xgwa.width, xgwa.height, xgwa.depth);
246 int x, y;
247
248 if (! osx_load_image_file (screen, window, pixmap, filename, &geom))
249 {
250 fprintf (stderr, "%s: %s failed\n", progname, filename);
251 return 0;
252 }
253
254 ximage = XGetImage (dpy, pixmap, geom.x, geom.y,
255 geom.width, geom.height,
256 ~0L, ZPixmap);
257 if (!ximage) abort();
258
259 /* Have to convert ABGR to RGBA */
260 for (y = 0; y < ximage->height; y++)
261 for (x = 0; x < ximage->width; x++)
262 {
263 unsigned long p = XGetPixel (ximage, x, y);
264 unsigned long a = (p >> 24) & 0xFF;
265 unsigned long b = (p >> 16) & 0xFF;
266 unsigned long g = (p >> 8) & 0xFF;
267 unsigned long r = (p >> 0) & 0xFF;
268 p = (r << 24) | (g << 16) | (b << 8) | (a << 0);
269 XPutPixel (ximage, x, y, p);
270 }
271
272 XFreePixmap (dpy, pixmap);
273
274# else /* !HAVE_COCOA -- iOS or Android. */
275 fprintf (stderr, "%s: image file loading not supported\n", progname);
276 return 0;
277# endif /* !HAVE_COCOA */
278 }
279 else
280 {
281 ximage = jwxyz_png_to_ximage (dpy, visual, image_data, data_size);
282 }
283
284 return ximage;
285}
286
287#elif defined(HAVE_LIBPNG)
288
289typedef struct {
290 const unsigned char *buf;
291 png_size_t siz, ptr;
292} png_read_closure;
293
294static void
295png_reader_fn (png_structp png_ptr, png_bytep buf, png_size_t siz)
296{
297 png_read_closure *r = png_get_io_ptr (png_ptr);
298 if (siz > r->siz - r->ptr)
299 png_error (png_ptr, "PNG internal read error");
300 memcpy (buf, r->buf + r->ptr, siz);
301 r->ptr += siz;
302}
303
304
305/* Loads the image to an XImage, RGBA -- libpng version.
306 */
307static XImage *
308make_ximage (Display *dpy, Visual *visual,
309 const char *filename, const unsigned char *image_data,
310 unsigned long data_size)
311{
312 XImage *image = 0;
313 png_structp png_ptr;
314 png_infop info_ptr;
315 png_infop end_info;
316 png_uint_32 width, height, channels;
317 int bit_depth, color_type, interlace_type;
318 FILE *fp = 0;
319
320 png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, 0, 0, 0);
321 if (!png_ptr) return 0;
322
323 info_ptr = png_create_info_struct (png_ptr);
324 if (!info_ptr)
325 {
326 png_destroy_read_struct (&png_ptr, 0, 0);
327 return 0;
328 }
329
330 end_info = png_create_info_struct (png_ptr);
331 if (!end_info)
332 {
333 png_destroy_read_struct (&png_ptr, &info_ptr, 0);
334 return 0;
335 }
336
337 if (setjmp (png_jmpbuf(png_ptr)))
338 {
339 png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
340 return 0;
341 }
342
343 if (filename)
344 {
345 fp = fopen (filename, "r");
346 if (! fp)
347 {
348 fprintf (stderr, "%s: unable to read %s\n", progname, filename);
349 return 0;
350 }
351 png_init_io (png_ptr, fp);
352 }
353 else
354 {
355 png_read_closure closure;
356 closure.buf = image_data;
357 closure.siz = data_size;
358 closure.ptr = 0;
359 png_set_read_fn (png_ptr, (void *) &closure, png_reader_fn);
360 }
361
362 png_read_info (png_ptr, info_ptr);
363 png_get_IHDR (png_ptr, info_ptr,
364 &width, &height, &bit_depth, &color_type,
365 &interlace_type, 0, 0);
366
367 png_set_strip_16 (png_ptr); /* Truncate 16 bits per component to 8 */
368 png_set_packing (png_ptr); /* Unpack to 1 pixel per byte */
369
370# if 0
371 if (color_type == PNG_COLOR_TYPE_PALETTE) /* Colormap to RGB */
372 png_set_palette_rgb (png_ptr);
373
374 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) /* Mono to 8bit */
375 png_set_gray_1_2_4_to_8 (png_ptr);
376# endif
377
378 if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) /* Fix weird alpha */
379 png_set_tRNS_to_alpha (png_ptr);
380
381 /* At least 8 bits deep */
382 if (color_type == PNG_COLOR_TYPE_PALETTE && bit_depth <= 8)
383 png_set_expand (png_ptr);
384
385 if (bit_depth == 8 && /* Convert RGB to RGBA */
386 (color_type == PNG_COLOR_TYPE_RGB ||
387 color_type == PNG_COLOR_TYPE_PALETTE))
388 png_set_filler (png_ptr, 0xFF, PNG_FILLER_AFTER);
389
390 /* Grayscale to color */
391 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
392 png_set_expand (png_ptr);
393
394
395 /* Convert graysale to color */
396 if (color_type == PNG_COLOR_TYPE_GRAY ||
397 color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
398 png_set_gray_to_rgb (png_ptr);
399
400# if 0
401 {
402 png_color_16 *bg;
403 if (png_get_bKGD (png_ptr, info_ptr, &bg))
404 png_set_background (png_ptr, bg, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
405 }
406# endif
407
408 /* Commit */
409 png_read_update_info (png_ptr, info_ptr);
410
411 channels = png_get_channels (png_ptr, info_ptr);
412
413 {
414 png_bytep *rows = png_malloc (png_ptr, height * sizeof(*rows));
415 int x, y;
416 for (y = 0; y < height; y++)
417 rows[y] = png_malloc (png_ptr, png_get_rowbytes (png_ptr, info_ptr));
418 png_read_image (png_ptr, rows);
419 png_read_end (png_ptr, info_ptr);
420
421 image = XCreateImage (dpy, visual, 32, ZPixmap, 0, 0,
422 width, height, 32, 0);
423 image->data = (char *) malloc (height * image->bytes_per_line);
424
425 /* Set the bit order in the XImage structure to whatever the
426 local host's native bit order is.
427 */
428 image->bitmap_bit_order =
429 image->byte_order =
430 (bigendian() ? MSBFirst : LSBFirst);
431
432 if (!image->data)
433 {
434 fprintf (stderr, "%s: out of memory (%lu x %lu)\n",
435 progname, (unsigned long)width, (unsigned long)height);
436 return 0;
437 }
438
439 for (y = 0; y < height; y++)
440 {
441 png_bytep i = rows[y];
442 for (x = 0; x < width; x++)
443 {
444 unsigned long rgba;
445 switch (channels) {
446 case 4:
447 rgba = ((i[3] << 24) |
448 (i[2] << 16) |
449 (i[1] << 8) |
450 i[0]);
451 break;
452 case 3:
453 rgba = ((0xFF << 24) |
454 (i[2] << 16) |
455 (i[1] << 8) |
456 i[0]);
457 break;
458 case 2:
459 rgba = ((i[1] << 24) |
460 (i[0] << 16) |
461 (i[0] << 8) |
462 i[0]);
463 break;
464 case 1:
465 rgba = ((0xFF << 24) |
466 (i[0] << 16) |
467 (i[0] << 8) |
468 i[0]);
469 break;
470 default:
471 abort();
472 }
473 XPutPixel (image, x, y, rgba);
474 i += channels;
475 }
476 png_free (png_ptr, rows[y]);
477 }
478
479 png_free (png_ptr, rows);
480 }
481
482 png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
483 if (fp) fclose (fp);
484
485 return image;
486}
487
488
489#else /* No image loaders! */
490
491static XImage *
492make_ximage (Display *dpy, Visual *visual,
493 const char *filename, const unsigned char *image_data,
494 unsigned long data_size)
495{
496 fprintf (stderr, "%s: no image loading support!\n", progname);
497 return 0;
498}
499
500#endif /* no loaders */
501
502
503/* Given a bitmask, returns the position and width of the field.
504 */
505static void
506decode_mask (unsigned long mask, unsigned long *pos_ret,
507 unsigned long *size_ret)
508{
509 int i;
510 for (i = 0; i < 32; i++)
511 if (mask & (1L << i))
512 {
513 int j = 0;
514 *pos_ret = i;
515 for (; i < 32; i++, j++)
516 if (! (mask & (1L << i)))
517 break;
518 *size_ret = j;
519 return;
520 }
521}
522
523
524/* Loads the image to a Pixmap and optional 1-bit mask.
525 */
526static Pixmap
527make_pixmap (Display *dpy, Window window,
528 const char *filename,
529 const unsigned char *image_data, unsigned long data_size,
530 int *width_ret, int *height_ret, Pixmap *mask_ret)
531{
532 XWindowAttributes xgwa;
533 XImage *in, *out, *mask = 0;
534 Pixmap pixmap;
535 XGCValues gcv;
536 GC gc;
537 int x, y;
538
539 unsigned long crpos=0, cgpos=0, cbpos=0, capos=0; /* bitfield positions */
540 unsigned long srpos=0, sgpos=0, sbpos=0;
541 unsigned long srmsk=0, sgmsk=0, sbmsk=0;
542 unsigned long srsiz=0, sgsiz=0, sbsiz=0;
543
544# ifdef HAVE_JWXYZ
545 // BlackPixel has alpha: 0xFF000000.
546 unsigned long black = BlackPixelOfScreen (DefaultScreenOfDisplay (dpy));
547#else
548 unsigned long black = 0;
549# endif
550
551 XGetWindowAttributes (dpy, window, &xgwa);
552
553 in = make_ximage (dpy, xgwa.visual, filename, image_data, data_size);
554 if (!in) return 0;
555
556 /* Create a new image in the depth and bit-order of the server. */
557 out = XCreateImage (dpy, xgwa.visual, xgwa.depth, ZPixmap, 0, 0,
558 in->width, in->height, 8, 0);
559
560 out->bitmap_bit_order = in->bitmap_bit_order;
561 out->byte_order = in->byte_order;
562
563 out->bitmap_bit_order = BitmapBitOrder (dpy);
564 out->byte_order = ImageByteOrder (dpy);
565
566 out->data = (char *) malloc (out->height * out->bytes_per_line);
567 if (!out->data) abort();
568
569 if (mask_ret)
570 {
571 mask = XCreateImage (dpy, xgwa.visual, 1, XYPixmap, 0, 0,
572 in->width, in->height, 8, 0);
573 mask->byte_order = in->byte_order;
574 mask->data = (char *) malloc (mask->height * mask->bytes_per_line);
575 }
576
577 /* Find the server's color masks.
578 */
579 srmsk = out->red_mask;
580 sgmsk = out->green_mask;
581 sbmsk = out->blue_mask;
582
583 if (!(srmsk && sgmsk && sbmsk)) abort(); /* No server color masks? */
584
585 decode_mask (srmsk, &srpos, &srsiz);
586 decode_mask (sgmsk, &sgpos, &sgsiz);
587 decode_mask (sbmsk, &sbpos, &sbsiz);
588
589 /* 'in' is RGBA in client endianness. Convert to what the server wants. */
590 if (bigendian())
591 crpos = 24, cgpos = 16, cbpos = 8, capos = 0;
592 else
593 crpos = 0, cgpos = 8, cbpos = 16, capos = 24;
594
595 for (y = 0; y < in->height; y++)
596 for (x = 0; x < in->width; x++)
597 {
598 unsigned long p = XGetPixel (in, x, y);
599 unsigned char a = (p >> capos) & 0xFF;
600 unsigned char b = (p >> cbpos) & 0xFF;
601 unsigned char g = (p >> cgpos) & 0xFF;
602 unsigned char r = (p >> crpos) & 0xFF;
603 XPutPixel (out, x, y, ((r << srpos) |
604 (g << sgpos) |
605 (b << sbpos) |
606 black));
607 if (mask)
608 XPutPixel (mask, x, y, (a ? 1 : 0));
609 }
610
611 XDestroyImage (in);
612 in = 0;
613
614 pixmap = XCreatePixmap (dpy, window, out->width, out->height, xgwa.depth);
615 gc = XCreateGC (dpy, pixmap, 0, &gcv);
616 XPutImage (dpy, pixmap, gc, out, 0, 0, 0, 0, out->width, out->height);
617 XFreeGC (dpy, gc);
618
619 if (mask)
620 {
621 Pixmap p2 = XCreatePixmap (dpy, window, mask->width, mask->height, 1);
622 gcv.foreground = 1;
623 gcv.background = 0;
624 gc = XCreateGC (dpy, p2, GCForeground|GCBackground, &gcv);
625 XPutImage (dpy, p2, gc, mask, 0, 0, 0, 0, mask->width, mask->height);
626 XFreeGC (dpy, gc);
627 XDestroyImage (mask);
628 mask = 0;
629 *mask_ret = p2;
630 }
631
632 if (width_ret) *width_ret = out->width;
633 if (height_ret) *height_ret = out->height;
634
635 XDestroyImage (out);
636
637 return pixmap;
638}
639
640
641/* Textures are upside down, so invert XImages before returning them.
642 */
643static void
644flip_ximage (XImage *ximage)
645{
646 char *data2, *in, *out;
647 int y;
648
649 if (!ximage) return;
650 data2 = malloc (ximage->bytes_per_line * ximage->height);
651 if (!data2) abort();
652 in = ximage->data;
653 out = data2 + ximage->bytes_per_line * (ximage->height - 1);
654 for (y = 0; y < ximage->height; y++)
655 {
656 memcpy (out, in, ximage->bytes_per_line);
657 in += ximage->bytes_per_line;
658 out -= ximage->bytes_per_line;
659 }
660 free (ximage->data);
661 ximage->data = data2;
662}
663
664
665Pixmap
666image_data_to_pixmap (Display *dpy, Window window,
667 const unsigned char *image_data, unsigned long data_size,
668 int *width_ret, int *height_ret,
669 Pixmap *mask_ret)
670{
671 return make_pixmap (dpy, window, 0, image_data, data_size,
672 width_ret, height_ret, mask_ret);
673}
674
675Pixmap
676file_to_pixmap (Display *dpy, Window window, const char *filename,
677 int *width_ret, int *height_ret,
678 Pixmap *mask_ret)
679{
680 return make_pixmap (dpy, window, filename, 0, 0,
681 width_ret, height_ret, mask_ret);
682}
683
684
685/* This XImage has RGBA data, which is what OpenGL code typically expects.
686 Also it is upside down: the origin is at the bottom left of the image.
687 X11 typically expects 0RGB as it has no notion of alpha, only 1-bit masks.
688 With X11 code, you should probably use the _pixmap routines instead.
689 */
690XImage *
691image_data_to_ximage (Display *dpy, Visual *visual,
692 const unsigned char *image_data,
693 unsigned long data_size)
694{
695 XImage *ximage = make_ximage (dpy, visual, 0, image_data, data_size);
696 flip_ximage (ximage);
697 return ximage;
698}
699
700XImage *
701file_to_ximage (Display *dpy, Visual *visual, const char *filename)
702{
703 XImage *ximage = make_ximage (dpy, visual, filename, 0, 0);
704 flip_ximage (ximage);
705 return ximage;
706}