2 /* Copyright (C) 1993, Robert Nation
3 * Copyright (C) 1999 Carsten Haitzler and various contributors (imlib2)
4 * Copyright (C) 2002 Olivier Chapuis */
5 /* This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * The png loader and PImageRGBtoPixel are from imlib2. The code is from raster
23 * (Carsten Haitzler) <raster@rasterman.com> <raster@valinux.com>
27 /* ---------------------------- included header files ---------------------- */
42 #include "PictureUtils.h"
44 #include "ColorUtils.h"
48 #include "FRenderInit.h"
52 /* ---------------------------- local definitions -------------------------- */
53 #define FIMAGE_CMD_ARGS \
54 Display *dpy, char *path, CARD32 **argb_data, int *width, int *height
56 #define FIMAGE_PASS_ARGS \
57 dpy, path, argb_data, width, height
59 typedef struct PImageLoader
63 int (*func
)(FIMAGE_CMD_ARGS
);
69 /* ---------------------------- local macros ------------------------------- */
71 /* ---------------------------- imports ------------------------------------ */
73 /* ---------------------------- included code files ------------------------ */
75 /* ---------------------------- local types -------------------------------- */
77 /* ---------------------------- forward declarations ----------------------- */
79 static Bool
PImageLoadSvg(FIMAGE_CMD_ARGS
);
80 static Bool
PImageLoadPng(FIMAGE_CMD_ARGS
);
81 static Bool
PImageLoadXpm(FIMAGE_CMD_ARGS
);
83 /* ---------------------------- local variables ---------------------------- */
85 PImageLoader Loaders
[] =
87 { "xpm", PImageLoadXpm
},
88 { "svg", PImageLoadSvg
},
89 { "png", PImageLoadPng
},
93 /* ---------------------------- exported variables (globals) --------------- */
95 /* ---------------------------- local functions ---------------------------- */
98 Bool
PImageLoadArgbDataFromFile(FIMAGE_CMD_ARGS
)
100 int done
= 0, i
= 0, tried
= -1;
106 if (strlen(path
) > 3)
108 ext
= path
+ strlen(path
) - 3;
110 /* first try to load by extension */
111 while (!done
&& ext
!= NULL
&& Loaders
[i
].extension
!= NULL
)
113 if (StrEquals(Loaders
[i
].extension
, ext
))
115 if (Loaders
[i
].func(FIMAGE_PASS_ARGS
))
126 while (Loaders
[i
].extension
!= NULL
)
128 if (i
!= tried
&& Loaders
[i
].func(FIMAGE_PASS_ARGS
))
144 Bool
PImageLoadSvg(FIMAGE_CMD_ARGS
)
146 char *allocated_path
;
149 FRsvgDimensionData dim
;
151 Fcairo_surface_t
*surface
;
167 Bool transpose
= False
;
168 unsigned char a_value
;
169 unsigned char r_value
;
170 unsigned char g_value
;
171 unsigned char b_value
;
178 /* Separate rendering options from path */
179 render_opts
= path
= allocated_path
= safestrdup(path
);
180 if (*path
== ':' && (path
= strchr(path
+ 1, ':')))
191 if (!(rsvg
= Frsvg_handle_new_from_file(path
, NULL
)))
193 free(allocated_path
);
198 /* Parsing of rendering options */
202 switch (*render_opts
)
205 transpose
= !transpose
;
208 if (sscanf(render_opts
, "*%lf%n", &buf
, &i
) >= 1)
210 switch (render_opts
[i
])
227 if (sscanf(render_opts
, "/%lf%n", &buf
, &i
) >= 1 &&
230 switch (render_opts
[i
])
247 if (sscanf(render_opts
, "@%lf%n", &buf
, &i
) >= 1)
256 render_opts
, "%dx%n%d%n", &b1
, &j
, &b2
,
263 if (w
< 0 || (!w
&& render_opts
[0] == '-'))
267 if (h
< 0 || (!h
&& render_opts
[j
] == '-'))
273 sscanf(render_opts
, "%d%d%n", &b1
, &b2
, &i
) >=
280 render_opts
+= i
? i
: 1;
282 free(allocated_path
);
284 /* Keep the original aspect ratio when either w or h is 0 */
285 Frsvg_handle_get_dimensions(rsvg
, &dim
);
293 w
= h
* dim
.em
/ dim
.ex
;
297 h
= w
* dim
.ex
/ dim
.em
;
320 data
= (CARD32
*)safemalloc(w
* h
* sizeof(CARD32
));
321 memset(data
, 0, w
* h
* sizeof(CARD32
));
322 surface
= Fcairo_image_surface_create_for_data((unsigned char *)data
,
323 FCAIRO_FORMAT_ARGB32
, w
, h
, w
* sizeof(CARD32
));
324 if (Fcairo_surface_status(surface
) != FCAIRO_STATUS_SUCCESS
)
326 Fg_object_unref(FG_OBJECT(rsvg
));
330 Fcairo_surface_destroy(surface
);
335 cr
= Fcairo_create(surface
);
336 Fcairo_surface_destroy(surface
);
337 if (Fcairo_status(cr
) != FCAIRO_STATUS_SUCCESS
)
339 Fg_object_unref(FG_OBJECT(rsvg
));
349 /* Affine transformations ...
350 * mirroring, rotation, scaling and translation */
351 Fcairo_translate(cr
, .5 * w
, .5 * h
);
352 Fcairo_scale(cr
, w_sgn
, h_sgn
);
353 Fcairo_translate(cr
, dw
, dh
);
354 Fcairo_rotate(cr
, angle
* M_PI
/ 180);
355 Fcairo_scale(cr
, w_scale
, h_scale
);
356 Fcairo_translate(cr
, -.5, -.5);
357 Fcairo_scale(cr
, 1 / dim
.em
, 1 / dim
.ex
);
359 Frsvg_handle_render_cairo(rsvg
, cr
);
360 Fg_object_unref(FG_OBJECT(rsvg
));
363 /* Cairo gave us alpha prescaled RGB values, hence we need
364 * to rescale them for PImageCreatePixmapFromArgbData() */
365 for (i
= 0; i
< w
* h
; i
++)
367 if ((a_value
= (data
[i
] >> 030) & 0xff))
369 r_value
= ((data
[i
] >> 020) & 0xff) * 0xff / a_value
;
370 g_value
= ((data
[i
] >> 010) & 0xff) * 0xff / a_value
;
371 b_value
= (data
[i
] & 0xff) * 0xff / a_value
;
374 (a_value
<< 030) | (r_value
<< 020) |
375 (g_value
<< 010) | b_value
;
392 Bool
PImageLoadPng(FIMAGE_CMD_ARGS
)
394 Fpng_uint_32 w32
, h32
;
395 Fpng_structp Fpng_ptr
= NULL
;
396 Fpng_infop Finfo_ptr
= NULL
;
399 char hasa
= 0, hasg
= 0;
404 unsigned char buf
[FPNG_BYTES_TO_CHECK
];
405 unsigned char **lines
;
410 /* suppress compiler warning */
415 if (!(f
= fopen(path
, "rb")))
419 fread(buf
, 1, FPNG_BYTES_TO_CHECK
, f
);
420 if (!Fpng_check_sig(buf
, FPNG_BYTES_TO_CHECK
))
426 Fpng_ptr
= Fpng_create_read_struct(FPNG_LIBPNG_VER_STRING
,
433 Finfo_ptr
= Fpng_create_info_struct(Fpng_ptr
);
436 Fpng_destroy_read_struct(&Fpng_ptr
, NULL
, NULL
);
441 if (setjmp(Fpng_ptr
->jmpbuf
))
443 Fpng_destroy_read_struct(&Fpng_ptr
, &Finfo_ptr
, NULL
);
448 Fpng_init_io(Fpng_ptr
, f
);
449 Fpng_read_info(Fpng_ptr
, Finfo_ptr
);
451 Fpng_ptr
, Finfo_ptr
, (Fpng_uint_32
*) (&w32
),
452 (Fpng_uint_32
*) (&h32
), &bit_depth
, &color_type
,
453 &interlace_type
, NULL
, NULL
);
454 interlace_type
= 0; /* not used */
455 *width
= w
= (int) w32
;
456 *height
= h
= (int) h32
;
457 if (color_type
== FPNG_COLOR_TYPE_PALETTE
)
459 Fpng_set_expand(Fpng_ptr
);
461 if (Finfo_ptr
->color_type
== FPNG_COLOR_TYPE_RGB_ALPHA
)
465 if (Finfo_ptr
->color_type
== FPNG_COLOR_TYPE_GRAY_ALPHA
)
470 if (Finfo_ptr
->color_type
== FPNG_COLOR_TYPE_GRAY
)
475 Fpng_set_expand(Fpng_ptr
);
478 * thanks to mustapha for helping debug this on PPC Linux remotely by
479 * sending across screenshots all the time and me figuring out form them
480 * what the hell was up with the colors
481 * now png loading should work on big endian machines nicely */
482 #ifdef WORDS_BIGENDIAN
483 Fpng_set_swap_alpha(Fpng_ptr
);
484 Fpng_set_filler(Fpng_ptr
, 0xff, FPNG_FILLER_BEFORE
);
486 Fpng_set_bgr(Fpng_ptr
);
487 Fpng_set_filler(Fpng_ptr
, 0xff, FPNG_FILLER_AFTER
);
489 /* 16bit color -> 8bit color */
490 Fpng_set_strip_16(Fpng_ptr
);
491 /* pack all pixels to byte boundaires */
492 Fpng_set_packing(Fpng_ptr
);
493 if (Fpng_get_valid(Fpng_ptr
, Finfo_ptr
, FPNG_INFO_tRNS
))
495 Fpng_set_expand(Fpng_ptr
);
498 data
= (CARD32
*)safemalloc(w
* h
* sizeof(CARD32
));
499 lines
= (unsigned char **) safemalloc(h
* sizeof(unsigned char *));
503 Fpng_set_gray_to_rgb(Fpng_ptr
);
504 if (Fpng_get_bit_depth(Fpng_ptr
, Finfo_ptr
) < 8)
506 Fpng_set_gray_1_2_4_to_8(Fpng_ptr
);
509 for (i
= 0; i
< h
; i
++)
511 lines
[i
] = (unsigned char *)data
+ (i
* w
* sizeof(CARD32
));
513 Fpng_read_image(Fpng_ptr
, lines
);
514 Fpng_read_end(Fpng_ptr
, Finfo_ptr
);
515 Fpng_destroy_read_struct(&Fpng_ptr
, &Finfo_ptr
, (png_infopp
) NULL
);
529 Bool
PImageLoadXpm(FIMAGE_CMD_ARGS
)
532 FxpmColor
*xpm_color
;
541 #ifdef HAVE_SIGACTION
542 struct sigaction defaultHandler
;
543 struct sigaction originalHandler
;
545 RETSIGTYPE (*originalHandler
)(int);
552 memset(&xpm_im
, 0, sizeof(FxpmImage
));
554 #ifdef HAVE_SIGACTION
555 sigemptyset(&defaultHandler
.sa_mask
);
556 defaultHandler
.sa_flags
= 0;
557 defaultHandler
.sa_handler
= SIG_DFL
;
558 sigaction(SIGCHLD
, &defaultHandler
, &originalHandler
);
560 originalHandler
= signal(SIGCHLD
, SIG_DFL
);
564 rc
= FxpmReadFileToXpmImage(path
, &xpm_im
, NULL
);
566 #ifdef HAVE_SIGACTION
567 sigaction(SIGCHLD
, &originalHandler
, NULL
);
569 signal(SIGCHLD
, originalHandler
);
572 if (rc
!= FxpmSuccess
)
577 if (xpm_im
.ncolors
<= 0)
579 FxpmFreeXpmImage(&xpm_im
);
582 colors
= (CARD32
*)safemalloc(xpm_im
.ncolors
* sizeof(CARD32
));
583 for (i
=0; i
< xpm_im
.ncolors
; i
++)
585 xpm_color
= &xpm_im
.colorTable
[i
];
586 if (xpm_color
->c_color
)
588 visual_color
= xpm_color
->c_color
;
590 else if (xpm_color
->g_color
)
592 visual_color
= xpm_color
->g_color
;
594 else if (xpm_color
->g4_color
)
596 visual_color
= xpm_color
->g4_color
;
600 visual_color
= xpm_color
->m_color
;
602 if (XParseColor(dpy
, Pcmap
, visual_color
, &color
))
604 colors
[i
] = 0xff000000 |
605 ((color
.red
<< 8) & 0xff0000) |
606 ((color
.green
) & 0xff00) |
607 ((color
.blue
>> 8) & 0xff);
614 *width
= w
= xpm_im
.width
;
615 *height
= h
= xpm_im
.height
;
616 data
= (CARD32
*)safemalloc(w
* h
* sizeof(CARD32
));
617 for (i
=0; i
< w
* h
; i
++)
619 data
[i
] = colors
[xpm_im
.data
[i
]];
629 * copy image to server
633 Pixmap
PImageCreatePixmapFromFImage(Display
*dpy
, Window win
, FImage
*fimage
)
642 w
= fimage
->im
->width
;
643 h
= fimage
->im
->height
;
644 depth
= fimage
->im
->depth
;
645 pixmap
= XCreatePixmap(dpy
, win
, w
, h
, depth
);
648 gc
= PictureDefaultGC(dpy
, win
);
653 gc
= fvwmlib_XCreateGC(dpy
, pixmap
, 0, NULL
);
656 FPutFImage(dpy
, pixmap
, gc
, fimage
, 0, 0, 0, 0, w
, h
);
665 /* ---------------------------- interface functions ------------------------ */
669 * argb data to pixmaps
672 Bool
PImageCreatePixmapFromArgbData(
673 Display
*dpy
, Window win
, CARD32
*data
, int start
, int width
,
674 int height
, Pixmap
*pixmap
, Pixmap
*mask
, Pixmap
*alpha
,
675 int *nalloc_pixels
, Pixel
**alloc_pixels
, int *no_limit
,
676 FvwmPictureAttributes fpa
)
679 FImage
*m_fim
= NULL
;
680 FImage
*a_fim
= NULL
;
685 PictureImageColorAllocator
*pica
= NULL
;
686 int alpha_limit
= PICTURE_ALPHA_LIMIT
;
687 int alpha_depth
= FRenderGetAlphaDepth();
688 Bool have_mask
= False
;
689 Bool have_alpha
= False
;
692 dpy
, Pvisual
, (fpa
.mask
& FPAM_MONOCHROME
) ? 1 : Pdepth
,
693 ZPixmap
, width
, height
);
700 m_fim
= FCreateFImage(
701 dpy
, Pvisual
, 1, ZPixmap
, width
, height
);
703 if (alpha
&& !(fpa
.mask
& FPAM_NO_ALPHA
) && alpha_depth
)
706 a_fim
= FCreateFImage(
707 dpy
, Pvisual
, alpha_depth
, ZPixmap
, width
, height
);
709 if (!(fpa
.mask
& FPAM_MONOCHROME
))
711 c
.flags
= DoRed
| DoGreen
| DoBlue
;
712 pica
= PictureOpenImageColorAllocator(
713 dpy
, Pcmap
, width
, height
,
714 !!(fpa
.mask
& FPAM_NO_COLOR_LIMIT
),
715 !!(fpa
.mask
& FPAM_NO_ALLOC_PIXELS
),
716 !!(fpa
.mask
& FPAM_DITHER
),
720 for (j
= 0; j
< height
; j
++)
722 for (i
= 0; i
< width
; i
++, data
++)
724 a
= (*data
>> 030) & 0xff;
727 c
.red
= (*data
>> 16) & 0xff;
728 c
.green
= (*data
>> 8) & 0xff;
729 c
.blue
= (*data
) & 0xff;
732 PictureAllocColorImage(
733 dpy
, pica
, &c
, i
, j
);
734 XPutPixel(fim
->im
, i
, j
, c
.pixel
);
736 /* Brightness threshold */
737 else if ((0x99 * c
.red
+
739 0x3A * c
.blue
) >> 16)
741 XPutPixel(fim
->im
, i
, j
, 1);
745 XPutPixel(fim
->im
, i
, j
, 0);
749 XPutPixel(m_fim
->im
, i
, j
, 1);
752 else if (m_fim
!= NULL
)
754 XPutPixel(m_fim
->im
, i
, j
, 0);
759 XPutPixel(a_fim
->im
, i
, j
, a
);
760 if (a
> 0 && a
< 0xff)
769 PictureCloseImageColorAllocator(
770 dpy
, pica
, nalloc_pixels
, alloc_pixels
, no_limit
);
772 *pixmap
= PImageCreatePixmapFromFImage(dpy
, win
, fim
);
775 *alpha
= PImageCreatePixmapFromFImage(dpy
, win
, a_fim
);
779 *mask
= PImageCreatePixmapFromFImage(dpy
, win
, m_fim
);
781 FDestroyFImage(dpy
, fim
);
784 FDestroyFImage(dpy
, m_fim
);
788 FDestroyFImage(dpy
, a_fim
);
801 Bool
PImageLoadPixmapFromFile(
802 Display
*dpy
, Window win
, char *path
, Pixmap
*pixmap
, Pixmap
*mask
,
803 Pixmap
*alpha
, int *width
, int *height
, int *depth
,
804 int *nalloc_pixels
, Pixel
**alloc_pixels
,
805 int *no_limit
, FvwmPictureAttributes fpa
)
809 if (PImageLoadArgbDataFromFile(dpy
, path
, &data
, width
, height
))
811 *depth
= (fpa
.mask
& FPAM_MONOCHROME
) ? 1 : Pdepth
;
812 if (PImageCreatePixmapFromArgbData(
813 dpy
, win
, data
, 0, *width
, *height
, pixmap
, mask
,
814 alpha
, nalloc_pixels
, alloc_pixels
, no_limit
, fpa
))
822 /* Bitmap fallback */
825 dpy
, win
, path
, (unsigned int *)width
,
826 (unsigned int *)height
, pixmap
, NULL
, NULL
) ==
837 *width
= *height
= *depth
= 0;
838 if (nalloc_pixels
!= NULL
)
842 if (alloc_pixels
!= NULL
)
844 *alloc_pixels
= NULL
;
849 FvwmPicture
*PImageLoadFvwmPictureFromFile(
850 Display
*dpy
, Window win
, char *path
, FvwmPictureAttributes fpa
)
853 Pixmap pixmap
= None
;
856 int width
= 0, height
= 0;
857 int depth
= 0, no_limit
;
858 int nalloc_pixels
= 0;
859 Pixel
*alloc_pixels
= NULL
;
862 /* Remove any svg rendering options from real_path */
863 if (USE_SVG
&& *path
== ':' &&
864 (real_path
= strchr(path
+ 1, ':')))
872 if (!PImageLoadPixmapFromFile(
873 dpy
, win
, path
, &pixmap
, &mask
, &alpha
, &width
, &height
,
874 &depth
, &nalloc_pixels
, &alloc_pixels
, &no_limit
, fpa
))
879 p
= (FvwmPicture
*)safemalloc(sizeof(FvwmPicture
));
880 memset(p
, 0, sizeof(FvwmPicture
));
883 p
->fpa_mask
= fpa
.mask
;
885 setFileStamp(&p
->stamp
, real_path
);
892 p
->nalloc_pixels
= nalloc_pixels
;
893 p
->alloc_pixels
= alloc_pixels
;
894 p
->no_limit
= no_limit
;
898 Cursor
PImageLoadCursorFromFile(
899 Display
*dpy
, Window win
, char *path
, int x_hot
, int y_hot
)
909 /* First try the Xcursor loader (animated cursors) */
910 if ((fcis
= FcursorFilenameLoadImages(
911 path
, FcursorGetDefaultSize(dpy
))))
913 for (i
= 0; i
< fcis
->nimage
; i
++)
915 if (x_hot
< fcis
->images
[i
]->width
&& x_hot
>= 0 &&
916 y_hot
< fcis
->images
[i
]->height
&& y_hot
>= 0)
918 fcis
->images
[i
]->xhot
= x_hot
;
919 fcis
->images
[i
]->yhot
= y_hot
;
922 cursor
= FcursorImagesLoadCursor(dpy
, fcis
);
923 FcursorImagesDestroy(fcis
);
925 /* Get cursor data from the regular image loader */
926 else if (PImageLoadArgbDataFromFile(dpy
, path
, &data
, &width
, &height
))
930 FvwmPictureAttributes fpa
;
932 fpa
.mask
= FPAM_NO_ALPHA
| FPAM_MONOCHROME
;
934 /* Adjust the hot-spot if necessary */
936 x_hot
< 0 || x_hot
>= width
||
937 y_hot
< 0 || y_hot
>= height
)
942 memset(&xpm_im
, 0, sizeof(FxpmImage
));
943 memset(&xpm_info
, 0, sizeof(FxpmInfo
));
944 if (FxpmReadFileToXpmImage(path
, &xpm_im
, &xpm_info
)
947 if (xpm_info
.valuemask
& FxpmHotspot
)
949 x_hot
= xpm_info
.x_hotspot
;
950 y_hot
= xpm_info
.y_hotspot
;
952 FxpmFreeXpmImage(&xpm_im
);
953 FxpmFreeXpmInfo(&xpm_info
);
955 if (x_hot
< 0 || x_hot
>= width
)
959 if (y_hot
< 0 || y_hot
>= height
)
964 /* Use the Xcursor library to create the argb cursor */
965 if ((fci
= FcursorImageCreate(width
, height
)))
972 /* Xcursor expects alpha prescaled RGB values */
973 for (i
= 0; i
< width
* height
; i
++)
975 alpha
= ((data
[i
] >> 24) & 0xff);
976 red
= ((data
[i
] >> 16) & 0xff) * alpha
/0xff;
977 green
= ((data
[i
] >> 8) & 0xff) * alpha
/0xff;
978 blue
= ((data
[i
] ) & 0xff) * alpha
/0xff;
981 (alpha
<< 24) | (red
<< 16) |
988 fci
->pixels
= (FcursorPixel
*)data
;
989 cursor
= FcursorImageLoadCursor(dpy
, fci
);
990 FcursorImageDestroy(fci
);
992 /* Create monochrome cursor from argb data */
993 else if (PImageCreatePixmapFromArgbData(
994 dpy
, win
, data
, 0, width
, height
,
995 &src
, &msk
, 0, 0, 0, 0, fpa
))
999 c
[0].pixel
= GetColor(DEFAULT_CURSOR_FORE_COLOR
);
1000 c
[1].pixel
= GetColor(DEFAULT_CURSOR_BACK_COLOR
);
1001 XQueryColors(dpy
, Pcmap
, c
, 2);
1002 cursor
= XCreatePixmapCursor(
1003 dpy
, src
, msk
, &(c
[0]), &(c
[1]), x_hot
, y_hot
);
1004 XFreePixmap(dpy
, src
);
1005 XFreePixmap(dpy
, msk
);
1013 /* FIXME: Use color limit */
1014 Bool
PImageLoadPixmapFromXpmData(
1015 Display
*dpy
, Window win
, int color_limit
,
1017 Pixmap
*pixmap
, Pixmap
*mask
,
1018 int *width
, int *height
, int *depth
)
1020 FxpmAttributes xpm_attributes
;
1026 xpm_attributes
.valuemask
= FxpmCloseness
|
1027 FxpmExtensions
| FxpmVisual
| FxpmColormap
| FxpmDepth
;
1028 xpm_attributes
.closeness
= 40000;
1029 xpm_attributes
.visual
= Pvisual
;
1030 xpm_attributes
.colormap
= Pcmap
;
1031 xpm_attributes
.depth
= Pdepth
;
1032 /* suppress compiler warning if xpm library is not compiled in */
1033 xpm_attributes
.width
= 0;
1034 xpm_attributes
.height
= 0;
1036 FxpmCreatePixmapFromData(
1037 dpy
, win
, data
, pixmap
, mask
, &xpm_attributes
) !=
1042 *width
= xpm_attributes
.width
;
1043 *height
= xpm_attributes
.height
;