1 #include "config.h"
2 
3 #ifdef CONFIG_GDK_PIXBUF_XLIB
4 
5 #include "yimage.h"
6 #include "yxapp.h"
7 #include <stdlib.h>
8 #include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
9 
10 #define ATH 10  /* alpha threshold */
11 
12 class YImageGDK: public YImage {
13 public:
YImageGDK(unsigned width,unsigned height,GdkPixbuf * pixbuf)14     YImageGDK(unsigned width, unsigned height, GdkPixbuf *pixbuf):
15         YImage(width, height)
16     {
17         fPixbuf = pixbuf;
18     }
~YImageGDK()19     virtual ~YImageGDK() {
20         g_object_unref(G_OBJECT(fPixbuf));
21     }
22     virtual ref<YPixmap> renderToPixmap(unsigned depth, bool premult);
23     virtual ref<YImage> scale(unsigned width, unsigned height);
24     virtual void draw(Graphics &g, int dx, int dy);
25     virtual void draw(Graphics &g, int x, int y,
26                        unsigned w, unsigned h, int dx, int dy);
27     virtual void composite(Graphics &g, int x, int y,
28                             unsigned w, unsigned h, int dx, int dy);
29     virtual unsigned depth() const;
30     virtual bool hasAlpha() const;
valid() const31     virtual bool valid() const { return fPixbuf != nullptr; }
32     virtual ref<YImage> subimage(int x, int y, unsigned w, unsigned h);
33     virtual void save(upath filename);
34 
35 private:
36     GdkPixbuf *fPixbuf;
37 };
38 
renderName()39 const char* YImage::renderName() {
40     return "GdkPixbuf";
41 }
42 
supportsDepth(unsigned depth)43 bool YImage::supportsDepth(unsigned depth) {
44     return depth == unsigned(xlib_rgb_get_depth());
45 }
46 
hasAlpha() const47 bool YImageGDK::hasAlpha() const {
48     return gdk_pixbuf_get_has_alpha(fPixbuf);
49 }
50 
depth() const51 unsigned YImageGDK::depth() const {
52     return 8 * gdk_pixbuf_get_n_channels(fPixbuf);
53 }
54 
load(upath filename)55 ref<YImage> YImage::load(upath filename) {
56     ref<YImage> image;
57     GError *gerror = nullptr;
58     GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(filename.string(), &gerror);
59 
60     if (pixbuf != nullptr) {
61         image.init(new YImageGDK(gdk_pixbuf_get_width(pixbuf),
62                                  gdk_pixbuf_get_height(pixbuf),
63                                  pixbuf));
64         return image;
65     }
66 
67     // support themes with indirect XPM images, like OnyX:
68     const int lim = 64;
69     for (int k = 9; --k > 0 && inrange(int(filename.fileSize()), 5, lim); ) {
70         fileptr fp(filename.fopen("r"));
71         if (fp == nullptr)
72             break;
73 
74         char buf[lim];
75         if (fgets(buf, lim, fp) == nullptr)
76             break;
77 
78         mstring match(mstring(buf).match("^[a-z][-_a-z0-9]*\\.xpm$", "i"));
79         if (match == null)
80             break;
81 
82         filename = filename.parent().relative(match);
83         if (filename.fileSize() > lim)
84             return load(filename);
85     }
86     return image;
87 }
88 
save(upath filename)89 void YImageGDK::save(upath filename) {
90     mstring handle(filename.replaceExtension(".png"));
91     GError *gerror = nullptr;
92     gdk_pixbuf_save(fPixbuf, handle, "png", &gerror, (void *) nullptr);
93     if (gerror) {
94         msg("Cannot save YImageGDK %s: %s", handle.c_str(), gerror->message);
95         g_error_free(gerror);
96     }
97 }
98 
scale(unsigned w,unsigned h)99 ref<YImage> YImageGDK::scale(unsigned w, unsigned h) {
100     if (w == width() && h == height())
101         return ref<YImage>(this);
102 
103     ref<YImage> image;
104     GdkPixbuf *pixbuf;
105     pixbuf = gdk_pixbuf_scale_simple(fPixbuf,
106                                      w, h,
107                                      GDK_INTERP_BILINEAR);
108     if (pixbuf != nullptr) {
109         image.init(new YImageGDK(w, h, pixbuf));
110     }
111 
112     return image;
113 }
114 
subimage(int x,int y,unsigned w,unsigned h)115 ref<YImage> YImageGDK::subimage(int x, int y, unsigned w, unsigned h) {
116     PRECONDITION(w <= width() && unsigned(x) <= width() - w);
117     PRECONDITION(h <= height() && unsigned(y) <= height() - h);
118 
119     GdkPixbuf* pixbuf = gdk_pixbuf_new(
120                         gdk_pixbuf_get_colorspace(fPixbuf),
121                         gdk_pixbuf_get_has_alpha(fPixbuf),
122                         gdk_pixbuf_get_bits_per_sample(fPixbuf),
123                         w, h);
124     gdk_pixbuf_copy_area(fPixbuf, x, y, int(w), int(h), pixbuf, 0, 0);
125     return ref<YImage>(new YImageGDK(w, h, pixbuf));
126 }
127 
createFromPixmap(ref<YPixmap> pixmap)128 ref<YImage> YImage::createFromPixmap(ref<YPixmap> pixmap) {
129     return createFromPixmapAndMask(pixmap->pixmap(),
130                                    pixmap->mask(),
131                                    pixmap->width(),
132                                    pixmap->height());
133 }
134 
createFromPixmapAndMask(Pixmap pixmap,Pixmap mask,unsigned width,unsigned height)135 ref<YImage> YImage::createFromPixmapAndMask(Pixmap pixmap, Pixmap mask,
136                                             unsigned width, unsigned height)
137 {
138     ref<YImage> image;
139     GdkPixbuf *pixbuf =
140         gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
141                        width, height);
142 
143 
144     if (pixbuf) {
145         pixbuf =
146             gdk_pixbuf_xlib_get_from_drawable(pixbuf,
147                                               pixmap,
148                                               xlib_rgb_get_cmap(),
149                                               xlib_rgb_get_visual(),
150                                               0, 0,
151                                               0, 0,
152                                               width,
153                                               height);
154 
155         if (mask != None) {
156             XImage *image = XGetImage(xapp->display(), mask,
157                                       0, 0, width, height,
158                                       AllPlanes, ZPixmap);
159             guchar *pixels = gdk_pixbuf_get_pixels(pixbuf);
160 
161             if (image) {
162                 //unsigned char *pix = image->data;
163                 for (unsigned r = 0; r < height; r++) {
164                     for (unsigned c = 0; c < width; c++) {
165                         unsigned int pix = XGetPixel(image, c, r);
166                         pixels[c * 4 + 3] = (unsigned char)(pix ? 255 : 0);
167                     }
168                     pixels += gdk_pixbuf_get_rowstride(pixbuf);
169                     //pix += image->bytes_per_line;
170                 }
171                 XDestroyImage(image);
172             }
173         }
174 
175         image.init(new YImageGDK(width,
176                                  height,
177                                  pixbuf));
178     }
179     return image;
180 }
181 
createFromIconProperty(long * prop_pixels,unsigned width,unsigned height)182 ref<YImage> YImage::createFromIconProperty(long *prop_pixels,
183                                            unsigned width, unsigned height)
184 {
185     ref<YImage> image;
186     GdkPixbuf *pixbuf =
187         gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
188                        width, height);
189 
190     if (!pixbuf)
191         return null;
192 
193     guchar *pixels = gdk_pixbuf_get_pixels(pixbuf);
194 
195     for (unsigned r = 0; r < height; r++) {
196         for (unsigned c = 0; c < width; c++) {
197             unsigned long pix =
198                 prop_pixels[c + r * width];
199             pixels[c * 4 + 2] = (unsigned char)(pix & 0xFF);
200             pixels[c * 4 + 1] = (unsigned char)((pix >> 8) & 0xFF);
201             pixels[c * 4] = (unsigned char)((pix >> 16) & 0xFF);
202             pixels[c * 4 + 3] = (unsigned char)((pix >> 24) & 0xFF);
203         }
204         pixels += gdk_pixbuf_get_rowstride(pixbuf);
205     }
206     image.init(new YImageGDK(width,
207                              height,
208                              pixbuf));
209     return image;
210 }
211 
createFromPixmapAndMaskScaled(Pixmap pix,Pixmap mask,unsigned width,unsigned height,unsigned nw,unsigned nh)212 ref<YImage> YImage::createFromPixmapAndMaskScaled(Pixmap pix, Pixmap mask,
213                                                   unsigned width, unsigned height,
214                                                   unsigned nw, unsigned nh)
215 {
216     ref<YImage> image = createFromPixmapAndMask(pix, mask, width, height);
217     if (image != null && (nw != width || nh != height))
218         image = image->scale(nw, nh);
219     return image;
220 }
221 
renderToPixmap(unsigned depth,bool premult)222 ref<YPixmap> YImageGDK::renderToPixmap(unsigned depth, bool premult) {
223     Pixmap pixmap = None, mask = None;
224 
225     if (depth == 0) {
226         depth = xapp->depth();
227     }
228     if (depth == 32 || depth == 24) {
229         int width = int(this->width());
230         int height = int(this->height());
231         Visual* visual = xapp->visualForDepth(depth);
232         XImage* image = XCreateImage(xapp->display(), visual, depth,
233                                      ZPixmap, 0, nullptr, width, height, 8, 0);
234         if (image)
235             image->data = (char *) calloc(image->bytes_per_line * height, 1);
236         XImage* imask = XCreateImage(xapp->display(), visual, 1,
237                                      XYPixmap, 0, nullptr, width, height, 8, 0);
238         if (imask)
239             imask->data = (char *) calloc(imask->bytes_per_line * height, 1);
240 
241         if (image && image->data && imask && imask->data) {
242             const bool alpha = gdk_pixbuf_get_has_alpha(fPixbuf);
243             const int nchans = gdk_pixbuf_get_n_channels(fPixbuf);
244             const int stride = gdk_pixbuf_get_rowstride(fPixbuf);
245             const guchar* pixels = gdk_pixbuf_get_pixels(fPixbuf);
246 
247             for (int row = 0; row < height; row++) {
248                 const guchar* rowpix = pixels + row * stride;
249                 for (int col = 0; col < width; col++, rowpix += nchans) {
250                     guchar red = rowpix[0];
251                     guchar grn = rowpix[1];
252                     guchar blu = rowpix[2];
253                     guchar alp = alpha
254                                ? (rowpix[3] >= ATH ? rowpix[3] : 0x00)
255                                : 0xFF;
256                     if (premult) {
257                         red = (red * (alp + 1)) >> 8;
258                         grn = (grn * (alp + 1)) >> 8;
259                         blu = (blu * (alp + 1)) >> 8;
260                     }
261                     XPutPixel(image, col, row,
262                               (red << 16) |
263                               (grn << 8) |
264                               (blu << 0) |
265                               (alp << 24));
266                     bool bit = (alp >= ATH);
267                     XPutPixel(imask, col, row, bit);
268                 }
269             }
270 
271             pixmap = XCreatePixmap(xapp->display(), xapp->root(),
272                                    width, height, depth);
273             GC gc = XCreateGC(xapp->display(), pixmap, None, None);
274             XPutImage(xapp->display(), pixmap, gc, image,
275                       0, 0, 0, 0, width, height);
276             XFreeGC(xapp->display(), gc);
277 
278             mask = XCreatePixmap(xapp->display(), xapp->root(),
279                                  width, height, 1);
280             gc = XCreateGC(xapp->display(), mask, None, None);
281             XPutImage(xapp->display(), mask, gc, imask,
282                       0, 0, 0, 0, width, height);
283             XFreeGC(xapp->display(), gc);
284 
285             XDestroyImage(image);
286             XDestroyImage(imask);
287         }
288     }
289     else if (depth == unsigned(xlib_rgb_get_depth())) {
290         gdk_pixbuf_xlib_render_pixmap_and_mask(fPixbuf, &pixmap, &mask, ATH);
291         if (pixmap == None)
292             return null;
293     }
294 
295     return createPixmap(pixmap, mask, width(), height(), depth);
296 }
297 
createPixmap(Pixmap pixmap,Pixmap mask,unsigned w,unsigned h,unsigned depth)298 ref<YPixmap> YImage::createPixmap(Pixmap pixmap, Pixmap mask,
299                                   unsigned w, unsigned h, unsigned depth) {
300     ref<YPixmap> n;
301 
302     if (pixmap)
303         n.init(new YPixmap(pixmap, mask, w, h, depth, ref<YImage>(this)));
304     return n;
305 }
306 
draw(Graphics & g,int dx,int dy)307 void YImageGDK::draw(Graphics &g, int dx, int dy) {
308 #if 1
309     composite(g, 0, 0, width(), height(), dx, dy);
310 #else
311     gdk_pixbuf_xlib_render_to_drawable_alpha(fPixbuf, g.drawable(), //g.handleX(),
312                                              0, 0, dx, dy, width(), height(),
313                                              GDK_PIXBUF_ALPHA_FULL,
314                                              ATH,
315                                              XLIB_RGB_DITHER_NORMAL, 0, 0);
316 #endif
317 }
318 
draw(Graphics & g,int x,int y,unsigned w,unsigned h,int dx,int dy)319 void YImageGDK::draw(Graphics &g, int x, int y, unsigned w, unsigned h, int dx, int dy) {
320 #if 1
321     composite(g, x, y, w, h, dx, dy);
322 #else
323     if (x < 0 || x + w > width() || y < 0 || y + h > height())
324         return;
325     gdk_pixbuf_xlib_render_to_drawable_alpha(fPixbuf, g.drawable(), //g.handleX(),
326                                              x, y, dx, dy, w, h,
327                                              GDK_PIXBUF_ALPHA_BILEVEL,
328                                              ATH,
329                                              XLIB_RGB_DITHER_NORMAL, 0, 0);
330 #endif
331 }
332 
composite(Graphics & g,int x,int y,unsigned width,unsigned height,int dx,int dy)333 void YImageGDK::composite(Graphics &g, int x, int y, unsigned width, unsigned height, int dx, int dy) {
334     int w = (int) width;
335     int h = (int) height;
336 
337     //MSG(("composite -- %d %d %d %d | %d %d", x, y, w, h, dx, dy));
338     if (g.xorigin() > dx) {
339         if (w <= g.xorigin() - dx)
340             return;
341         w -= g.xorigin() - dx;
342         x += g.xorigin() - dx;
343         dx = g.xorigin();
344     }
345     if (g.yorigin() > dy) {
346         if (h <= g.yorigin() - dy)
347             return;
348         h -= g.yorigin() - dy;
349         y += g.yorigin() - dy;
350         dy = g.yorigin();
351     }
352     if (dx + w > (int) (g.xorigin() + g.rwidth())) {
353         if ((int) (g.xorigin() + g.rwidth()) <= dx)
354             return;
355         w = g.xorigin() + g.rwidth() - dx;
356     }
357     if (dy + h > (int) (g.yorigin() + g.rheight())) {
358         if ((int) (g.yorigin() + g.rheight()) <= dy)
359             return;
360         h = g.yorigin() + g.rheight() - dy;
361     }
362     if (w <= 0 || h <= 0)
363         return;
364 
365     const int src_x = int(dx - g.xorigin());
366     const int src_y = int(dy - g.yorigin());
367 
368     //MSG(("composite ++ %d %d %d %d | %d %d", x, y, w, h, dx, dy));
369     GdkPixbuf *pixbuf =
370         gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, w, h);
371     Visual* visual = xapp->visualForDepth(g.rdepth());
372     Colormap cmap = xapp->colormapForVisual(visual);
373     gdk_pixbuf_xlib_get_from_drawable(pixbuf,
374                                       g.drawable(),
375                                       cmap,
376                                       visual,
377                                       src_x, src_y, 0, 0, w, h);
378     gdk_pixbuf_composite(fPixbuf, pixbuf,
379                          0, 0, w, h,
380                          -x, -y, 1.0, 1.0,
381                          GDK_INTERP_BILINEAR, 255);
382     gdk_pixbuf_xlib_render_to_drawable(pixbuf, g.drawable(), g.handleX(),
383                                              0, 0, src_x, src_y, w, h,
384 //                                             GDK_PIXBUF_ALPHA_BILEVEL, 128,
385                                              XLIB_RGB_DITHER_NONE, 0, 0);
386     g_object_unref(G_OBJECT(pixbuf));
387 }
388 
image_init()389 void image_init() {
390 #if !GLIB_CHECK_VERSION(2,36,0)
391     g_type_init();
392 #endif
393 
394     int depth = xapp->alpha() ? 32 : int(xapp->depth());
395 
396     gdk_pixbuf_xlib_init_with_depth(xapp->display(), xapp->screen(), depth);
397 }
398 
399 #endif
400 
401 // vim: set sw=4 ts=4 et:
402