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