1/*
2 * Blit RGBA images to X with X(Shm)Images
3 */
4
5#ifndef _XOPEN_SOURCE
6# define _XOPEN_SOURCE 1
7#endif
8
9#ifndef _XOPEN_SOURCE
10# define _XOPEN_SOURCE 1
11#endif
12
13#define noSHOWINFO
14
15#include "mupdf/fitz.h"
16
17#include <string.h>
18#include <stdlib.h>
19#include <stdio.h>
20
21#include <X11/Xlib.h>
22#include <X11/Xutil.h>
23#include <sys/ipc.h>
24#include <sys/shm.h>
25#include <X11/extensions/XShm.h>
26
27extern int ffs(int);
28
29static int is_big_endian(void)
30{
31 static const int one = 1;
32 return *(char*)&one == 0;
33}
34
35typedef void (*ximage_convert_func_t)
36(
37 const unsigned char *src,
38 int srcstride,
39 unsigned char *dst,
40 int dststride,
41 int w,
42 int h
43 );
44
45#define POOLSIZE 4
46#define WIDTH 256
47#define HEIGHT 256
48
49enum {
50 ARGB8888,
51 BGRA8888,
52 RGBA8888,
53 ABGR8888,
54 RGB888,
55 BGR888,
56 RGB565,
57 RGB565_BR,
58 RGB555,
59 RGB555_BR,
60 BGR233,
61 UNKNOWN
62};
63
64#ifdef SHOWINFO
65static char *modename[] = {
66 "ARGB8888",
67 "BGRA8888",
68 "RGBA8888",
69 "ABGR8888",
70 "RGB888",
71 "BGR888",
72 "RGB565",
73 "RGB565_BR",
74 "RGB555",
75 "RGB555_BR",
76 "BGR233",
77 "UNKNOWN"
78};
79#endif
80
81extern ximage_convert_func_t ximage_convert_funcs[];
82
83static struct
84{
85 Display *display;
86 int screen;
87 XVisualInfo visual;
88 Colormap colormap;
89
90 int bitsperpixel;
91 int mode;
92
93 XColor rgbcube[256];
94
95 ximage_convert_func_t convert_func;
96
97 int useshm;
98 int shmcode;
99 XImage *pool[POOLSIZE];
100 /* MUST exist during the lifetime of the shared ximage according to the
101 xc/doc/hardcopy/Xext/mit-shm.PS.gz */
102 XShmSegmentInfo shminfo[POOLSIZE];
103 int lastused;
104} info;
105
106static XImage *
107createximage(Display *dpy, Visual *vis, XShmSegmentInfo *xsi, int depth, int w, int h)
108{
109 XImage *img;
110 Status status;
111
112 if (!XShmQueryExtension(dpy))
113 goto fallback;
114 if (!info.useshm)
115 goto fallback;
116
117 img = XShmCreateImage(dpy, vis, depth, ZPixmap, NULL, xsi, w, h);
118 if (!img)
119 {
120 fprintf(stderr, "warn: could not XShmCreateImage\n");
121 goto fallback;
122 }
123
124 xsi->shmid = shmget(IPC_PRIVATE,
125 img->bytes_per_line * img->height,
126 IPC_CREAT | 0777);
127 if (xsi->shmid < 0)
128 {
129 XDestroyImage(img);
130 fprintf(stderr, "warn: could not shmget\n");
131 goto fallback;
132 }
133
134 img->data = xsi->shmaddr = shmat(xsi->shmid, NULL, 0);
135 if (img->data == (char*)-1)
136 {
137 XDestroyImage(img);
138 fprintf(stderr, "warn: could not shmat\n");
139 goto fallback;
140 }
141
142 xsi->readOnly = False;
143 status = XShmAttach(dpy, xsi);
144 if (!status)
145 {
146 shmdt(xsi->shmaddr);
147 XDestroyImage(img);
148 fprintf(stderr, "warn: could not XShmAttach\n");
149 goto fallback;
150 }
151
152 XSync(dpy, False);
153
154 shmctl(xsi->shmid, IPC_RMID, NULL);
155
156 return img;
157
158fallback:
159 info.useshm = 0;
160
161 img = XCreateImage(dpy, vis, depth, ZPixmap, 0, NULL, w, h, 32, 0);
162 if (!img)
163 {
164 fprintf(stderr, "fail: could not XCreateImage");
165 abort();
166 }
167
168 img->data = malloc(h * img->bytes_per_line);
169 if (!img->data)
170 {
171 fprintf(stderr, "fail: could not malloc");
172 abort();
173 }
174
175 return img;
176}
177
178static void
179make_colormap(void)
180{
181 if (info.visual.class == PseudoColor && info.visual.depth == 8)
182 {
183 int i, r, g, b;
184 i = 0;
185 for (b = 0; b < 4; b++) {
186 for (g = 0; g < 8; g++) {
187 for (r = 0; r < 8; r++) {
188 info.rgbcube[i].pixel = i;
189 info.rgbcube[i].red = (r * 36) << 8;
190 info.rgbcube[i].green = (g * 36) << 8;
191 info.rgbcube[i].blue = (b * 85) << 8;
192 info.rgbcube[i].flags =
193 DoRed | DoGreen | DoBlue;
194 i++;
195 }
196 }
197 }
198 info.colormap = XCreateColormap(info.display,
199 RootWindow(info.display, info.screen),
200 info.visual.visual,
201 AllocAll);
202 XStoreColors(info.display, info.colormap, info.rgbcube, 256);
203 return;
204 }
205 else if (info.visual.class == TrueColor)
206 {
207 info.colormap = 0;
208 return;
209 }
210 fprintf(stderr, "Cannot handle visual class %d with depth: %d\n",
211 info.visual.class, info.visual.depth);
212 return;
213}
214
215static void
216select_mode(void)
217{
218 int byteorder;
219 int byterev;
220 unsigned long rm, gm, bm;
221 unsigned long rs, gs, bs;
222
223 byteorder = ImageByteOrder(info.display);
224 if (is_big_endian())
225 byterev = byteorder != MSBFirst;
226 else
227 byterev = byteorder != LSBFirst;
228
229 rm = info.visual.red_mask;
230 gm = info.visual.green_mask;
231 bm = info.visual.blue_mask;
232
233 rs = ffs(rm) - 1;
234 gs = ffs(gm) - 1;
235 bs = ffs(bm) - 1;
236
237#ifdef SHOWINFO
238 printf("ximage: mode %d/%d %08lx %08lx %08lx (%ld,%ld,%ld) %s%s\n",
239 info.visual.depth,
240 info.bitsperpixel,
241 rm, gm, bm, rs, gs, bs,
242 byteorder == MSBFirst ? "msb" : "lsb",
243 byterev ? " <swap>":"");
244#endif
245
246 info.mode = UNKNOWN;
247 if (info.bitsperpixel == 8) {
248 /* Either PseudoColor with BGR233 colormap, or TrueColor */
249 info.mode = BGR233;
250 }
251 else if (info.bitsperpixel == 16) {
252 if (rm == 0xF800 && gm == 0x07E0 && bm == 0x001F)
253 info.mode = !byterev ? RGB565 : RGB565_BR;
254 if (rm == 0x7C00 && gm == 0x03E0 && bm == 0x001F)
255 info.mode = !byterev ? RGB555 : RGB555_BR;
256 }
257 else if (info.bitsperpixel == 24) {
258 if (rs == 0 && gs == 8 && bs == 16)
259 info.mode = byteorder == MSBFirst ? RGB888 : BGR888;
260 if (rs == 16 && gs == 8 && bs == 0)
261 info.mode = byteorder == MSBFirst ? BGR888 : RGB888;
262 }
263 else if (info.bitsperpixel == 32) {
264 if (rs == 0 && gs == 8 && bs == 16)
265 info.mode = byteorder == MSBFirst ? ABGR8888 : RGBA8888;
266 if (rs == 8 && gs == 16 && bs == 24)
267 info.mode = byteorder == MSBFirst ? BGRA8888 : ARGB8888;
268 if (rs == 16 && gs == 8 && bs == 0)
269 info.mode = byteorder == MSBFirst ? ARGB8888 : BGRA8888;
270 if (rs == 24 && gs == 16 && bs == 8)
271 info.mode = byteorder == MSBFirst ? RGBA8888 : ABGR8888;
272 }
273
274#ifdef SHOWINFO
275 printf("ximage: RGBA8888 to %s\n", modename[info.mode]);
276#endif
277
278 /* select conversion function */
279 info.convert_func = ximage_convert_funcs[info.mode];
280}
281
282static int
283create_pool(void)
284{
285 int i;
286
287 info.lastused = 0;
288
289 for (i = 0; i < POOLSIZE; i++) {
290 info.pool[i] = NULL;
291 }
292
293 for (i = 0; i < POOLSIZE; i++) {
294 info.pool[i] = createximage(info.display,
295 info.visual.visual, &info.shminfo[i], info.visual.depth,
296 WIDTH, HEIGHT);
297 if (!info.pool[i]) {
298 return 0;
299 }
300 }
301
302 return 1;
303}
304
305static XImage *
306next_pool_image(void)
307{
308 if (info.lastused + 1 >= POOLSIZE) {
309 if (info.useshm)
310 XSync(info.display, False);
311 else
312 XFlush(info.display);
313 info.lastused = 0;
314 }
315 return info.pool[info.lastused ++];
316}
317
318static int
319ximage_error_handler(Display *display, XErrorEvent *event)
320{
321 /* Turn off shared memory images if we get an error from the MIT-SHM extension */
322 if (event->request_code == info.shmcode)
323 {
324 char buf[80];
325 XGetErrorText(display, event->error_code, buf, sizeof buf);
326 fprintf(stderr, "ximage: disabling shared memory extension: %s\n", buf);
327 info.useshm = 0;
328 return 0;
329 }
330
331 XSetErrorHandler(NULL);
332 return (XSetErrorHandler(ximage_error_handler))(display, event);
333}
334
335int
336ximage_init(Display *display, int screen, Visual *visual)
337{
338 XVisualInfo template;
339 XVisualInfo *visuals;
340 int nvisuals;
341 XPixmapFormatValues *formats;
342 int nformats;
343 int ok;
344 int i;
345 int major;
346 int event;
347 int error;
348
349 info.display = display;
350 info.screen = screen;
351 info.colormap = 0;
352
353 /* Get XVisualInfo for this visual */
354 template.visualid = XVisualIDFromVisual(visual);
355 visuals = XGetVisualInfo(display, VisualIDMask, &template, &nvisuals);
356 if (nvisuals != 1) {
357 fprintf(stderr, "Visual not found!\n");
358 XFree(visuals);
359 return 0;
360 }
361 memcpy(&info.visual, visuals, sizeof (XVisualInfo));
362 XFree(visuals);
363
364 /* Get appropriate PixmapFormat for this visual */
365 formats = XListPixmapFormats(info.display, &nformats);
366 for (i = 0; i < nformats; i++) {
367 if (formats[i].depth == info.visual.depth) {
368 info.bitsperpixel = formats[i].bits_per_pixel;
369 break;
370 }
371 }
372 XFree(formats);
373 if (i == nformats) {
374 fprintf(stderr, "PixmapFormat not found!\n");
375 return 0;
376 }
377
378 /* extract mode */
379 select_mode();
380
381 /* prepare colormap */
382 make_colormap();
383
384 /* identify code for MIT-SHM extension */
385 if (XQueryExtension(display, "MIT-SHM", &major, &event, &error) &&
386 XShmQueryExtension(display))
387 info.shmcode = major;
388
389 /* intercept errors looking for SHM code */
390 XSetErrorHandler(ximage_error_handler);
391
392 /* prepare pool of XImages */
393 info.useshm = 1;
394 ok = create_pool();
395 if (!ok)
396 return 0;
397
398#ifdef SHOWINFO
399 printf("ximage: %sPutImage\n", info.useshm ? "XShm" : "X");
400#endif
401
402 return 1;
403}
404
405int
406ximage_get_depth(void)
407{
408 return info.visual.depth;
409}
410
411Visual *
412ximage_get_visual(void)
413{
414 return info.visual.visual;
415}
416
417Colormap
418ximage_get_colormap(void)
419{
420 return info.colormap;
421}
422
423void
424ximage_blit(Drawable d, GC gc,
425 int dstx, int dsty,
426 unsigned char *srcdata,
427 int srcx, int srcy,
428 int srcw, int srch,
429 int srcstride)
430{
431 XImage *image;
432 int ax, ay;
433 int w, h;
434 unsigned char *srcptr;
435
436 for (ay = 0; ay < srch; ay += HEIGHT)
437 {
438 h = fz_mini(srch - ay, HEIGHT);
439 for (ax = 0; ax < srcw; ax += WIDTH)
440 {
441 w = fz_mini(srcw - ax, WIDTH);
442
443 image = next_pool_image();
444
445 srcptr = srcdata +
446 (ay + srcy) * srcstride +
447 (ax + srcx) * 4;
448
449 info.convert_func(srcptr, srcstride,
450 (unsigned char *) image->data,
451 image->bytes_per_line, w, h);
452
453 if (info.useshm)
454 {
455 XShmPutImage(info.display, d, gc, image,
456 0, 0, dstx + ax, dsty + ay,
457 w, h, False);
458 }
459 else
460 {
461 XPutImage(info.display, d, gc, image,
462 0, 0,
463 dstx + ax,
464 dsty + ay,
465 w, h);
466 }
467 }
468 }
469}
470
471/*
472 * Primitive conversion functions
473 */
474
475#ifndef restrict
476#ifndef _C99
477#ifdef __GNUC__
478#define restrict __restrict__
479#else
480#define restrict
481#endif
482#endif
483#endif
484
485#define PARAMS \
486 const unsigned char * restrict src, \
487 int srcstride, \
488 unsigned char * restrict dst, \
489 int dststride, \
490 int w, \
491 int h
492
493/*
494 * Convert byte:RGBA8888 to various formats
495 */
496
497static void
498ximage_convert_argb8888(PARAMS)
499{
500 int x, y;
501 for (y = 0; y < h; y++) {
502 for (x = 0; x < w; x ++) {
503 dst[x * 4 + 0] = src[x * 4 + 3]; /* a */
504 dst[x * 4 + 1] = src[x * 4 + 0]; /* r */
505 dst[x * 4 + 2] = src[x * 4 + 1]; /* g */
506 dst[x * 4 + 3] = src[x * 4 + 2]; /* b */
507 }
508 dst += dststride;
509 src += srcstride;
510 }
511}
512
513static void
514ximage_convert_bgra8888(PARAMS)
515{
516 int x, y;
517 for (y = 0; y < h; y++) {
518 for (x = 0; x < w; x++) {
519 dst[x * 4 + 0] = src[x * 4 + 2];
520 dst[x * 4 + 1] = src[x * 4 + 1];
521 dst[x * 4 + 2] = src[x * 4 + 0];
522 dst[x * 4 + 3] = src[x * 4 + 3];
523 }
524 dst += dststride;
525 src += srcstride;
526 }
527}
528
529static void
530ximage_convert_abgr8888(PARAMS)
531{
532 int x, y;
533 for (y = 0; y < h; y++) {
534 for (x = 0; x < w; x++) {
535 dst[x * 4 + 0] = src[x * 4 + 3];
536 dst[x * 4 + 1] = src[x * 4 + 2];
537 dst[x * 4 + 2] = src[x * 4 + 1];
538 dst[x * 4 + 3] = src[x * 4 + 0];
539 }
540 dst += dststride;
541 src += srcstride;
542 }
543}
544
545static void
546ximage_convert_rgba8888(PARAMS)
547{
548 int x, y;
549 for (y = 0; y < h; y++) {
550 for (x = 0; x < w; x++) {
551 ((unsigned *)dst)[x] = ((unsigned *)src)[x];
552 }
553 dst += dststride;
554 src += srcstride;
555 }
556}
557
558static void
559ximage_convert_bgr888(PARAMS)
560{
561 int x, y;
562 for (y = 0; y < h; y++) {
563 for (x = 0; x < w; x++) {
564 dst[3*x + 0] = src[4*x + 2];
565 dst[3*x + 1] = src[4*x + 1];
566 dst[3*x + 2] = src[4*x + 0];
567 }
568 src += srcstride;
569 dst += dststride;
570 }
571}
572
573static void
574ximage_convert_rgb888(PARAMS)
575{
576 int x, y;
577 for (y = 0; y < h; y++) {
578 for (x = 0; x < w; x++) {
579 dst[3*x + 0] = src[4*x + 0];
580 dst[3*x + 1] = src[4*x + 1];
581 dst[3*x + 2] = src[4*x + 2];
582 }
583 src += srcstride;
584 dst += dststride;
585 }
586}
587
588static void
589ximage_convert_rgb565(PARAMS)
590{
591 unsigned char r, g, b;
592 int x, y;
593 for (y = 0; y < h; y++) {
594 for (x = 0; x < w; x++) {
595 r = src[4*x + 0];
596 g = src[4*x + 1];
597 b = src[4*x + 2];
598 ((unsigned short *)dst)[x] =
599 ((r & 0xF8) << 8) |
600 ((g & 0xFC) << 3) |
601 (b >> 3);
602 }
603 src += srcstride;
604 dst += dststride;
605 }
606}
607
608static void
609ximage_convert_rgb565_br(PARAMS)
610{
611 unsigned char r, g, b;
612 int x, y;
613 for (y = 0; y < h; y++) {
614 for (x = 0; x < w; x++) {
615 r = src[4*x + 0];
616 g = src[4*x + 1];
617 b = src[4*x + 2];
618 /* final word is:
619 g4 g3 g2 b7 b6 b5 b4 b3 : r7 r6 r5 r4 r3 g7 g6 g5
620 */
621 ((unsigned short *)dst)[x] =
622 (r & 0xF8) |
623 ((g & 0xE0) >> 5) |
624 ((g & 0x1C) << 11) |
625 ((b & 0xF8) << 5);
626 }
627 src += srcstride;
628 dst += dststride;
629 }
630}
631
632static void
633ximage_convert_rgb555(PARAMS)
634{
635 unsigned char r, g, b;
636 int x, y;
637 for (y = 0; y < h; y++) {
638 for (x = 0; x < w; x++) {
639 r = src[4*x + 0];
640 g = src[4*x + 1];
641 b = src[4*x + 2];
642 ((unsigned short *)dst)[x] =
643 ((r & 0xF8) << 7) |
644 ((g & 0xF8) << 2) |
645 (b >> 3);
646 }
647 src += srcstride;
648 dst += dststride;
649 }
650}
651
652static void
653ximage_convert_rgb555_br(PARAMS)
654{
655 unsigned char r, g, b;
656 int x, y;
657 for (y = 0; y < h; y++) {
658 for (x = 0; x < w; x++) {
659 r = src[4*x + 0];
660 g = src[4*x + 1];
661 b = src[4*x + 2];
662 /* final word is:
663 g5 g4 g3 b7 b6 b5 b4 b3 : 0 r7 r6 r5 r4 r3 g7 g6
664 */
665 ((unsigned short *)dst)[x] =
666 ((r & 0xF8) >> 1) |
667 ((g & 0xC0) >> 6) |
668 ((g & 0x38) << 10) |
669 ((b & 0xF8) << 5);
670 }
671 src += srcstride;
672 dst += dststride;
673 }
674}
675
676static void
677ximage_convert_bgr233(PARAMS)
678{
679 unsigned char r, g, b;
680 int x,y;
681 for(y = 0; y < h; y++) {
682 for(x = 0; x < w; x++) {
683 r = src[4*x + 0];
684 g = src[4*x + 1];
685 b = src[4*x + 2];
686 /* format: b7 b6 g7 g6 g5 r7 r6 r5 */
687 dst[x] = (b&0xC0) | ((g>>2)&0x38) | ((r>>5)&0x7);
688 }
689 src += srcstride;
690 dst += dststride;
691 }
692}
693
694static void
695ximage_convert_generic(PARAMS)
696{
697 unsigned long rm, gm, bm, rs, gs, bs, rx, bx, gx;
698 unsigned long pixel;
699 unsigned long r, g, b;
700 int x, y;
701
702 rm = info.visual.red_mask;
703 gm = info.visual.green_mask;
704 bm = info.visual.blue_mask;
705
706 rs = ffs(rm) - 1;
707 gs = ffs(gm) - 1;
708 bs = ffs(bm) - 1;
709
710 rx = ffs(~(rm >> rs)) - 1;
711 gx = ffs(~(gm >> gs)) - 1;
712 bx = ffs(~(bm >> bs)) - 1;
713
714 for (y = 0; y < h; y++) {
715 for (x = 0; x < w; x++) {
716 r = src[4*x + 0];
717 g = src[4*x + 1];
718 b = src[4*x + 2];
719
720 /* adjust precision */
721 if (rx < 8) r >>= (8 - rx); else if (rx > 8) r <<= (rx - 8);
722 if (gx < 8) g >>= (8 - gx); else if (gx > 8) g <<= (gx - 8);
723 if (bx < 8) b >>= (8 - bx); else if (bx > 8) b <<= (bx - 8);
724
725 pixel = (r << rs) | (g << gs) | (b << bs);
726
727 if (ImageByteOrder(info.display) == MSBFirst) {
728 if (info.bitsperpixel > 16) {
729 dst[4*x + 0] = (pixel >> 24) & 0xFF;
730 dst[4*x + 1] = (pixel >> 16) & 0xFF;
731 dst[4*x + 2] = (pixel >> 8) & 0xFF;
732 dst[4*x + 3] = (pixel) & 0xFF;
733 } else if (info.bitsperpixel > 8) {
734 dst[2*x + 0] = (pixel >> 8) & 0xFF;
735 dst[2*x + 1] = (pixel) & 0xFF;
736 }
737 } else {
738 if (info.bitsperpixel > 16) {
739 dst[4*x + 0] = (pixel) & 0xFF;
740 dst[4*x + 1] = (pixel >> 8) & 0xFF;
741 dst[4*x + 2] = (pixel >> 16) & 0xFF;
742 dst[4*x + 3] = (pixel >> 24) & 0xFF;
743 } else if (info.bitsperpixel > 8) {
744 dst[2*x + 0] = (pixel) & 0xFF;
745 dst[2*x + 1] = (pixel >> 8) & 0xFF;
746 }
747 }
748 }
749 src += srcstride;
750 dst += dststride;
751 }
752}
753
754ximage_convert_func_t ximage_convert_funcs[] = {
755 ximage_convert_argb8888,
756 ximage_convert_bgra8888,
757 ximage_convert_rgba8888,
758 ximage_convert_abgr8888,
759 ximage_convert_rgb888,
760 ximage_convert_bgr888,
761 ximage_convert_rgb565,
762 ximage_convert_rgb565_br,
763 ximage_convert_rgb555,
764 ximage_convert_rgb555_br,
765 ximage_convert_bgr233,
766 ximage_convert_generic
767};
768