/* * render.c * * Render a high dynamic range buffer in some sensible way * * (c) 2006-2011 Thomas White * * Part of CrystFEL - crystallography with a FEL * */ #ifdef HAVE_CONFIG_H #include #endif #ifdef HAVE_GTK #include #endif #include #include #include #ifdef HAVE_LIBPNG #include #endif #ifdef HAVE_TIFF #include #endif #include "hdf5-file.h" #include "render.h" #include "peaks.h" #include "filters.h" #include "utils.h" static void render_rgb(float val, float max, float *rp, float *gp, float *bp) { int s; float p; float r, g, b; s = val / (max/6); p = fmod(val, max/6.0); p /= (max/6.0); r = 0.0; g = 0.0; b = 0.0; if ( (val < 0.0) ) { s = 0; p = 0; } if ( (val > max) ) { s = 6; } switch ( s ) { case 0 : { /* Black to blue */ r = 0; g = 0; b = p; break; } case 1 : { /* Blue to pink */ r = p; g = 0; b = 1.0; break; } case 2 : { /* Pink to red */ r = 1.0; g = 0; b = (1.0-p)*1.0; break; } case 3 : { /* Red to Orange */ r = 1.0; g = 0.5*p; b = 0; break; } case 4 : { /* Orange to Yellow */ r = 1.0; g = 0.5 + 0.5*p; b = 0; break; } case 5 : { /* Yellow to White */ r = 1.0; g = 1.0; b = 1.0*p; break; } case 6 : { /* Pixel has hit the maximum value */ r = 1.0; g = 1.0; b = 1.0; break; } } *rp = r; *gp = g; *bp = b; } static void render_mono(float val, float max, float *rp, float *gp, float *bp) { float p; p = val / max; if ( val < 0.0 ) p = 0.0; if ( val > max ) p = 1.0; *rp = p; *gp = p; *bp = p; } static void render_invmono(float val, float max, float *rp, float *gp, float *bp) { float p; p = val / max; p = 1.0 - p; if ( val < 0.0 ) p = 1.0; if ( val > max ) p = 0.0; *rp = p; *gp = p; *bp = p; } void render_scale(float val, float max, int scale, float *rp, float *gp, float *bp) { switch ( scale ) { case SCALE_COLOUR : render_rgb(val, max, rp, gp, bp); break; case SCALE_MONO : render_mono(val, max, rp, gp, bp); break; case SCALE_INVMONO : render_invmono(val, max, rp, gp, bp); break; } } #ifdef HAVE_GTK static float *get_binned_image(struct image *image, int binning, float *pmax) { float *data; int x, y; int w, h; int inw, inh; float *in; float max; inw = image->width; inh = image->height; in = image->data; w = inw / binning; h = inh / binning; /* Some pixels might get discarded */ data = malloc(w*h*sizeof(float)); max = 0.0; for ( x=0; x max ) max = data[x+w*y]; } } *pmax = max; return data; } /* NB This function is shared between render_get_image() and * render_get_colour_scale() */ static void render_free_data(guchar *data, gpointer p) { free(data); } /* FIXME: This doesn't belong here at all */ static void show_marked_features(struct image *image, guchar *data, int w, int h, int binning) { int i; float r = 10.0/binning; if ( image->features == NULL ) return; for ( i=0; ifeatures); i++ ) { struct imagefeature *f; float x, y; double th; f = image_get_feature(image->features, i); if ( f == NULL ) continue; x = f->x / (float)binning; y = f->y / (float)binning; for ( th=0; th<2*M_PI; th+=M_PI/40.0 ) { int nx, ny; nx = x + r*cos(th); ny = y + r*sin(th); if ( nx < 0 ) continue; if ( ny < 0 ) continue; if ( nx >= w ) continue; if ( ny >= h ) continue; data[3*( nx+w*(h-1-ny) )+0] = 128; data[3*( nx+w*(h-1-ny) )+1] = 128; data[3*( nx+w*(h-1-ny) )+2] = 30; } } } /* Return a pixbuf containing a rendered version of the image after binning. * This pixbuf might be scaled later - hopefully mostly in a downward * direction. */ GdkPixbuf *render_get_image(struct image *image, int binning, int scale, double boost) { int w, h; guchar *data; float *hdr; int x, y; float max; int mw, mh; mw = image->width; mh = image->height; w = mw / binning; h = mh / binning; /* High dynamic range version */ hdr = get_binned_image(image, binning, &max); if ( hdr == NULL ) return NULL; /* Rendered (colourful) version */ data = malloc(3*w*h); if ( data == NULL ) { free(hdr); return NULL; } max /= boost; if ( max <= 6 ) { max = 10; } /* These x,y coordinates are measured relative to the bottom-left * corner */ for ( y=0; ywidth); TIFFSetField(th, TIFFTAG_IMAGELENGTH, image->height); TIFFSetField(th, TIFFTAG_SAMPLESPERPIXEL, 1); TIFFSetField(th, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP); TIFFSetField(th, TIFFTAG_BITSPERSAMPLE, 32); TIFFSetField(th, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); TIFFSetField(th, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); TIFFSetField(th, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField(th, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(th, image->width*4)); line = _TIFFmalloc(TIFFScanlineSize(th)); for ( y=0; yheight; y++ ) { memcpy(line, &image->data[(image->height-1-y)*image->width], image->width*4); TIFFWriteScanline(th, line, y, 0); } _TIFFfree(line); TIFFClose(th); #else STATUS("No TIFF support.\n"); #endif return 0; } int render_tiff_int16(struct image *image, const char *filename, double boost) { #ifdef HAVE_TIFF TIFF *th; int16_t *line; int x, y; float max; th = TIFFOpen(filename, "w"); if ( th == NULL ) return 1; TIFFSetField(th, TIFFTAG_IMAGEWIDTH, image->width); TIFFSetField(th, TIFFTAG_IMAGELENGTH, image->height); TIFFSetField(th, TIFFTAG_SAMPLESPERPIXEL, 1); TIFFSetField(th, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_INT); /* (signed) */ TIFFSetField(th, TIFFTAG_BITSPERSAMPLE, 16); TIFFSetField(th, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); TIFFSetField(th, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); TIFFSetField(th, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField(th, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(th, image->width*4)); line = _TIFFmalloc(TIFFScanlineSize(th)); max = 0.0; for ( y=0; yheight; y++ ) { for ( x=0;xwidth; x++ ) { float val; val = image->data[x+image->height*y]; if ( val > max ) max = val; } } max /= 32767.0; for ( y=0; yheight; y++ ) { for ( x=0;xwidth; x++ ) { float val; val = image->data[x+(image->height-1-y)*image->width]; val *= ((float)boost/max); /* Clamp to 16-bit range, * and work round inability of most readers to deal * with signed integers. */ val += 1000.0; if ( val > 32767.0 ) val = 32767.0; if ( val < 0.0 ) val = 0.0; line[x] = val; } TIFFWriteScanline(th, line, y, 0); } _TIFFfree(line); TIFFClose(th); #else STATUS("No TIFF support.\n"); #endif return 0; } #endif /* HAVE_GTK */