/* context.c - X context management * * Raster graphics library * * Copyright (c) 1997-2003 Alfredo K. Kojima * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301, USA. */ #include #include #include #include #ifdef HAVE_LIBXMU #include #endif #include #include #include #include #include #include "wraster.h" #include "scale.h" #include "wr_i18n.h" #ifndef HAVE_FLOAT_MATHFUNC #define powf(x, y) ((float) pow((double)(x), (double)(y))) #endif static Bool bestContext(Display * dpy, int screen_number, RContext * context); static const RContextAttributes DEFAULT_CONTEXT_ATTRIBS = { RC_UseSharedMemory | RC_RenderMode | RC_ColorsPerChannel, /* flags */ RDitheredRendering, /* render_mode */ 4, /* colors_per_channel */ 0, 0, 0, 0, True, /* use_shared_memory */ RMitchellFilter, RUseStdColormap }; /* * * Colormap allocation for PseudoColor visuals: * * * switch standardColormap: * none: * allocate colors according to colors_per_channel * * best/default: * if there's a std colormap defined then use it * * else * create a std colormap and set it */ /* *---------------------------------------------------------------------- * allocateStandardPseudoColor * Creates the internal colormap for PseudoColor, setting the * color values according to the supplied standard colormap. * * Returns: - * * Side effects: - * * Notes: - *---------------------------------------------------------------------- */ static Bool allocateStandardPseudoColor(RContext * ctx, XStandardColormap * stdcmap) { int i; ctx->ncolors = stdcmap->red_max * stdcmap->red_mult + stdcmap->green_max * stdcmap->green_mult + stdcmap->blue_max * stdcmap->blue_mult + 1; if (ctx->ncolors <= 1) { RErrorCode = RERR_INTERNAL; puts("wraster: bad standard colormap"); return False; } ctx->colors = malloc(sizeof(XColor) * ctx->ncolors); if (!ctx->colors) { RErrorCode = RERR_NOMEMORY; return False; } ctx->pixels = malloc(sizeof(unsigned long) * ctx->ncolors); if (!ctx->pixels) { free(ctx->colors); ctx->colors = NULL; RErrorCode = RERR_NOMEMORY; return False; } #define calc(max,mult) (((i / stdcmap->mult) % \ (stdcmap->max + 1)) * 65535) / stdcmap->max for (i = 0; i < ctx->ncolors; i++) { ctx->colors[i].pixel = i + stdcmap->base_pixel; ctx->colors[i].red = calc(red_max, red_mult); ctx->colors[i].green = calc(green_max, green_mult); ctx->colors[i].blue = calc(blue_max, blue_mult); ctx->pixels[i] = ctx->colors[i].pixel; } #undef calc return True; } static Bool setupStandardColormap(RContext * ctx, Atom property) { #ifdef HAVE_LIBXMU if (!XmuLookupStandardColormap(ctx->dpy, ctx->screen_number, ctx->visual->visualid, ctx->depth, property, True, True)) { RErrorCode = RERR_STDCMAPFAIL; return False; } return True; #else (void) ctx; (void) property; RErrorCode = RERR_STDCMAPFAIL; return False; #endif } static XColor *allocateColor(RContext *ctx, XColor *colors, int ncolors) { XColor avcolors[256]; int avncolors; int i, r, g, b; int retries; for (i = 0; i < ncolors; i++) { #ifdef WRLIB_DEBUG fprintf(stderr, "trying:%x,%x,%x\n", colors[i].red, colors[i].green, colors[i].blue); #endif if (!XAllocColor(ctx->dpy, ctx->cmap, &(colors[i]))) { colors[i].flags = 0; /* failed */ #ifdef WRLIB_DEBUG fprintf(stderr, "failed:%x,%x,%x\n", colors[i].red, colors[i].green, colors[i].blue); #endif } else { colors[i].flags = DoRed | DoGreen | DoBlue; #ifdef WRLIB_DEBUG fprintf(stderr, "success:%x,%x,%x\n", colors[i].red, colors[i].green, colors[i].blue); #endif } } /* try to allocate close values for the colors that couldn't * be allocated before */ avncolors = (1 << ctx->depth > 256 ? 256 : 1 << ctx->depth); for (i = 0; i < avncolors; i++) avcolors[i].pixel = i; XQueryColors(ctx->dpy, ctx->cmap, avcolors, avncolors); for (i = 0; i < ncolors; i++) { if (colors[i].flags == 0) { int j; unsigned long cdiff = 0xffffffff, diff; unsigned long closest = 0; retries = 2; while (retries--) { /* find closest color */ for (j = 0; j < avncolors; j++) { r = (colors[i].red - avcolors[i].red) >> 8; g = (colors[i].green - avcolors[i].green) >> 8; b = (colors[i].blue - avcolors[i].blue) >> 8; diff = r * r + g * g + b * b; if (diff < cdiff) { cdiff = diff; closest = j; } } /* allocate closest color found */ #ifdef WRLIB_DEBUG fprintf(stderr, "best match:%x,%x,%x => %x,%x,%x\n", colors[i].red, colors[i].green, colors[i].blue, avcolors[closest].red, avcolors[closest].green, avcolors[closest].blue); #endif colors[i].red = avcolors[closest].red; colors[i].green = avcolors[closest].green; colors[i].blue = avcolors[closest].blue; if (XAllocColor(ctx->dpy, ctx->cmap, &colors[i])) { colors[i].flags = DoRed | DoGreen | DoBlue; break; /* succeeded, don't need to retry */ } #ifdef WRLIB_DEBUG fputs("close color allocation failed. Retrying...\n", stderr); #endif } } } return colors; } static Bool allocatePseudoColor(RContext *ctx) { XColor *colors; int i, ncolors, r, g, b; int cpc = ctx->attribs->colors_per_channel; ncolors = cpc * cpc * cpc; if (ncolors > (1 << ctx->depth)) { /* reduce colormap size */ cpc = ctx->attribs->colors_per_channel = 1 << ((int)ctx->depth / 3); ncolors = cpc * cpc * cpc; } assert(cpc >= 2 && ncolors <= (1 << ctx->depth)); colors = malloc(sizeof(XColor) * ncolors); if (!colors) { RErrorCode = RERR_NOMEMORY; return False; } ctx->pixels = malloc(sizeof(unsigned long) * ncolors); if (!ctx->pixels) { free(colors); RErrorCode = RERR_NOMEMORY; return False; } i = 0; if ((ctx->attribs->flags & RC_GammaCorrection) && ctx->attribs->rgamma > 0 && ctx->attribs->ggamma > 0 && ctx->attribs->bgamma > 0) { float rg, gg, bg; float tmp; /* do gamma correction */ rg = 1.0F / ctx->attribs->rgamma; gg = 1.0F / ctx->attribs->ggamma; bg = 1.0F / ctx->attribs->bgamma; for (r = 0; r < cpc; r++) { for (g = 0; g < cpc; g++) { for (b = 0; b < cpc; b++) { colors[i].red = (r * 0xffff) / (cpc - 1); colors[i].green = (g * 0xffff) / (cpc - 1); colors[i].blue = (b * 0xffff) / (cpc - 1); colors[i].flags = DoRed | DoGreen | DoBlue; tmp = (float) colors[i].red / 65536.0F; colors[i].red = (unsigned short)(65536.0F * powf(tmp, rg)); tmp = (float) colors[i].green / 65536.0F; colors[i].green = (unsigned short)(65536.0F * powf(tmp, gg)); tmp = (float) colors[i].blue / 65536.0F; colors[i].blue = (unsigned short)(65536.0F * powf(tmp, bg)); i++; } } } } else { for (r = 0; r < cpc; r++) { for (g = 0; g < cpc; g++) { for (b = 0; b < cpc; b++) { colors[i].red = (r * 0xffff) / (cpc - 1); colors[i].green = (g * 0xffff) / (cpc - 1); colors[i].blue = (b * 0xffff) / (cpc - 1); colors[i].flags = DoRed | DoGreen | DoBlue; i++; } } } } /* try to allocate the colors */ ctx->colors = allocateColor(ctx, colors, ncolors); ctx->ncolors = ncolors; /* fill the pixels shortcut array */ for (i = 0; i < ncolors; i++) { ctx->pixels[i] = ctx->colors[i].pixel; } return True; } static XColor *allocateGrayScale(RContext * ctx) { XColor *colors; int i, ncolors; int cpc = ctx->attribs->colors_per_channel; ncolors = cpc * cpc * cpc; if (ctx->vclass == StaticGray) { /* we might as well use all grays */ ncolors = 1 << ctx->depth; } else { if (ncolors > (1 << ctx->depth)) { /* reduce colormap size */ cpc = ctx->attribs->colors_per_channel = 1 << ((int)ctx->depth / 3); ncolors = cpc * cpc * cpc; } assert(cpc >= 2 && ncolors <= (1 << ctx->depth)); } if (ncolors >= 256 && ctx->vclass == StaticGray) { /* don't need dithering for 256 levels of gray in StaticGray visual */ ctx->attribs->render_mode = RBestMatchRendering; } colors = malloc(sizeof(XColor) * ncolors); if (!colors) { RErrorCode = RERR_NOMEMORY; return False; } for (i = 0; i < ncolors; i++) { colors[i].red = (i * 0xffff) / (ncolors - 1); colors[i].green = (i * 0xffff) / (ncolors - 1); colors[i].blue = (i * 0xffff) / (ncolors - 1); colors[i].flags = DoRed | DoGreen | DoBlue; } /* try to allocate the colors */ return allocateColor(ctx, colors, ncolors); } static Bool setupPseudoColorColormap(RContext * context) { Atom property = 0; if (context->attribs->standard_colormap_mode == RCreateStdColormap) { property = XInternAtom(context->dpy, "RGB_DEFAULT_MAP", False); if (!setupStandardColormap(context, property)) { return False; } } if (context->attribs->standard_colormap_mode != RIgnoreStdColormap) { XStandardColormap *maps; int count, i; if (!property) { property = XInternAtom(context->dpy, "RGB_BEST_MAP", False); if (!XGetRGBColormaps(context->dpy, DefaultRootWindow(context->dpy), &maps, &count, property)) { maps = NULL; } if (!maps) { property = XInternAtom(context->dpy, "RGB_DEFAULT_MAP", False); if (!XGetRGBColormaps(context->dpy, DefaultRootWindow(context->dpy), &maps, &count, property)) { maps = NULL; } } } else { if (!XGetRGBColormaps(context->dpy, DefaultRootWindow(context->dpy), &maps, &count, property)) { maps = NULL; } } if (maps) { int theMap = -1; for (i = 0; i < count; i++) { if (maps[i].visualid == context->visual->visualid) { theMap = i; break; } } if (theMap < 0) { fprintf(stderr, _("wrlib: no standard colormap found for visual 0x%lX\n"), context->visual->visualid); } if (theMap >= 0 && allocateStandardPseudoColor(context, &maps[theMap])) { context->std_rgb_map = XAllocStandardColormap(); *context->std_rgb_map = maps[theMap]; context->cmap = context->std_rgb_map->colormap; XFree(maps); return True; } XFree(maps); } } context->attribs->standard_colormap_mode = RIgnoreStdColormap; /* RIgnoreStdColormap and fallback */ return allocatePseudoColor(context); } static char *mygetenv(const char *var, int scr) { char *p; char varname[64]; snprintf(varname, sizeof(varname), "%s%i", var, scr); p = getenv(varname); if (!p) { p = getenv(var); } return p; } static void gatherconfig(RContext * context, int screen_n) { char *ptr; ptr = mygetenv("WRASTER_GAMMA", screen_n); if (ptr) { float g1, g2, g3; if (sscanf(ptr, "%f/%f/%f", &g1, &g2, &g3) != 3 || g1 <= 0.0F || g2 <= 0.0F || g3 <= 0.0F) { fprintf(stderr, _("wrlib: invalid value \"%s\" for %s\n"), ptr, "WRASTER_GAMMA"); } else { context->attribs->flags |= RC_GammaCorrection; context->attribs->rgamma = g1; context->attribs->ggamma = g2; context->attribs->bgamma = g3; } } ptr = mygetenv("WRASTER_COLOR_RESOLUTION", screen_n); if (ptr) { int i; if (sscanf(ptr, "%d", &i) != 1 || i < 2 || i > 6) { fprintf(stderr, _("wrlib: invalid value \"%s\" for %s\n"), ptr, "WRASTER_COLOR_RESOLUTION"); } else { context->attribs->flags |= RC_ColorsPerChannel; context->attribs->colors_per_channel = i; } } } static void getColormap(RContext * context, int screen_number) { Colormap cmap = None; XStandardColormap *cmaps; int ncmaps, i; if (XGetRGBColormaps(context->dpy, RootWindow(context->dpy, screen_number), &cmaps, &ncmaps, XA_RGB_DEFAULT_MAP)) { for (i = 0; i < ncmaps; ++i) { if (cmaps[i].visualid == context->visual->visualid) { cmap = cmaps[i].colormap; break; } } XFree(cmaps); } if (cmap == None) { XColor color; cmap = XCreateColormap(context->dpy, RootWindow(context->dpy, screen_number), context->visual, AllocNone); color.red = color.green = color.blue = 0; XAllocColor(context->dpy, cmap, &color); context->black = color.pixel; color.red = color.green = color.blue = 0xffff; XAllocColor(context->dpy, cmap, &color); context->white = color.pixel; } context->cmap = cmap; } static int count_offset(unsigned long mask) { int i; i = 0; while ((mask & 1) == 0) { i++; mask = mask >> 1; } return i; } RContext *RCreateContext(Display * dpy, int screen_number, const RContextAttributes * attribs) { RContext *context; XGCValues gcv; context = malloc(sizeof(RContext)); if (!context) { RErrorCode = RERR_NOMEMORY; return NULL; } memset(context, 0, sizeof(RContext)); context->dpy = dpy; context->screen_number = screen_number; context->attribs = malloc(sizeof(RContextAttributes)); if (!context->attribs) { free(context); RErrorCode = RERR_NOMEMORY; return NULL; } if (!attribs) *context->attribs = DEFAULT_CONTEXT_ATTRIBS; else *context->attribs = *attribs; if (!(context->attribs->flags & RC_StandardColormap)) { context->attribs->standard_colormap_mode = RUseStdColormap; } if (!(context->attribs->flags & RC_ScalingFilter)) { context->attribs->flags |= RC_ScalingFilter; context->attribs->scaling_filter = RMitchellFilter; } /* get configuration from environment variables */ gatherconfig(context, screen_number); wraster_change_filter(context->attribs->scaling_filter); if ((context->attribs->flags & RC_VisualID)) { XVisualInfo *vinfo, templ; int nret; templ.screen = screen_number; templ.visualid = context->attribs->visualid; vinfo = XGetVisualInfo(context->dpy, VisualIDMask | VisualScreenMask, &templ, &nret); if (!vinfo || nret == 0) { free(context); RErrorCode = RERR_BADVISUALID; return NULL; } if (vinfo[0].visual == DefaultVisual(dpy, screen_number)) { context->attribs->flags |= RC_DefaultVisual; } else { XSetWindowAttributes attr; unsigned long mask; context->visual = vinfo[0].visual; context->depth = vinfo[0].depth; context->vclass = vinfo[0].class; getColormap(context, screen_number); attr.colormap = context->cmap; attr.override_redirect = True; attr.border_pixel = 0; attr.background_pixel = 0; mask = CWBorderPixel | CWColormap | CWOverrideRedirect | CWBackPixel; context->drawable = XCreateWindow(dpy, RootWindow(dpy, screen_number), 1, 1, 1, 1, 0, context->depth, CopyFromParent, context->visual, mask, &attr); } XFree(vinfo); } /* use default */ if (!context->visual) { if ((context->attribs->flags & RC_DefaultVisual) || !bestContext(dpy, screen_number, context)) { context->visual = DefaultVisual(dpy, screen_number); context->depth = DefaultDepth(dpy, screen_number); context->cmap = DefaultColormap(dpy, screen_number); context->drawable = RootWindow(dpy, screen_number); context->black = BlackPixel(dpy, screen_number); context->white = WhitePixel(dpy, screen_number); context->vclass = context->visual->class; } } gcv.function = GXcopy; gcv.graphics_exposures = False; context->copy_gc = XCreateGC(dpy, context->drawable, GCFunction | GCGraphicsExposures, &gcv); if (context->vclass == PseudoColor || context->vclass == StaticColor) { if (!setupPseudoColorColormap(context)) { free(context); return NULL; } } else if (context->vclass == GrayScale || context->vclass == StaticGray) { context->colors = allocateGrayScale(context); if (!context->colors) { free(context); return NULL; } } else if (context->vclass == TrueColor) { /* calc offsets to create a TrueColor pixel */ context->red_offset = count_offset(context->visual->red_mask); context->green_offset = count_offset(context->visual->green_mask); context->blue_offset = count_offset(context->visual->blue_mask); /* disable dithering on 24 bits visuals */ if (context->depth >= 24) context->attribs->render_mode = RBestMatchRendering; } /* check avaiability of MIT-SHM */ #ifdef USE_XSHM if (!(context->attribs->flags & RC_UseSharedMemory)) { context->attribs->flags |= RC_UseSharedMemory; context->attribs->use_shared_memory = True; } if (context->attribs->use_shared_memory) { int major, minor; Bool sharedPixmaps; context->flags.use_shared_pixmap = 0; if (!XShmQueryVersion(context->dpy, &major, &minor, &sharedPixmaps)) { context->attribs->use_shared_memory = False; } else { if (XShmPixmapFormat(context->dpy) == ZPixmap) context->flags.use_shared_pixmap = sharedPixmaps; } } #endif return context; } void RDestroyContext(RContext *context) { if (context) { if (context->copy_gc) XFreeGC(context->dpy, context->copy_gc); if (context->attribs) { if ((context->attribs->flags & RC_VisualID) && !(context->attribs->flags & RC_DefaultVisual)) XDestroyWindow(context->dpy, context->drawable); free(context->attribs); } free(context); } } static Bool bestContext(Display * dpy, int screen_number, RContext * context) { XVisualInfo *vinfo = NULL, rvinfo; int best = -1, numvis, i; long flags; XSetWindowAttributes attr; rvinfo.class = TrueColor; rvinfo.screen = screen_number; flags = VisualClassMask | VisualScreenMask; vinfo = XGetVisualInfo(dpy, flags, &rvinfo, &numvis); if (vinfo) { /* look for a TrueColor, 24-bit or more (pref 24) */ for (i = numvis - 1, best = -1; i >= 0; i--) { if (vinfo[i].depth == 24) best = i; else if (vinfo[i].depth > 24 && best < 0) best = i; } } if (best > -1) { context->visual = vinfo[best].visual; context->depth = vinfo[best].depth; context->vclass = vinfo[best].class; getColormap(context, screen_number); attr.colormap = context->cmap; attr.override_redirect = True; attr.border_pixel = 0; context->drawable = XCreateWindow(dpy, RootWindow(dpy, screen_number), 1, 1, 1, 1, 0, context->depth, CopyFromParent, context->visual, CWBorderPixel | CWColormap | CWOverrideRedirect, &attr); } if (vinfo) XFree((char *)vinfo); if (best < 0) return False; else return True; }