Commit | Line | Data |
---|---|---|
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... */ | |
66 | extern void Log(const char *format, ...); | |
67 | # undef fprintf | |
68 | # define fprintf(S, ...) Log(__VA_ARGS__) | |
69 | #endif | |
70 | ||
71 | extern char *progname; | |
72 | ||
73 | static Bool | |
74 | bigendian (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 | */ | |
86 | static XImage * | |
87 | make_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 | */ | |
230 | static XImage * | |
231 | make_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 | ||
289 | typedef struct { | |
290 | const unsigned char *buf; | |
291 | png_size_t siz, ptr; | |
292 | } png_read_closure; | |
293 | ||
294 | static void | |
295 | png_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 | */ | |
307 | static XImage * | |
308 | make_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 | ||
491 | static XImage * | |
492 | make_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 | */ | |
505 | static void | |
506 | decode_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 | */ | |
526 | static Pixmap | |
527 | make_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 | */ | |
643 | static void | |
644 | flip_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 | ||
665 | Pixmap | |
666 | image_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 | ||
675 | Pixmap | |
676 | file_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 | */ | |
690 | XImage * | |
691 | image_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 | ||
700 | XImage * | |
701 | file_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 | } |