b7ddf89400d77e4fc43762b4e305d2cd15d4cc78
[wmaker-crm.git] / src / texture.c
blobb7ddf89400d77e4fc43762b4e305d2cd15d4cc78
1 /*
2 * Window Maker window manager
4 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "wconfig.h"
23 #include <X11/Xlib.h>
24 #include <X11/Xutil.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
30 #include <wraster.h>
32 #include "WindowMaker.h"
33 #include "texture.h"
34 #include "window.h"
35 #include "misc.h"
37 extern WPreferences wPreferences;
39 static void bevelImage(RImage * image, int relief);
40 static RImage * get_texture_image(WScreen *scr, char *pixmap_file);
42 WTexSolid *wTextureMakeSolid(WScreen * scr, XColor * color)
44 WTexSolid *texture;
45 int gcm;
46 XGCValues gcv;
48 texture = wmalloc(sizeof(WTexture));
50 texture->type = WTEX_SOLID;
51 texture->subtype = 0;
53 XAllocColor(dpy, scr->w_colormap, color);
54 texture->normal = *color;
55 if (color->red == 0 && color->blue == 0 && color->green == 0) {
56 texture->light.red = 0xb6da;
57 texture->light.green = 0xb6da;
58 texture->light.blue = 0xb6da;
59 texture->dim.red = 0x6185;
60 texture->dim.green = 0x6185;
61 texture->dim.blue = 0x6185;
62 } else {
63 RColor rgb;
64 RHSVColor hsv, hsv2;
65 int v;
67 rgb.red = color->red >> 8;
68 rgb.green = color->green >> 8;
69 rgb.blue = color->blue >> 8;
70 RRGBtoHSV(&rgb, &hsv);
71 RHSVtoRGB(&hsv, &rgb);
72 hsv2 = hsv;
74 v = hsv.value * 16 / 10;
75 hsv.value = (v > 255 ? 255 : v);
76 RHSVtoRGB(&hsv, &rgb);
77 texture->light.red = rgb.red << 8;
78 texture->light.green = rgb.green << 8;
79 texture->light.blue = rgb.blue << 8;
81 hsv2.value = hsv2.value / 2;
82 RHSVtoRGB(&hsv2, &rgb);
83 texture->dim.red = rgb.red << 8;
84 texture->dim.green = rgb.green << 8;
85 texture->dim.blue = rgb.blue << 8;
87 texture->dark.red = 0;
88 texture->dark.green = 0;
89 texture->dark.blue = 0;
90 XAllocColor(dpy, scr->w_colormap, &texture->light);
91 XAllocColor(dpy, scr->w_colormap, &texture->dim);
92 XAllocColor(dpy, scr->w_colormap, &texture->dark);
94 gcm = GCForeground | GCBackground | GCGraphicsExposures;
95 gcv.graphics_exposures = False;
97 gcv.background = gcv.foreground = texture->light.pixel;
98 texture->light_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
100 gcv.background = gcv.foreground = texture->dim.pixel;
101 texture->dim_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
103 gcv.background = gcv.foreground = texture->dark.pixel;
104 texture->dark_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
106 gcv.background = gcv.foreground = color->pixel;
107 texture->normal_gc = XCreateGC(dpy, scr->w_win, gcm, &gcv);
109 return texture;
112 static int dummyErrorHandler(Display * foo, XErrorEvent * bar)
114 return 0;
117 void wTextureDestroy(WScreen * scr, WTexture * texture)
119 int i;
120 int count = 0;
121 unsigned long colors[8];
124 * some stupid servers don't like white or black being freed...
126 #define CANFREE(c) (c!=scr->black_pixel && c!=scr->white_pixel && c!=0)
127 switch (texture->any.type) {
128 case WTEX_SOLID:
129 XFreeGC(dpy, texture->solid.light_gc);
130 XFreeGC(dpy, texture->solid.dark_gc);
131 XFreeGC(dpy, texture->solid.dim_gc);
132 if (CANFREE(texture->solid.light.pixel))
133 colors[count++] = texture->solid.light.pixel;
134 if (CANFREE(texture->solid.dim.pixel))
135 colors[count++] = texture->solid.dim.pixel;
136 if (CANFREE(texture->solid.dark.pixel))
137 colors[count++] = texture->solid.dark.pixel;
138 break;
140 case WTEX_PIXMAP:
141 RReleaseImage(texture->pixmap.pixmap);
142 break;
144 case WTEX_MHGRADIENT:
145 case WTEX_MVGRADIENT:
146 case WTEX_MDGRADIENT:
147 for (i = 0; texture->mgradient.colors[i] != NULL; i++) {
148 wfree(texture->mgradient.colors[i]);
150 wfree(texture->mgradient.colors);
151 break;
153 case WTEX_THGRADIENT:
154 case WTEX_TVGRADIENT:
155 case WTEX_TDGRADIENT:
156 RReleaseImage(texture->tgradient.pixmap);
157 break;
160 if (CANFREE(texture->any.color.pixel))
161 colors[count++] = texture->any.color.pixel;
162 if (count > 0) {
163 XErrorHandler oldhandler;
165 /* ignore error from buggy servers that don't know how
166 * to do reference counting for colors. */
167 XSync(dpy, 0);
168 oldhandler = XSetErrorHandler(dummyErrorHandler);
169 XFreeColors(dpy, scr->w_colormap, colors, count, 0);
170 XSync(dpy, 0);
171 XSetErrorHandler(oldhandler);
173 XFreeGC(dpy, texture->any.gc);
174 wfree(texture);
175 #undef CANFREE
178 WTexGradient *wTextureMakeGradient(WScreen * scr, int style, RColor * from, RColor * to)
180 WTexGradient *texture;
181 XGCValues gcv;
183 texture = wmalloc(sizeof(WTexture));
184 texture->type = style;
185 texture->subtype = 0;
187 texture->color1 = *from;
188 texture->color2 = *to;
190 texture->normal.red = (from->red + to->red) << 7;
191 texture->normal.green = (from->green + to->green) << 7;
192 texture->normal.blue = (from->blue + to->blue) << 7;
194 XAllocColor(dpy, scr->w_colormap, &texture->normal);
195 gcv.background = gcv.foreground = texture->normal.pixel;
196 gcv.graphics_exposures = False;
197 texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv);
199 return texture;
202 WTexIGradient *wTextureMakeIGradient(WScreen * scr, int thickness1, RColor colors1[2],
203 int thickness2, RColor colors2[2])
205 WTexIGradient *texture;
206 XGCValues gcv;
207 int i;
209 texture = wmalloc(sizeof(WTexture));
210 texture->type = WTEX_IGRADIENT;
211 for (i = 0; i < 2; i++) {
212 texture->colors1[i] = colors1[i];
213 texture->colors2[i] = colors2[i];
215 texture->thickness1 = thickness1;
216 texture->thickness2 = thickness2;
217 if (thickness1 >= thickness2) {
218 texture->normal.red = (colors1[0].red + colors1[1].red) << 7;
219 texture->normal.green = (colors1[0].green + colors1[1].green) << 7;
220 texture->normal.blue = (colors1[0].blue + colors1[1].blue) << 7;
221 } else {
222 texture->normal.red = (colors2[0].red + colors2[1].red) << 7;
223 texture->normal.green = (colors2[0].green + colors2[1].green) << 7;
224 texture->normal.blue = (colors2[0].blue + colors2[1].blue) << 7;
226 XAllocColor(dpy, scr->w_colormap, &texture->normal);
227 gcv.background = gcv.foreground = texture->normal.pixel;
228 gcv.graphics_exposures = False;
229 texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv);
231 return texture;
234 WTexMGradient *wTextureMakeMGradient(WScreen * scr, int style, RColor ** colors)
236 WTexMGradient *texture;
237 XGCValues gcv;
238 int i;
240 texture = wmalloc(sizeof(WTexture));
241 texture->type = style;
242 texture->subtype = 0;
244 i = 0;
245 while (colors[i] != NULL)
246 i++;
247 i--;
248 texture->normal.red = (colors[0]->red << 8);
249 texture->normal.green = (colors[0]->green << 8);
250 texture->normal.blue = (colors[0]->blue << 8);
252 texture->colors = colors;
254 XAllocColor(dpy, scr->w_colormap, &texture->normal);
255 gcv.background = gcv.foreground = texture->normal.pixel;
256 gcv.graphics_exposures = False;
257 texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv);
259 return texture;
262 WTexPixmap *wTextureMakePixmap(WScreen * scr, int style, char *pixmap_file, XColor * color)
264 WTexPixmap *texture;
265 XGCValues gcv;
266 RImage *image;
268 image = get_texture_image(scr, pixmap_file);
269 if (!image)
270 return NULL;
272 texture = wmalloc(sizeof(WTexture));
273 texture->type = WTEX_PIXMAP;
274 texture->subtype = style;
276 texture->normal = *color;
278 XAllocColor(dpy, scr->w_colormap, &texture->normal);
279 gcv.background = gcv.foreground = texture->normal.pixel;
280 gcv.graphics_exposures = False;
281 texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv);
283 texture->pixmap = image;
285 return texture;
288 WTexTGradient *wTextureMakeTGradient(WScreen * scr, int style, RColor * from, RColor * to,
289 char *pixmap_file, int opacity)
291 WTexTGradient *texture;
292 XGCValues gcv;
293 RImage *image;
295 image = get_texture_image(scr, pixmap_file);
296 if (!image)
297 return NULL;
299 texture = wmalloc(sizeof(WTexture));
300 texture->type = style;
302 texture->opacity = opacity;
304 texture->color1 = *from;
305 texture->color2 = *to;
307 texture->normal.red = (from->red + to->red) << 7;
308 texture->normal.green = (from->green + to->green) << 7;
309 texture->normal.blue = (from->blue + to->blue) << 7;
311 XAllocColor(dpy, scr->w_colormap, &texture->normal);
312 gcv.background = gcv.foreground = texture->normal.pixel;
313 gcv.graphics_exposures = False;
314 texture->normal_gc = XCreateGC(dpy, scr->w_win, GCForeground | GCBackground | GCGraphicsExposures, &gcv);
316 texture->pixmap = image;
318 return texture;
321 static RImage * get_texture_image(WScreen *scr, char *pixmap_file)
323 char *file;
324 RImage *image;
326 file = FindImage(wPreferences.pixmap_path, pixmap_file);
327 if (!file) {
328 wwarning(_("image file \"%s\" used as texture could not be found."), pixmap_file);
329 return NULL;
331 image = RLoadImage(scr->rcontext, file, 0);
332 if (!image) {
333 wwarning(_("could not load texture pixmap \"%s\":%s"), file, RMessageForError(RErrorCode));
334 wfree(file);
335 return NULL;
337 wfree(file);
339 return image;
342 RImage *wTextureRenderImage(WTexture * texture, int width, int height, int relief)
344 RImage *image = NULL;
345 RColor color1;
346 int d;
347 int subtype;
349 switch (texture->any.type) {
350 case WTEX_SOLID:
351 image = RCreateImage(width, height, False);
353 color1.red = texture->solid.normal.red >> 8;
354 color1.green = texture->solid.normal.green >> 8;
355 color1.blue = texture->solid.normal.blue >> 8;
356 color1.alpha = 255;
358 RClearImage(image, &color1);
359 break;
361 case WTEX_PIXMAP:
362 if (texture->pixmap.subtype == WTP_TILE) {
363 image = RMakeTiledImage(texture->pixmap.pixmap, width, height);
364 } else if (texture->pixmap.subtype == WTP_CENTER) {
365 color1.red = texture->pixmap.normal.red >> 8;
366 color1.green = texture->pixmap.normal.green >> 8;
367 color1.blue = texture->pixmap.normal.blue >> 8;
368 color1.alpha = 255;
369 image = RMakeCenteredImage(texture->pixmap.pixmap, width, height, &color1);
370 } else {
371 image = RScaleImage(texture->pixmap.pixmap, width, height);
373 break;
375 case WTEX_IGRADIENT:
376 image = RRenderInterwovenGradient(width, height,
377 texture->igradient.colors1,
378 texture->igradient.thickness1,
379 texture->igradient.colors2, texture->igradient.thickness2);
380 break;
382 case WTEX_HGRADIENT:
383 subtype = RGRD_HORIZONTAL;
384 goto render_gradient;
386 case WTEX_VGRADIENT:
387 subtype = RGRD_VERTICAL;
388 goto render_gradient;
390 case WTEX_DGRADIENT:
391 subtype = RGRD_DIAGONAL;
392 render_gradient:
394 image = RRenderGradient(width, height, &texture->gradient.color1,
395 &texture->gradient.color2, subtype);
396 break;
398 case WTEX_MHGRADIENT:
399 subtype = RGRD_HORIZONTAL;
400 goto render_mgradient;
402 case WTEX_MVGRADIENT:
403 subtype = RGRD_VERTICAL;
404 goto render_mgradient;
406 case WTEX_MDGRADIENT:
407 subtype = RGRD_DIAGONAL;
408 render_mgradient:
409 image = RRenderMultiGradient(width, height, &(texture->mgradient.colors[1]), subtype);
410 break;
412 case WTEX_THGRADIENT:
413 subtype = RGRD_HORIZONTAL;
414 goto render_tgradient;
416 case WTEX_TVGRADIENT:
417 subtype = RGRD_VERTICAL;
418 goto render_tgradient;
420 case WTEX_TDGRADIENT:
421 subtype = RGRD_DIAGONAL;
422 render_tgradient:
424 RImage *grad;
426 image = RMakeTiledImage(texture->tgradient.pixmap, width, height);
427 if (!image)
428 break;
430 grad = RRenderGradient(width, height, &texture->tgradient.color1,
431 &texture->tgradient.color2, subtype);
432 if (!grad) {
433 RReleaseImage(image);
434 image = NULL;
435 break;
438 RCombineImagesWithOpaqueness(image, grad, texture->tgradient.opacity);
439 RReleaseImage(grad);
441 break;
442 default:
443 puts("ERROR in wTextureRenderImage()");
444 image = NULL;
445 break;
448 if (!image) {
449 RColor gray;
451 wwarning(_("could not render texture: %s"), RMessageForError(RErrorCode));
453 image = RCreateImage(width, height, False);
454 if (image == NULL) {
455 wwarning(_("could not allocate image buffer"));
456 return NULL;
459 gray.red = 190;
460 gray.green = 190;
461 gray.blue = 190;
462 gray.alpha = 255;
463 RClearImage(image, &gray);
466 /* render bevel */
468 switch (relief) {
469 case WREL_ICON:
470 d = RBEV_RAISED3;
471 break;
473 case WREL_RAISED:
474 d = RBEV_RAISED2;
475 break;
477 case WREL_SUNKEN:
478 d = RBEV_SUNKEN;
479 break;
481 case WREL_FLAT:
482 d = 0;
483 break;
485 case WREL_MENUENTRY:
486 d = -WREL_MENUENTRY;
487 break;
489 default:
490 d = 0;
493 if (d > 0) {
494 RBevelImage(image, d);
495 } else if (d < 0) {
496 bevelImage(image, -d);
499 return image;
502 static void bevelImage(RImage * image, int relief)
504 int width = image->width;
505 int height = image->height;
506 RColor color;
508 switch (relief) {
509 case WREL_MENUENTRY:
510 color.red = color.green = color.blue = 80;
511 color.alpha = 0;
512 /**/ ROperateLine(image, RAddOperation, 1, 0, width - 2, 0, &color);
513 /**/ ROperateLine(image, RAddOperation, 0, 0, 0, height - 1, &color);
515 color.red = color.green = color.blue = 40;
516 color.alpha = 0;
517 ROperateLine(image, RSubtractOperation, width - 1, 0, width - 1, height - 1, &color);
519 /**/ ROperateLine(image, RSubtractOperation, 1, height - 2, width - 2, height - 2, &color);
521 color.red = color.green = color.blue = 0;
522 color.alpha = 255;
523 RDrawLine(image, 0, height - 1, width - 1, height - 1, &color);
524 /**/ break;
529 void wDrawBevel(Drawable d, unsigned width, unsigned height, WTexSolid * texture, int relief)
531 GC light, dim, dark;
532 XSegment segs[4];
534 if (relief == WREL_FLAT)
535 return;
537 light = texture->light_gc;
538 dim = texture->dim_gc;
539 dark = texture->dark_gc;
540 switch (relief) {
541 case WREL_FLAT:
542 return;
543 case WREL_MENUENTRY:
544 case WREL_RAISED:
545 case WREL_ICON:
546 segs[0].x1 = 1;
547 segs[0].x2 = width - 2;
548 segs[0].y2 = segs[0].y1 = height - 2;
549 segs[1].x1 = width - 2;
550 segs[1].y1 = 1;
551 segs[1].x2 = width - 2;
552 segs[1].y2 = height - 2;
553 if (wPreferences.new_style == TS_NEXT) {
554 XDrawSegments(dpy, d, dark, segs, 2);
555 } else {
556 XDrawSegments(dpy, d, dim, segs, 2);
558 segs[0].x1 = 0;
559 segs[0].x2 = width - 1;
560 segs[0].y2 = segs[0].y1 = height - 1;
561 segs[1].x1 = segs[1].x2 = width - 1;
562 segs[1].y1 = 0;
563 segs[1].y2 = height - 1;
564 if (wPreferences.new_style == TS_NEXT) {
565 XDrawSegments(dpy, d, light, segs, 2);
566 } else {
567 XDrawSegments(dpy, d, dark, segs, 2);
569 segs[0].x1 = segs[0].y1 = segs[0].y2 = 0;
570 segs[0].x2 = width - 2;
571 segs[1].x1 = segs[1].y1 = 0;
572 segs[1].x2 = 0;
573 segs[1].y2 = height - 2;
574 if (wPreferences.new_style == TS_NEXT) {
575 XDrawSegments(dpy, d, dark, segs, 2);
576 } else {
577 XDrawSegments(dpy, d, light, segs, 2);
579 if (relief == WREL_ICON) {
580 segs[0].x1 = segs[0].y1 = segs[0].y2 = 1;
581 segs[0].x2 = width - 2;
582 segs[1].x1 = segs[1].y1 = 1;
583 segs[1].x2 = 1;
584 segs[1].y2 = height - 2;
585 XDrawSegments(dpy, d, light, segs, 2);
587 break;