/* Part of XPCE --- The SWI-Prolog GUI toolkit Author: Jan Wielemaker and Anjo Anjewierden E-mail: jan@swi.psy.uva.nl WWW: http://www.swi.psy.uva.nl/projects/xpce/ Copyright (c) 1995-2011, University of Amsterdam All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The XPCE PNM interface. Converts between X11 images and p[bgp]m images for loading and saving XPCE bitmaps in a form accessible for other applications. PNM (Portable aNy Map) is a public simple format for exchanging bitmap/pixmap information. It has 6 flavours, three of them are `rawbits' (binary) versions of the three main formats: PBM (Portable BitMap), PGM (Portable GrayMap) and PPM (Portable PixMap). The PNM package is public domain and available as pbmplus10dec91.tar.gz on many anonymous ftp servers. Search for pbmplus is you want to check for more recent releases. The magnificient tool xv by John Bradley (bradley@cis.upenn.edu) version 3 can also generate ppm files. This module implements two additional formats: PGM and PPM PNM_RUNLEN encoded files. These are similar to the RAWBIT files, but encode their info as tuples . These files are normally more compact and read/write faster. As yet this format is exclusively used in saved object (object ->save_in_file). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include #include "include.h" #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_MALLOC_H #include #endif #define BRIGHT ((1L<<16)-1) #define NOPIXEL (~0UL) #undef roundup #define valdigit(d) ((d) - '0') #define roundup(v, n) ((((v)+(n)-1)/(n))*(n)) #define rescale(v, o, n) ((v) * (n) / (o)) #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif #define Symbol PixSymbol /* name-clash with kernel */ #define symbol pix_symbol static int ncolours; /* colours in image */ static int nmapped; /* remapped colours */ static int nfailed; /* failed conversions */ /******************************* * HASH-TABLE FOR PIXEL LOOKUP * *******************************/ typedef struct symbol *Symbol; typedef struct table *Table; struct symbol { unsigned long name; unsigned long value; Symbol next; }; struct table { int size; Symbol symbols[1]; }; #define hashvalue(size, v) ((v) % size) static Table newTable(int size) { Table t = (Table)pceMalloc(sizeof(struct table) + (size-1) * sizeof(Symbol)); int i; Symbol *s; t->size = size; for(i=size, s=t->symbols; --i >= 0; s++) *s = NULL; return t; } static void freeTable(Table t) { Symbol *s; int i; for(i=t->size, s=t->symbols; --i >= 0; s++) { Symbol n, m = *s; for( ; m; m = n) { n = m->next; pceFree(m); } } pceFree(t); } static void addTable(Table t, unsigned long name, unsigned long value) { Symbol *l = &t->symbols[hashvalue(t->size, name)]; Symbol s = (Symbol)pceMalloc(sizeof(struct symbol)); s->name = name; s->value = value; s->next = *l; *l = s; } static unsigned long memberTable(Table t, unsigned long name) { Symbol s = t->symbols[hashvalue(t->size, name)]; for(; s; s = s->next) { if ( s->name == name ) return s->value; } return NOPIXEL; } /******************************* * COLOUR PIXELS * *******************************/ static unsigned long colourPixel(Display *disp, int depth, Colormap cmap, Table t, int r, int g, int b) { unsigned long pixel; unsigned long direct = (r << 16) + (g << 8) + b; XColor c; if ( (pixel = memberTable(t, direct)) != NOPIXEL ) return pixel; ncolours++; c.red = r * 257; c.green = g * 257; c.blue = b * 257; if ( !XAllocColor(disp, cmap, &c) ) { if ( !allocNearestColour(disp, cmap, depth, DEFAULT, &c) ) { Cprintf("PNM: failed to alloc pixel %d/%d/%d\n", r, g, b); c.pixel = 0; nfailed++; } } addTable(t, direct, c.pixel); DEBUG(NAME_ppm, Cprintf("PNM: Colour %d %d %d on pixel %d\n", r, g, b, c.pixel)); return c.pixel; } /******************************* * ASCII FORMAT PARSING * *******************************/ static int getNum(IOSTREAM *fd) { int c; int v; for(;;) { do { c = Sgetc(fd); } while(isspace(c)); if ( isdigit(c) ) { v = valdigit(c); for(;;) { c = Sgetc(fd); if ( isdigit(c) ) v = v*10 + valdigit(c); else break; } if ( !isspace(c) ) Sungetc(c, fd); return v; } if ( c == '#' ) { do { c = Sgetc(fd); } while( c != '\n' && c != EOF ); } else return -1; } } XImage * read_ppm_file(Display *disp, Colormap cmap, int depth, IOSTREAM *fd) { XImage *img; long here = Stell(fd); int c; int fmt, encoding; int width, height, bytes_per_line, scale=0; char *data; int allocdepth; int pad = XBitmapPad(disp); Visual *v = DefaultVisual(disp, DefaultScreen(disp)); ncolours = nmapped = nfailed = 0; /* statistics */ assert(pad%8 == 0); if ( (c=Sgetc(fd)) != 'P' ) { Sungetc(c, fd); return NULL; } if ( !cmap ) cmap = DefaultColormap(disp, DefaultScreen(disp)); c = Sgetc(fd); if ( c < '1' || c > '9' ) goto errout; c -= '0'; fmt = ((c - 1) % 3) + 1; encoding = c - fmt; width = getNum(fd); height = getNum(fd); if ( fmt == PNM_PBM ) { depth = 1; } else { scale = getNum(fd); if ( !depth ) depth = DefaultDepth(disp, DefaultScreen(disp)); } if ( width < 0 || height < 0 || scale < 0 ) goto errout; allocdepth = (depth >= 24 ? 32 : depth); bytes_per_line = roundup((width*allocdepth+7)/8, pad/8); data = (char *)pceMalloc(height * bytes_per_line); img = XCreateImage(disp, v, depth, fmt == PNM_PBM ? XYBitmap : ZPixmap, 0, data, width, height, pad, bytes_per_line); if ( !img ) { perror("XCreateImage"); pceFree(data); goto errout; } img->bits_per_pixel = depth; switch(encoding) { int x, y; case PNM_ASCII: { switch(fmt) { case PNM_PBM: for(y=0; y 1 ) goto errout; XPutPixel(img, x, y, value); } } break; case PNM_PGM: { Table t = newTable(64); for(y=0; y scale ) goto errout; if ( scale != 255 ) g = rescale(g, scale, 255); pixel = colourPixel(disp, depth, cmap, t, g, g, g); XPutPixel(img, x, y, pixel); } } freeTable(t); break; } case PNM_PPM: { Table t = newTable(64); for(y=0; y scale || g < 0 || g > scale || b < 0 || b > scale ) goto errout; if ( scale != 255 ) { r = rescale(r, scale, 255); g = rescale(g, scale, 255); b = rescale(b, scale, 255); } pixel = colourPixel(disp, depth, cmap, t, r, g, b); XPutPixel(img, x, y, pixel); } } freeTable(t); break; } break; } break; } case PNM_RAWBITS: { switch(fmt) { case PNM_PBM: { int byte = 0; int bit = 0; for(y=0; y scale ) goto errout; if ( scale != 255 ) g = rescale(g, scale, 255); pixel = colourPixel(disp, depth, cmap, t, g, g, g); XPutPixel(img, x, y, pixel); } } freeTable(t); break; } case PNM_PPM: { Table t = newTable(64); for(y=0; y scale || (g=Sgetc(fd)) > scale || (b=Sgetc(fd)) > scale ) goto errout; if ( scale != 255 ) { r = rescale(r, scale, 255); g = rescale(g, scale, 255); b = rescale(b, scale, 255); } pixel = colourPixel(disp, depth, cmap, t, r, g, b); XPutPixel(img, x, y, pixel); } } freeTable(t); break; } break; } break; } case PNM_RUNLEN: { int rlen = 0; unsigned long cpixel = NOPIXEL; switch(fmt) { case PNM_PGM: { Table t = newTable(64); DEBUG(NAME_pnm, Cprintf("Reading runlength encoded graymap\n")); for(y=0; y 0 ) { XPutPixel(img, x, y, cpixel); } else { int g; if ( (g=Sgetc(fd)) > scale || (rlen = Sgetc(fd)) == EOF ) goto errout; rlen &= 0xff; if ( scale != 255 ) g = rescale(g, scale, 255); cpixel = colourPixel(disp, depth, cmap, t, g, g, g); XPutPixel(img, x, y, cpixel); rlen--; } } } freeTable(t); break; } case PNM_PPM: { Table t = newTable(64); for(y=0; y 0 ) { XPutPixel(img, x, y, cpixel); } else { int r, g, b; if ( (r=Sgetc(fd)) > scale || (g=Sgetc(fd)) > scale || (b=Sgetc(fd)) > scale || (rlen = Sgetc(fd)) == EOF ) goto errout; rlen &= 0xff; if ( scale != 255 ) { r = rescale(r, scale, 255); g = rescale(g, scale, 255); b = rescale(b, scale, 255); } cpixel = colourPixel(disp, depth, cmap, t, r, g, b); XPutPixel(img, x, y, cpixel); rlen--; } } } freeTable(t); break; } } } } DEBUG(NAME_ppm, Cprintf("PNM: Converted %dx%dx%d image, %d colours (%d mapped, %d failed)\n", width, height, depth, ncolours, nmapped, nfailed)); return img; errout: DEBUG(NAME_ppm, Cprintf("PNM: Format error, index = %d\n", Stell(fd))); Sseek(fd, here, SEEK_SET); return NULL; } /******************************* * WRITING * *******************************/ static int file_col; static int putNum(int n, IOSTREAM *fd) { if ( file_col != 0 && Sputc(' ', fd) == EOF ) return -1; do { if ( Sputc(n % 10 + '0', fd) == EOF ) return -1; file_col++; n /= 10; } while( n > 0 ); if ( file_col >= 70 ) { if ( Sputc('\n', fd) == EOF ) return -1; file_col = 0; } return 0; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Low-level XImage hacking. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ typedef struct { XColor *cinfo; /* a colour-info vector */ int r_shift; /* red-shift */ int g_shift; /* green-shift */ int b_shift; /* blue-shift */ int r_fill; /* shift to make 16-bit */ int g_fill; /* shift to make 16-bit */ int b_fill; /* shift to make 16-bit */ XColor xc; /* static one */ } XPixelInfo; static int mask_width(unsigned long mask) { unsigned long m = 0x1; int width = 0; while((mask&m) == 0) m <<= 1; while((mask&m)) { m <<= 1; width++; } return width; } static XColor * pixelToColor(XImage *im, unsigned long pixel, XPixelInfo *info) { if ( info->cinfo ) { return &info->cinfo[pixel]; } else { info->xc.red = (pixel >> info->r_shift) << info->r_fill; info->xc.green = (pixel >> info->g_shift) << info->g_fill; info->xc.blue = (pixel >> info->b_shift) << info->b_fill; return &info->xc; } } static void makeXPixelInfo(XPixelInfo *info, XImage *img, Display *disp, Colormap cmap) { if ( img->depth <= 8 ) { XColor *cdata = info->cinfo; int entries = 1<depth; int i; for(i=0; icinfo = 0; info->r_shift = shift_for_mask(img->red_mask); info->g_shift = shift_for_mask(img->green_mask); info->b_shift = shift_for_mask(img->blue_mask); info->r_fill = 16 - mask_width(img->red_mask); info->g_fill = 16 - mask_width(img->green_mask); info->b_fill = 16 - mask_width(img->blue_mask); } } int write_pnm_file(IOSTREAM *fd, XImage *img, Display *disp, Colormap cmap, int scale, int fmt, int encode) { int width = img->width; int height = img->height; XColor cdata[256]; XPixelInfo info; int x, y; if ( !scale ) scale = 255; if ( !fmt ) { if ( img->format == XYBitmap ) fmt = PNM_PBM; else fmt = PNM_PPM; } if ( fmt == PNM_PBM && encode == PNM_RUNLEN ) encode = PNM_RAWBITS; /* no use to runlen encode a bitmap */ if ( img->format != XYBitmap ) { info.cinfo = cdata; makeXPixelInfo(&info, img, disp, cmap); } Sfprintf(fd, "P%c\n", fmt + encode + '0'); Sfprintf(fd, "# Creator: XPCE version %s\n", strName(get(PCE,NAME_version,EAV))); Sfprintf(fd, "%d %d\n", width, height); if ( fmt != PNM_PBM ) Sfprintf(fd, "%d\n", scale); file_col = 0; switch(encode) { case PNM_ASCII: { switch(fmt) { case PNM_PBM: { for(y=0; yformat != XYBitmap ) { XColor *c; int r; c = pixelToColor(img, pixel, &info); r = intensityXColor(c); pixel = r < 32768 ? 1 : 0; } if ( putNum(pixel, fd) < 0 ) return -1; } } break; } case PNM_PGM: { for(y=0; yred, BRIGHT, scale); g = rescale(c->green, BRIGHT, scale); b = rescale(c->blue, BRIGHT, scale); if ( putNum(r, fd) < 0 || putNum(g, fd) < 0 || putNum(b, fd) < 0 ) return -1; } } break; } } if ( file_col && Sputc('\n', fd) == EOF ) return -1; file_col = 0; } case PNM_RAWBITS: { switch(fmt) { case PNM_PBM: { int byte = 0; int bit = 7; for(y=0; yformat != XYBitmap ) { XColor *c; int r; c = pixelToColor(img, pixel, &info); r = intensityXColor(c); pixel = r < 32768 ? 1 : 0; } if ( pixel ) byte |= 1<red, BRIGHT, scale); g = rescale(c->green, BRIGHT, scale); b = rescale(c->blue, BRIGHT, scale); if ( Sputc(r, fd) == EOF || Sputc(g, fd) == EOF || Sputc(b, fd) == EOF ) return -1; } } break; } } } case PNM_RUNLEN: { int rlen=-1; unsigned long cpixel = NOPIXEL; switch(fmt) { case PNM_PGM: { for(y=0; y 0 && Sputc(rlen, fd) == EOF ) return -1; cpixel = pixel; rlen = 1; c = pixelToColor(img, pixel, &info); r = intensityXColor(c); r = rescale(r, BRIGHT, scale); if ( Sputc(r, fd) == EOF ) return -1; } } } if ( Sputc(rlen, fd) == EOF ) return -1; break; } case PNM_PPM: { for(y=0; y 0 && Sputc(rlen, fd) == EOF ) return -1; cpixel = pixel; rlen = 1; c = pixelToColor(img, pixel, &info); r = rescale(c->red, BRIGHT, scale); g = rescale(c->green, BRIGHT, scale); b = rescale(c->blue, BRIGHT, scale); if ( Sputc(r, fd) == EOF || Sputc(g, fd) == EOF || Sputc(b, fd) == EOF ) return -1; } } } if ( Sputc(rlen, fd) == EOF ) return -1; break; } } } } return 0; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Translate an XImage and an optional mask into an RBGA image in a format that is suitable for _NET_WM_ICON. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ unsigned long * XImageToRGBA(XImage *img, XImage *msk, Display *disp, Colormap cmap, size_t *lenp) { int width = img->width; int height = img->height; XPixelInfo img_info; XPixelInfo msk_info; XColor img_cdata[256]; XColor msk_cdata[256]; int msk_is_bitmap = TRUE; int x, y; unsigned long *data, *p; const int scale = 255; size_t len; if ( msk && (msk->height != height || msk->width != width) ) msk = NULL; if ( img->format != XYBitmap ) { img_info.cinfo = img_cdata; makeXPixelInfo(&img_info, img, disp, cmap); } if ( msk ) { if ( msk->format == XYBitmap || (msk->format == ZPixmap && msk->bits_per_pixel == 1) ) { msk_is_bitmap = TRUE; } else { msk_is_bitmap = FALSE; msk_info.cinfo = msk_cdata; makeXPixelInfo(&msk_info, msk, disp, cmap); } } len = 2+width*height; if ( lenp ) *lenp = len; if ( !(data = malloc(len*sizeof(*data))) ) return data; p = data; *p++ = width; *p++ = height; for(y=0; yred, BRIGHT, scale); g = rescale(c->green, BRIGHT, scale); b = rescale(c->blue, BRIGHT, scale); if ( msk ) { unsigned long pixel = XGetPixel(msk, x, y); if ( msk_is_bitmap ) { a = pixel ? scale : 0; } else { XColor *c; int r; c = pixelToColor(img, pixel, &msk_info); r = intensityXColor(c); a = rescale(r, BRIGHT, scale); } } else { a = 255; } upix.c32[0] = b; /* blue */ upix.c32[1] = g; /* green */ upix.c32[2] = r; /* red */ upix.c32[3] = a; *p++ = upix.i32; } } return data; }