Logo Search packages:      
Sourcecode: ufraw version File versions

ufraw_preview.c

/*
   UFRaw - Unidentified Flying Raw
   Raw photo loader plugin for The GIMP
   by udi Fuchs,

   based on the gimp plug-in by Pawel T. Jochym jochym at ifj edu pl,
   
   based on the gimp plug-in by Dave Coffin
   http://www.cybercom.net/~dcoffin/

   UFRaw is licensed under the GNU General Public License.
   It uses "dcraw" code to do the actual raw decoding.
*/

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <errno.h>
#include <gtk/gtk.h>
#include "ufraw.h"
#include "ufraw_icon.h"

/* Set to huge number so that the preview size is set by the screen size */
const int def_preview_width = 9000;
const int def_preview_height = 9000;
const int raw_his_size = 365;
const int raw_his_height = 128;
#define live_his_size 256
const int live_his_height = 128;

enum { pixel_format, percent_format };

char *expanderText[] = { "Raw histogram", "White balance", "Color management",
    "Curve corrections", "Live histogram", NULL };

typedef struct {
    GtkLabel *labels[4];
    int num;
    int format;
} colorLabels;

/* Global variables for holding information on the preview image */
image_data *previewImage;
#define Developer previewImage->developer
/* Some actions update the progress bar while working, but meanwhile we want
 * to freeze all other actions. After we thaw the dialog we must call
 * update_scales() which was also frozen. */
gboolean freezeDialog, spotOn;
int spotx, spoty;

/* Remember the Gtk Widgets that we need to access later */
GdkPixbuf *rawHistoInitPixbuf;
GtkWidget *previewWidget, *previewVBox, *rawHisto, *liveHisto;
GtkProgressBar *progressBar = NULL;
GtkAdjustment *adjTemperature, *adjGreen, *adjExposure, *adjProfileGamma,
          *adjGamma, *adjContrast, *adjSaturation, *adjBrightness,
          *adjBlack, *adjShadow, *adjDepth;
GtkComboBox *wbCombo, *curveCombo, *profileCombo[profile_types];
GtkTooltips *toolTips;
GtkWidget *gammaWidget[3], *contrastWidget[3];
GtkLabel *spotPatch;
colorLabels *spotLabels, *avrLabels, *devLabels, *overLabels, *underLabels;

/* cfg holds infomation that is kept for the next image load */
cfg_data *cfg;
cfg_data save_cfg;

#define CFG_cameraCurve (cfg->curve[camera_curve].curveSize!=0)
#define CFG_gamma       cfg->curve[cfg->curveIndex].gamma
#define CFG_contrast    cfg->curve[cfg->curveIndex].contrast
#define CFG_saturation  cfg->curve[cfg->curveIndex].saturation
#define CFG_brightness  cfg->curve[cfg->curveIndex].brightness
#define CFG_black       cfg->curve[cfg->curveIndex].black
#define CFG_shadow      cfg->curve[cfg->curveIndex].shadow
#define CFG_depth       cfg->curve[cfg->curveIndex].depth

void window_response(GtkWidget *widget, gpointer user_data)
{
    GtkWindow *window = GTK_WINDOW(gtk_widget_get_toplevel(widget));

    g_object_set_data(G_OBJECT(window), "WindowResponse", user_data);
    gtk_main_quit();
}

void load_curve(GtkWidget *widget, gpointer user_data)
{
    GtkFileChooser *fileChooser;
    void *parentWindow;
    GSList *list, *saveList;
    curve_data c;
    char *cp;

    user_data = user_data;
    if (freezeDialog) return;
    if (cfg->curveCount==max_curves) {
        ufraw_message(UFRAW_ERROR, "No more room for new curves.");
        return;
    }
    fileChooser = GTK_FILE_CHOOSER(gtk_file_chooser_dialog_new("Load curve",
            GTK_WINDOW(gtk_widget_get_toplevel(widget)),
            GTK_FILE_CHOOSER_ACTION_OPEN,
            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
            GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL));
    parentWindow = ufraw_message_handler(UFRAW_SET_PARENT,
            (char *)fileChooser);
    gtk_file_chooser_set_select_multiple(fileChooser, TRUE);
    if (strlen(cfg->curvePath)>0)
        gtk_file_chooser_set_current_folder(fileChooser, cfg->curvePath);
    if (gtk_dialog_run(GTK_DIALOG(fileChooser))==GTK_RESPONSE_ACCEPT) {
        for (list=saveList=gtk_file_chooser_get_filenames(fileChooser);
            list!=NULL && cfg->curveCount<max_curves;
            list=g_slist_next(list)) {
            c = cfg_default.curve[0];
            if (curve_load(&c, list->data)!=UFRAW_SUCCESS) continue;
            gtk_combo_box_append_text(curveCombo, c.name);
            cfg->curve[cfg->curveCount++] = c;
            cfg->curveIndex = cfg->curveCount-1;
            if (CFG_cameraCurve)
                gtk_combo_box_set_active(curveCombo, cfg->curveIndex);
            else
                gtk_combo_box_set_active(curveCombo, cfg->curveIndex-1);
            cp = g_path_get_dirname(list->data);
            g_strlcpy(cfg->curvePath, cp, max_path);
            g_free(cp);
            g_free(list->data);
        }
        if (list!=NULL)
        ufraw_message(UFRAW_ERROR, "No more room for new curves.");
        g_slist_free(saveList);
    }
    gtk_widget_destroy(GTK_WIDGET(fileChooser));
    ufraw_message_handler(UFRAW_SET_PARENT, parentWindow);
}

void save_curve(GtkWidget *widget, gpointer user_data)
{
    GtkFileChooser *fileChooser;
    void *parentWindow;
    guint8 imageCurve8[0x10000];
    guint16 imageCurve16[0x10000];
    char *filename;
    curve_data c;
    FILE *out;
    int indexSize, i;
    char *cp;

    user_data = user_data;
    if (freezeDialog) return;
    indexSize = cfg->curve[MAX(cfg->curveIndex, camera_curve)].indexSize;
    if (indexSize==0) indexSize = 0xFFFF;
    set_curve(imageCurve16, indexSize,
            cfg->curveIndex, &cfg->curve[cfg->curveIndex]);
    for (i=0; i<0x10000; i++) imageCurve8[i] = imageCurve16[i] >> 8;
    curve_convert(&c, indexSize, imageCurve8);

    fileChooser = GTK_FILE_CHOOSER(gtk_file_chooser_dialog_new("Save curve",
            GTK_WINDOW(gtk_widget_get_toplevel(widget)),
            GTK_FILE_CHOOSER_ACTION_SAVE,
            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
            GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL));
    parentWindow = ufraw_message_handler(UFRAW_SET_PARENT,
            (char *)fileChooser);
    if (strlen(cfg->curvePath)>0)
        gtk_file_chooser_set_current_folder(fileChooser, cfg->curvePath);
    if (gtk_dialog_run(GTK_DIALOG(fileChooser))==GTK_RESPONSE_ACCEPT) {
        filename = gtk_file_chooser_get_filename(fileChooser);
        if ( (out=fopen(filename, "w"))==NULL ) {
            ufraw_message(UFRAW_ERROR, "Error opening file '%s': %s",
                          filename, g_strerror(errno));
        } else {
            for (i=0; i<c.curveSize; i++)
                fprintf(out, "%5d %3d\n", c.curve[i].index, c.curve[i].value);
            fprintf(out, "%5d %3d\n", c.indexSize-1, c.curve[i-1].value);
            fclose(out);
        }
        cp = g_path_get_dirname(filename);
        g_strlcpy(cfg->curvePath, cp, max_path);
        g_free(cp);
        g_free(filename);
    }
    gtk_widget_destroy(GTK_WIDGET(fileChooser));
    ufraw_message_handler(UFRAW_SET_PARENT, parentWindow);
}

void load_profile(GtkWidget *widget, long type)
{
    GtkFileChooser *fileChooser;
    void *parentWindow;
    GSList *list, *saveList;
    GtkFileFilter *filter;
    char *filename, *cp;
    profile_data p;
    unsigned i;

    if (freezeDialog) return;
    if (cfg->profileCount[type]==max_profiles) {
        ufraw_message(UFRAW_ERROR, "No more room for new profiles.");
        return;
    }
    fileChooser = GTK_FILE_CHOOSER(gtk_file_chooser_dialog_new(
            "Load color profile",
            GTK_WINDOW(gtk_widget_get_toplevel(widget)),
            GTK_FILE_CHOOSER_ACTION_OPEN,
            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
            GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL));
    parentWindow = ufraw_message_handler(UFRAW_SET_PARENT,
            (char *)fileChooser);
    gtk_file_chooser_set_select_multiple(fileChooser, TRUE);
    if (strlen(cfg->profilePath)>0)
        gtk_file_chooser_set_current_folder(fileChooser, cfg->profilePath);
    filter = GTK_FILE_FILTER(gtk_file_filter_new());
    gtk_file_filter_set_name(filter, "Color Profiles");
    gtk_file_filter_add_pattern(filter, "*.icm");
    gtk_file_filter_add_pattern(filter, "*.ICM");
    gtk_file_filter_add_pattern(filter, "*.icc");
    gtk_file_filter_add_pattern(filter, "*.ICC");
    gtk_file_chooser_add_filter(fileChooser, filter);
    filter = GTK_FILE_FILTER(gtk_file_filter_new());
    gtk_file_filter_set_name(filter, "All files");
    gtk_file_filter_add_pattern(filter, "*");
    gtk_file_chooser_add_filter(fileChooser, filter);
    if (gtk_dialog_run(GTK_DIALOG(fileChooser))==GTK_RESPONSE_ACCEPT) {
        for (list=saveList=gtk_file_chooser_get_filenames(fileChooser);
            list!=NULL && cfg->profileCount[type]<max_profiles;
            list=g_slist_next(list))
        {
            filename = list->data;
            p = cfg_default.profile[type][0];
            p.curve = -1; /* Mark profile as new to set some defaults */
            g_strlcpy(p.file, filename, max_path);
            developer_profile(Developer, type, &p);
            if (Developer->profile[type]==NULL) {
                g_free(list->data);
                continue;
            }
            for (i=strlen(filename);
                 i>0 && filename[i]!='/' && filename[i]!='\\'; i--);
            filename += i+1;
            for (i=0; i<strlen(filename) && filename[i]!='.' && i<max_name-1;
                    i++) p.name[i] = filename[i];
            p.name[i] = '\0';
            cfg->profile[type][cfg->profileCount[type]++] = p;
            gtk_combo_box_append_text(profileCombo[type], p.name);
            cfg->profileIndex[type] = cfg->profileCount[type]-1;
            cp = g_path_get_dirname(list->data);
            g_strlcpy(cfg->profilePath, cp, max_path);
            g_free(cp);
            g_free(list->data);
        }
        gtk_combo_box_set_active(profileCombo[type], cfg->profileIndex[type]);
        if (list!=NULL)
            ufraw_message(UFRAW_ERROR, "No more room for new profiles.");
        g_slist_free(saveList);
    }
    gtk_widget_destroy(GTK_WIDGET(fileChooser));
    ufraw_message_handler(UFRAW_SET_PARENT, parentWindow);
}

colorLabels *color_labels_new(GtkTable *table, int x, int y, char *label,
        int format)
{
    colorLabels *l;
    GtkWidget *lbl;
    int c, i = 0;

    l = g_new(colorLabels, 1);
    l->format = format;
    if (label!=NULL){
        lbl = gtk_label_new(label);
        gtk_misc_set_alignment(GTK_MISC(lbl), 1, 0.5);
        gtk_table_attach(table, lbl, x, x+1, y, y+1,
                GTK_SHRINK|GTK_FILL, GTK_FILL, 0, 0);
        i++;
    }
    for (c=0; c<3; c++, i++) {
        l->labels[c] = GTK_LABEL(gtk_label_new(NULL));
        gtk_table_attach_defaults(table, GTK_WIDGET(l->labels[c]),
                x+i, x+i+1, y, y+1);
    }
    return l;
}

void color_labels_set(colorLabels *l, float data[3])
{
    int c;
    char buf1[max_name], buf2[max_name];
    const char *colorName[] = {"red", "green", "blue"};

    for (c=0; c<3; c++) {
        switch (l->format) {
        case pixel_format: snprintf(buf1, max_name, "%3.f", data[c]); break;
        case percent_format:
                      if (data[c]<10.0)
                          snprintf(buf1, max_name, "%2.1f%%", data[c]);
                      else
                          snprintf(buf1, max_name, "%2.0f%%", data[c]);
                      break;
        default: snprintf(buf1, max_name, "ERR");
        }
        snprintf(buf2, max_name, "<span foreground='%s'>%s</span>",
                colorName[c], buf1);
        gtk_label_set_markup(l->labels[c], buf2);
    }
}

int renderDefault, renderOverexposed, renderUnderexposed;
gpointer render_default=&renderDefault, render_overexposed=&renderOverexposed,
    render_underexposed=&renderUnderexposed;

gboolean render_raw_histogram(gpointer mode);
gboolean render_preview_image(gpointer mode);

void render_preview(GtkWidget *widget, gpointer mode)
{
    widget = widget;
    if (freezeDialog) return;
    g_idle_remove_by_data((gpointer)render_default);
    g_idle_remove_by_data((gpointer)render_underexposed);
    g_idle_remove_by_data((gpointer)render_overexposed);
    g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
            render_raw_histogram, (gpointer)mode, NULL);
}

static int renderRestart = FALSE;
gboolean render_raw_histogram(gpointer mode)
{
    static GdkPixbuf *pixbuf;
    static guint8 *pixies;
    int rowstride;
    guint8 pix[99], *p8 , *p;
    guint16 pixtmp[9999];
    image_type p16;
    int x, c, cl, y, y1;

    developer_prepare(Developer, previewImage->rgbMax,
            pow(2, cfg->exposure), cfg->unclip, cfg->temperature, cfg->green,
            previewImage->preMul,
            &cfg->profile[0][cfg->profileIndex[0]],
            &cfg->profile[1][cfg->profileIndex[1]], cfg->intent,
            CFG_saturation, cfg->curveIndex, &cfg->curve[cfg->curveIndex]);
    /* draw curves on the raw histogram */
    pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(rawHisto));
    pixies = gdk_pixbuf_get_pixels(pixbuf);
    rowstride = gdk_pixbuf_get_rowstride(pixbuf);
    memcpy(pixies, gdk_pixbuf_get_pixels(rawHistoInitPixbuf),
            (gdk_pixbuf_get_height(pixbuf)-1)*rowstride +
            gdk_pixbuf_get_width(pixbuf));
    p8 = pix;
    for (x=0, p=pixies + raw_his_height*rowstride + 3; x<raw_his_size; x++)
        for (c=0; c<3; c++, p++) {
            /* Value for pixel x of color c in a grey pixel */
            for (cl=0; cl<3; cl++)
                p16[cl] = MIN((guint64)x*Developer->rgbMax *
                          Developer->rgbWB[c] /
                          Developer->rgbWB[cl] / raw_his_size, 0xFFFF);
            develope(p8, p16, Developer, 8, pixtmp, 1);
            y=p8[c] * (raw_his_height-1)/MAXOUT;
            /* Value for pixel x+1 of color c in a grey pixel */
            for (cl=0; cl<3; cl++)
                p16[cl] = MIN((guint64)(x+1)*Developer->rgbMax *
                         Developer->rgbWB[c] /
                         Developer->rgbWB[cl]/raw_his_size-1, 0xFFFF);
            develope(p8, p16, Developer, 8, pixtmp, 1);
            y1=p8[c] * (raw_his_height-1)/MAXOUT;
            for (; y<=y1; y++) *(p-y*rowstride) = 255 - *(p-y*rowstride);
            /* Value for pixel x of pure color c */
            p16[0] = p16[1] = p16[2] = 0;
            p16[c] = MIN((guint64)x*Developer->rgbMax/raw_his_size, 0xFFFF);
            develope(p8, p16, Developer, 8, pixtmp, 1);
            y1=p8[c] * (raw_his_height-1)/MAXOUT;
            for (; y<y1; y++) *(p-y*rowstride) = 160 - *(p-y*rowstride)/4;
            /* Value for pixel x+1 of pure color c */
            p16[c] = MIN((guint64)(x+1)*Developer->rgbMax/raw_his_size - 1,
                     0xFFFF);
            develope(p8, p16, Developer, 8, pixtmp, 1);
            y1=p8[c] * (raw_his_height-1)/MAXOUT;
            for (; y<=y1; y++) *(p-y*rowstride) = 255 - *(p-y*rowstride);
        }
    gtk_widget_queue_draw(rawHisto);
    renderRestart = TRUE;
    g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
            render_preview_image, (gpointer)mode, NULL);
    return FALSE;
}

gboolean render_preview_image(gpointer mode)
{
    static GdkPixbuf *pixbuf;
    static guint8 *pixies;
    static int width, height, x, y, c, o, min, max, rowstride, i, y0;
    static int live_his[live_his_size][4], live_his_max;
    guint8 pix[99], *p8;
    guint16 pixtmp[9999];
    float rgb[3];
    guint64 sum[3], sqr[3];
    char tmp[max_name];

    if (renderRestart) {
        renderRestart = FALSE;
        pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(previewWidget));
        width = gdk_pixbuf_get_width(pixbuf);
        height = gdk_pixbuf_get_height(pixbuf);
        rowstride = gdk_pixbuf_get_rowstride(pixbuf);
        pixies = gdk_pixbuf_get_pixels(pixbuf);
#ifdef DEBUG
        fprintf(stderr, "render_preview_image: w=%d, h=%d, r=%d, mode=%d\n",
                width, height, rowstride, mode);
        fflush(stderr);
#endif
        memset(live_his, 0, sizeof(live_his));
        y = y0 = 0;
    }
    for (; y<height; y++) {
        develope(&pixies[y*rowstride], previewImage->image[y*width],
                Developer, 8, pixtmp, width);
        for (x=0, p8=&pixies[y*rowstride]; x<width; x++, p8+=3) {
            for (c=0, max=0, min=0x100; c<3; c++) {
                max = MAX(max, p8[c]);
                min = MIN(min, p8[c]);
                live_his[p8[c]][c]++;
            }
            if (cfg->histogram==luminosity_histogram)
                live_his[(int)(0.3*p8[0]+0.59*p8[1]+0.11*p8[2])][3]++;
            if (cfg->histogram==value_histogram)
                live_his[max][3]++;
            if (cfg->histogram==saturation_histogram) {
                if (max==0) live_his[0][3]++;
                else live_his[255*(max-min)/max][3]++;
            }
            for (c=0; c<3; c++) {
                o = p8[c];
                if (mode==render_default) {
                    if (cfg->overExp && max==MAXOUT) o = 0;
                    if (cfg->underExp && min==0) o = 255;
                } else if (mode==render_overexposed) {
                    if (o!=255) o = 0;
                } else if (mode==render_underexposed) {
                    if (o!=0) o = 255;
                }
                pixies[y*rowstride+3*x+c] = o;
            }
        }
        if (y%32==31) {
            gtk_widget_queue_draw_area(previewWidget, 0, y0, width, y+1-y0);
            y0 = y+1;
            y++;
            return TRUE;
        }
    }
    gtk_widget_queue_draw_area(previewWidget, 0, y0, width, y+1-y0);
    if (spotOn) {
        for (c=0; c<3; c++) rgb[c] = 0;
        for (y=spoty-cfg->spotSize+1; y<spoty+cfg->spotSize; y++) {
            develope(pix, previewImage->image[y*width+spotx-cfg->spotSize+1],
                    Developer, 8, pixtmp, 2*cfg->spotSize-1);
            for (x=0; x<2*cfg->spotSize-1; x++)
                for (c=0; c<3; c++)
                    rgb[c] += pix[x*3+c];
        }
        for (c=0; c<3; c++) rgb[c] /= (2*cfg->spotSize-1)*(2*cfg->spotSize-1);
        color_labels_set(spotLabels, rgb);
        snprintf(tmp, max_name, "<span background='#%02X%02X%02X'>"
              "         </span>", (int)rgb[0], (int)rgb[1], (int)rgb[2]);
        gtk_label_set_markup(spotPatch, tmp);
        for (i=-cfg->spotSize; i<=cfg->spotSize; i++) {
            pixbuf_reverse(spotx+i, spoty+cfg->spotSize,
                    pixies, width, height, rowstride);
            pixbuf_reverse(spotx+i, spoty-cfg->spotSize,
                    pixies, width, height, rowstride);
        }
        for (i=-cfg->spotSize+1; i<=cfg->spotSize-1; i++) {
            pixbuf_reverse(spotx+cfg->spotSize, spoty+i,
                    pixies, width, height, rowstride);
            pixbuf_reverse(spotx-cfg->spotSize, spoty+i,
                    pixies, width, height, rowstride);
        }
        gtk_widget_queue_draw_area(previewWidget, spotx-cfg->spotSize,
                spoty-cfg->spotSize, spotx+cfg->spotSize, spoty+cfg->spotSize);
    }

    /* draw live histogram */
    pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(liveHisto));
    pixies = gdk_pixbuf_get_pixels(pixbuf);
    rowstride = gdk_pixbuf_get_rowstride(pixbuf);
    memset(pixies, 0, (gdk_pixbuf_get_height(pixbuf)-1)*rowstride +
            gdk_pixbuf_get_width(pixbuf));
    for (c=0; c<3; c++) {
        sum[c] = 0;
        sqr[c] = 0;
        for (x=1; x<live_his_size; x++) {
            sum[c] += x*live_his[x][c];
            sqr[c] += (guint64)x*x*live_his[x][c];
        }
    }
    for (x=1, live_his_max=1; x<live_his_size-1; x++)
        if (cfg->histogram==rgb_histogram)
            for (c=0; c<3; c++)
                live_his_max = MAX(live_his_max, live_his[x][c]);
        else if (cfg->histogram==r_g_b_histogram)
            live_his_max = MAX(live_his_max,
                    live_his[x][0]+live_his[x][1]+live_his[x][2]);
        else live_his_max = MAX(live_his_max, live_his[x][3]);
#ifdef DEBUG
    fprintf(stderr, "render_preview: live_his_max=%d\n", live_his_max);
    fflush(stderr);
#endif
    for (x=0; x<live_his_size; x++) for (y=0; y<live_his_height; y++)
        if (cfg->histogram==r_g_b_histogram) {
            if (y*live_his_max < live_his[x][0]*live_his_height)
                pixies[(live_his_height-y)*rowstride+3*(x+1)+0] = 255;
            else if (y*live_his_max <
                    (live_his[x][0]+live_his[x][1])*live_his_height)
                pixies[(live_his_height-y)*rowstride+3*(x+1)+1] = 255;
            else if (y*live_his_max <
                    (live_his[x][0]+live_his[x][1]+live_his[x][2])
                    *live_his_height)
                pixies[(live_his_height-y)*rowstride+3*(x+1)+2] = 255;
        } else {
            for (c=0; c<3; c++)
                if (cfg->histogram==rgb_histogram) {
                    if (y*live_his_max<live_his[x][c]*live_his_height)
                        pixies[(live_his_height-y)*rowstride+3*(x+1)+c] = 255;
                } else {
                    if (y*live_his_max<live_his[x][3]*live_his_height)
                        pixies[(live_his_height-y)*rowstride+3*(x+1)+c] = 255;
                }
       }

    /* draw vertical line at quarters "behind" the live histogram */
    for (y=-1; y<raw_his_height+1; y++) for (x=64; x<255; x+=64) 
        if (pixies[(live_his_height-y)*rowstride+3*(x+1)+0]==0 &&
            pixies[(live_his_height-y)*rowstride+3*(x+1)+1]==0 &&
            pixies[(live_his_height-y)*rowstride+3*(x+1)+2]==0 )
            for (c=0; c<3; c++)
                pixies[(live_his_height-y)*rowstride+3*(x+1)+c]=64;
    gtk_widget_queue_draw(liveHisto);
    for (c=0;c<3;c++) rgb[c] = sum[c]/height/width;
    color_labels_set(avrLabels, rgb);
    for (c=0;c<3;c++) rgb[c] = sqrt(sqr[c]/height/width-rgb[c]*rgb[c]);
    color_labels_set(devLabels, rgb);
    for (c=0;c<3;c++) rgb[c] = 100.0*live_his[live_his_size-1][c]/height/width;
    color_labels_set(overLabels, rgb);
    for (c=0;c<3;c++) rgb[c] = 100.0*live_his[0][c]/height/width;
    color_labels_set(underLabels, rgb);
    return FALSE;
}

/* update the UI entries that could have changed automatically */
void update_scales()
{
    if (freezeDialog) return;
    freezeDialog = TRUE;
    if (cfg->curveIndex>camera_curve && !CFG_cameraCurve)
        gtk_combo_box_set_active(curveCombo, cfg->curveIndex-1);
    else
        gtk_combo_box_set_active(curveCombo, cfg->curveIndex);
    gtk_combo_box_set_active(wbCombo, cfg->wb);
    gtk_adjustment_set_value(adjTemperature, cfg->temperature);
    gtk_adjustment_set_value(adjGreen, cfg->green);
    gtk_adjustment_set_value(adjExposure, cfg->exposure);
    gtk_adjustment_set_value(adjProfileGamma,
            cfg->profile[0][cfg->profileIndex[0]].gamma);
    gtk_adjustment_set_value(adjGamma, CFG_gamma);
    gtk_adjustment_set_value(adjContrast, CFG_contrast);
    gtk_adjustment_set_value(adjSaturation, CFG_saturation);
    gtk_adjustment_set_value(adjShadow, CFG_shadow);
    gtk_adjustment_set_value(adjDepth, CFG_depth);
//    gtk_adjustment_set_value(adjBrightness, CFG_brightness);
    gtk_adjustment_set_value(adjBlack, CFG_black);
    freezeDialog = FALSE;
    render_preview(NULL, render_default);
};    

/* Calculate optimal exposure and black point */
void auto_adjust(GtkWidget *widget, gpointer user_data)
{
    widget = widget;
    user_data = user_data;
    if (freezeDialog) return;
    ufraw_auto_adjust(previewImage);
    update_scales();
}

void reset_to_defaults(GtkWidget *widget, gpointer user_data)
{
    widget = widget;
    user_data = user_data;
    if (freezeDialog) return;
    cfg->exposure = cfg_default.exposure;
    CFG_gamma = cfg_default.curve[0].gamma;
    CFG_contrast = cfg_default.curve[0].contrast;
    CFG_saturation = cfg_default.curve[0].saturation;
    CFG_shadow = cfg_default.curve[0].shadow;
    CFG_depth = cfg_default.curve[0].depth;
    CFG_brightness = cfg_default.curve[0].brightness;
    CFG_black = cfg_default.curve[0].black;
    update_scales();
}

void spot_wb(GtkWidget *widget, gpointer user_data)
{
    widget = widget;
    user_data = user_data;
    int xi, yi, c;
    float rgb[3];

    if (freezeDialog) return;
    if (!spotOn) {
        ufraw_message(UFRAW_ERROR, "Select a spot on the preview image "
                "to apply spot white balance");
        return;
    }
    for (c=0; c<3; c++) rgb[c] = 0;
    for (yi=spoty-cfg->spotSize+1; yi<spoty+cfg->spotSize; yi++)
        for (xi=spotx-cfg->spotSize+1; xi<spotx+cfg->spotSize; xi++)
            for (c=0; c<3; c++)
                rgb[c] += previewImage->image[yi*previewImage->width+xi][c];
    for (c=0; c<3; c++)
        rgb[c] /= (2*cfg->spotSize-1)*(2*cfg->spotSize-1);
    ufraw_message(UFRAW_VERBOSE, "spot_wb: x=%d, y=%d, r=%f, g=%f, b=%f\n",
            spotx, spoty, rgb[0], rgb[1], rgb[2]);
    for (c=0; c<3; c++) rgb[c] *= previewImage->preMul[c];
    RGB_to_temperature(rgb, &cfg->temperature, &cfg->green);
    cfg->wb = 0;
    update_scales();
}

void spot_event(GtkWidget *event_box, GdkEventButton *event, gpointer data)
{
    event_box = event_box;
    data = data;
    if (freezeDialog) return;
    spotOn = TRUE;
    spotx = MAX(cfg->spotSize, event->x);
    spoty = MAX(cfg->spotSize, event->y);
    spotx = MIN(previewImage->width-cfg->spotSize-1, spotx);
    spoty = MIN(previewImage->height-cfg->spotSize-1, spoty);
    render_preview(NULL, render_default);
}

GtkTable *table_with_frame(GtkWidget *box, char *label, gboolean expand)
{
    GtkWidget *frame, *expander;
    GtkTable *table;

    table = GTK_TABLE(gtk_table_new(10, 10, FALSE));
    if (GTK_IS_NOTEBOOK(box)) gtk_notebook_append_page(GTK_NOTEBOOK(box),
            GTK_WIDGET(table), gtk_label_new(label));
    else if (label!=NULL) {
        expander = gtk_expander_new(label);
        gtk_expander_set_expanded(GTK_EXPANDER(expander), expand);
        gtk_box_pack_start(GTK_BOX(box), expander, FALSE, FALSE, 0);
        frame = gtk_frame_new(NULL);
        gtk_container_add(GTK_CONTAINER(expander), GTK_WIDGET(frame));
        gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(table));
    } else {
        frame = gtk_frame_new(NULL);
        gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 0);
        gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(table));
    }
    return table;
}

void toggle_button_update(GtkToggleButton *button, gboolean *valuep)
{
    *valuep = gtk_toggle_button_get_active(button);
    render_preview(NULL, render_default);
}

void toggle_button(GtkTable *table, int x, int y, char *label, gboolean *valuep)
{
    GtkWidget *widget, *align;

    widget = gtk_check_button_new_with_label(label);
    if (label==NULL) {
        align = gtk_alignment_new(1, 0, 0, 1);
        gtk_container_add(GTK_CONTAINER(align), widget);
    } else align = widget;
    gtk_table_attach(table, align, x, x+1, y, y+1, 0, 0, 0, 0);
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), *valuep);
    g_signal_connect(G_OBJECT(widget), "toggled",
            G_CALLBACK(toggle_button_update), valuep);
}

void adjustment_update(GtkAdjustment *adj, float *valuep)
{
    if (freezeDialog) return;
    if (valuep>=&cfg->curve[0].gamma && valuep<=&cfg->curve[0].depth)
        valuep = (void *)valuep + cfg->curveIndex * sizeof(curve_data);
    if (valuep==&cfg->profile[0][0].gamma)
        valuep = (void *)&cfg->profile[0][cfg->profileIndex[0]].gamma;
    if (valuep==(float *)&cfg->spotSize) {
        cfg->spotSize = (int)gtk_adjustment_get_value(adj);
        if (spotOn) {
            spotx = MAX(cfg->spotSize, spotx);
            spoty = MAX(cfg->spotSize, spoty);
            spotx = MIN(previewImage->width-cfg->spotSize-1, spotx);
            spoty = MIN(previewImage->height-cfg->spotSize-1, spoty);
        }
    } else {
        *valuep = (float)gtk_adjustment_get_value(adj);
    }
    if (valuep==&cfg->temperature || valuep==&cfg->green) {
        cfg->wb = 0;
        gtk_combo_box_set_active(wbCombo, cfg->wb);
    }
    render_preview(NULL, render_default);
}

GtkAdjustment *adjustment_scale(GtkTable *table, GtkWidget *widget[3],
    int x, int y, char *label, float value, float *valuep,
    float min, float max, float step, float jump, int accuracy, char *tip)
{
    GtkAdjustment *adj;
    GtkWidget *w, *l;

    w = gtk_event_box_new();
    l = gtk_label_new(label);
    gtk_misc_set_alignment(GTK_MISC(l), 1, 0.5);
    gtk_container_add(GTK_CONTAINER(w),l);
    gtk_tooltips_set_tip(toolTips, w, tip, NULL);
    if (widget!=NULL) widget[0] = w;
    gtk_table_attach(table, w, x, x+1, y, y+1, GTK_SHRINK|GTK_FILL, 0, 0, 0);
    adj = GTK_ADJUSTMENT(gtk_adjustment_new(value, min, max, step, jump, 0));
    g_signal_connect(G_OBJECT(adj), "value-changed",
            G_CALLBACK(adjustment_update), valuep);
    w = gtk_hscale_new(adj);
    gtk_scale_set_draw_value(GTK_SCALE(w), FALSE);
    gtk_tooltips_set_tip(toolTips, w, tip, NULL);
    if (widget!=NULL) widget[1] = w;
    gtk_table_attach(table, w, x+1, x+6, y, y+1, GTK_EXPAND|GTK_FILL, 0, 0, 0);
    w = gtk_spin_button_new(adj, step, accuracy);
    gtk_tooltips_set_tip(toolTips, w, tip, NULL);
    if (widget!=NULL) widget[2] = w;
    gtk_table_attach(table, w, x+6, x+7, y, y+1, GTK_SHRINK|GTK_FILL, 0, 0, 0);
    return adj;
}

void combo_update(GtkWidget *combo, gint *valuep)
{
    int i;

    if (freezeDialog) return;
    *valuep = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
    if (valuep==&cfg->curveIndex) {
        if (cfg->curveIndex==gamma_curve || cfg->curveIndex==log_curve) {
            for (i=0; i<3; i++) {
                gtk_widget_show(gammaWidget[i]);
                gtk_widget_hide(contrastWidget[i]);
            }
        } else {
            for (i=0; i<3; i++) {
                gtk_widget_show(contrastWidget[i]);
                gtk_widget_hide(gammaWidget[i]);
            }
            if (!CFG_cameraCurve && cfg->curveIndex>=camera_curve)
                cfg->curveIndex++;
        }
        cfg->profile[0][cfg->profileIndex[0]].curve = cfg->curveIndex;
        update_scales();
    } else if (valuep==&cfg->profileIndex[in_profile]) {
        developer_profile(Developer, in_profile,
                &cfg->profile[in_profile][cfg->profileIndex[in_profile]]);
        if (!CFG_cameraCurve && cfg->profile[in_profile]
            [cfg->profileIndex[in_profile]].curve>=camera_curve)
            gtk_combo_box_set_active(curveCombo,
                cfg->profile[in_profile][cfg->profileIndex[in_profile]].curve
                    -1);
        else
            gtk_combo_box_set_active(curveCombo,
                cfg->profile[in_profile][cfg->profileIndex[in_profile]].curve);
        update_scales();
    } else if (valuep==&cfg->wb) {
        ufraw_set_wb(previewImage);
        update_scales();
    } else if (valuep!=&cfg->interpolation)
        render_preview(NULL, render_default);
}

void container_remove(GtkWidget *widget, gpointer user_data)
{
    GtkContainer *container = GTK_CONTAINER(user_data);
    gtk_container_remove(container, widget);
}

void delete_from_list(GtkWidget *widget, gpointer user_data)
{
    GtkDialog *dialog;
    long type, index;

    dialog = GTK_DIALOG(gtk_widget_get_ancestor(widget, GTK_TYPE_DIALOG));
    type = (long)g_object_get_data(G_OBJECT(widget), "Type");
    index = (long)user_data;
    if (type<2) {
        gtk_combo_box_remove_text(profileCombo[type], index);
        cfg->profileCount[type]--;
        if (cfg->profileIndex[type]==cfg->profileCount[type])
            cfg->profileIndex[type]--;
        for (; index<cfg->profileCount[type]; index++)
            cfg->profile[type][index] = cfg->profile[type][index+1];
    } else {
        if (CFG_cameraCurve)
            gtk_combo_box_remove_text(curveCombo, index);
        else
            gtk_combo_box_remove_text(curveCombo, index-1);
        cfg->curveCount--;
        if (cfg->curveIndex==cfg->curveCount)
            cfg->curveIndex--;
        if (cfg->curveIndex==camera_curve && !CFG_cameraCurve)
            cfg->curveIndex = log_curve;
        for (; index<cfg->curveCount; index++)
            cfg->curve[index] = cfg->curve[index+1];
    }
    gtk_dialog_response(dialog, GTK_RESPONSE_APPLY);
}

void configuration_save(GtkWidget *widget, gpointer user_data)
{
    widget = widget;
    user_data = user_data;
    save_configuration(cfg, NULL, 0);
    save_cfg = *previewImage->cfg;
}

void options_combo_update(GtkWidget *combo, gint *valuep)
{
    GtkDialog *dialog;

    *valuep = gtk_combo_box_get_active(GTK_COMBO_BOX(combo));
    dialog = GTK_DIALOG(gtk_widget_get_ancestor(combo, GTK_TYPE_DIALOG));
    gtk_dialog_response(dialog, GTK_RESPONSE_APPLY);
}

void options_dialog(GtkWidget *widget, gpointer user_data)
{
    GtkWidget *optionsDialog;
    void *parentWindow;
    GtkWidget *notebook, *label, *page, *button, *text, *box, *image;
    GtkComboBox *combo;
    GtkTable *profileTable[2], *curveTable;
    GtkTable *table;
    GtkTextBuffer *cfgBuffer, *buffer;
    char txt[max_name], buf[10000];
    int i, j;

    user_data = user_data;
    if (freezeDialog) return;
    optionsDialog = gtk_dialog_new_with_buttons("UFRaw options",
            GTK_WINDOW(gtk_widget_get_toplevel(widget)),
            GTK_DIALOG_DESTROY_WITH_PARENT,
            GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
    parentWindow = ufraw_message_handler(UFRAW_SET_PARENT,
            (char *)optionsDialog);
    gtk_dialog_set_default_response(GTK_DIALOG(optionsDialog),
            GTK_RESPONSE_OK);
    notebook = gtk_notebook_new();
    gtk_container_add(GTK_CONTAINER(GTK_DIALOG(optionsDialog)->vbox),
            notebook);

    label = gtk_label_new("Settings");
    box = gtk_vbox_new(FALSE, 0);
    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), box, label);
    table = table_with_frame(box, "Initial settings", TRUE);
    combo = GTK_COMBO_BOX(gtk_combo_box_new_text());
    gtk_combo_box_append_text(combo, "Preserve WB");
    gtk_combo_box_append_text(combo, "Camera WB");
    gtk_combo_box_append_text(combo, "Auto WB");
    gtk_combo_box_set_active(combo, cfg->wbLoad);
    g_signal_connect(G_OBJECT(combo), "changed",
            G_CALLBACK(options_combo_update), &cfg->wbLoad);
    gtk_table_attach_defaults(table, GTK_WIDGET(combo), 0, 1, 0, 1);
    combo = GTK_COMBO_BOX(gtk_combo_box_new_text());
    gtk_combo_box_append_text(combo, "Preserve curve corrections");
    gtk_combo_box_append_text(combo, "Default curve corrections");
    gtk_combo_box_set_active(combo, cfg->curveLoad);
    g_signal_connect(G_OBJECT(combo), "changed",
            G_CALLBACK(options_combo_update), &cfg->curveLoad);
    gtk_table_attach_defaults(table, GTK_WIDGET(combo), 1, 2, 0, 1);
    combo = GTK_COMBO_BOX(gtk_combo_box_new_text());
    gtk_combo_box_append_text(combo, "Preserve exposure");
    gtk_combo_box_append_text(combo, "Default exposure");
    gtk_combo_box_append_text(combo, "Auto exposure");
    gtk_combo_box_set_active(combo, cfg->exposureLoad);
    g_signal_connect(G_OBJECT(combo), "changed",
            G_CALLBACK(options_combo_update), &cfg->exposureLoad);
    gtk_table_attach_defaults(table, GTK_WIDGET(combo), 2, 3, 0, 1);

    profileTable[0] = table_with_frame(box, "Input color profiles", TRUE);
    profileTable[1] = table_with_frame(box, "Output color profiles", TRUE);
    curveTable = table_with_frame(box, "Curves", TRUE);

    label = gtk_label_new("Configuration");
    box = gtk_vbox_new(FALSE, 0);
    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), box, label);
    page = gtk_scrolled_window_new(NULL, NULL);
    gtk_box_pack_start(GTK_BOX(box), page, TRUE, TRUE, 0);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(page),
            GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
    text = gtk_text_view_new();
    gtk_container_add(GTK_CONTAINER(page), text);
    gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
    cfgBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
    table = GTK_TABLE(gtk_table_new(10, 10, FALSE));
    gtk_box_pack_start(GTK_BOX(box), GTK_WIDGET(table), FALSE, FALSE, 0);
    button = gtk_button_new_from_stock(GTK_STOCK_SAVE);
    g_signal_connect(G_OBJECT(button), "clicked",
            G_CALLBACK(configuration_save), NULL);
    gtk_table_attach(table, button, 0, 1, 0, 1, 0, 0, 0, 0);
/*    button = gtk_button_new_from_stock(GTK_STOCK_SAVE_AS);
    gtk_table_attach(table, button, 1, 2, 0, 1, 0, 0, 0, 0);
    button = gtk_button_new_from_stock(GTK_STOCK_OPEN);
    gtk_table_attach(table, button, 2, 3, 0, 1, 0, 0, 0, 0);
*/
    label = gtk_label_new("Log");
    page = gtk_scrolled_window_new(NULL, NULL);
    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(page),
            GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
    text = gtk_text_view_new();
    gtk_container_add(GTK_CONTAINER(page), text);
    gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
    buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
    gtk_text_buffer_set_text(buffer,
            ufraw_message_handler(UFRAW_GET_MESSAGE, NULL), -1);

    label = gtk_label_new("About");
    box = gtk_vbox_new(FALSE, 0);
    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), box, label);
    image = gtk_image_new_from_pixbuf(
            gdk_pixbuf_new_from_inline(-1, ufraw_icon, FALSE, NULL));
    gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 0);
    label = gtk_label_new(NULL);
    gtk_label_set_markup(GTK_LABEL(label),
            "<span size='xx-large'><b>UFRaw 0.4</b></span>");
    gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0);
    label = gtk_label_new(NULL);
    gtk_label_set_markup(GTK_LABEL(label),
            "The <b>U</b>nidentified <b>F</b>lying <b>Raw</b> "
            "(<b>UFRaw</b>) is a utility to read\n"
            "and manipulate raw images from digital cameras.\n"
            "Author: Udi Fuchs\n"
            "Homepage: http://ufraw.sourceforge.net/\n");
    gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0);

    while (1) {
        for (j=0; j<2; j++) {
            gtk_container_foreach(GTK_CONTAINER(profileTable[j]),
                    (GtkCallback)(container_remove), profileTable[j]);
            table = profileTable[j];
            for (i=1; i<cfg->profileCount[j]; i++) {
                snprintf(txt, max_name, "%s (%s)",
                        cfg->profile[j][i].name,
                        cfg->profile[j][i].productName);
                label = gtk_label_new(txt);
                gtk_table_attach_defaults(table, label, 0, 1, i, i+1);
                button = gtk_button_new_from_stock(GTK_STOCK_DELETE);
                g_object_set_data(G_OBJECT(button), "Type", (void*)j);
                g_signal_connect(G_OBJECT(button), "clicked",
                        G_CALLBACK(delete_from_list), (gpointer)i);
                gtk_table_attach(table, button, 1, 2, i, i+1, 0, 0, 0, 0);
            }
        }
        gtk_container_foreach(GTK_CONTAINER(curveTable),
                (GtkCallback)(container_remove), curveTable);
        table = curveTable;
        for (i=camera_curve+1; i<cfg->curveCount; i++) {
            label = gtk_label_new(cfg->curve[i].name);
            gtk_table_attach_defaults(table, label, 0, 1, i, i+1);
            button = gtk_button_new_from_stock(GTK_STOCK_DELETE);
            g_object_set_data(G_OBJECT(button), "Type", (void*)2);
            g_signal_connect(G_OBJECT(button), "clicked",
                    G_CALLBACK(delete_from_list), (gpointer)i);
            gtk_table_attach(table, button, 1, 2, i, i+1, 0, 0, 0, 0);
        }
        save_configuration(cfg, buf, 10000);
        gtk_text_buffer_set_text(cfgBuffer, buf, -1);
        gtk_widget_show_all(optionsDialog);
        if (gtk_dialog_run(GTK_DIALOG(optionsDialog))!=
                    GTK_RESPONSE_APPLY) {
            gtk_widget_destroy(optionsDialog);
            ufraw_message_handler(UFRAW_SET_PARENT, parentWindow);
            render_preview(NULL, render_default);
            return;
        }
    }
}

gboolean window_delete_event(GtkWidget *widget, GdkEvent *event, gpointer data)
{
    data = data;
    event = event;
    g_object_set_data(G_OBJECT(widget), "WindowResponse",
            (gpointer)GTK_RESPONSE_CANCEL);
    gtk_main_quit();
    return TRUE;
}

void expander_state(GtkWidget *widget, gpointer user_data)
{
    const char *text;
    GtkWidget *label;
    int i;

    user_data = user_data;
    if (!GTK_IS_EXPANDER(widget)) return;
    label = gtk_expander_get_label_widget(GTK_EXPANDER(widget));
    text = gtk_label_get_text(GTK_LABEL(label));
    for (i=0; expanderText[i]!=NULL; i++)
        if (!strcmp(text, expanderText[i]))
            cfg->expander[i] = gtk_expander_get_expanded(GTK_EXPANDER(widget));
}

void ufraw_messenger(char *message,  void *parentWindow)
{
    GtkDialog *dialog;

    if (parentWindow==NULL) {
        fprintf(stderr, "ufraw: %s", message);
        if (message[strlen(message)-1]!='\n') fputc('\n', stderr);
        fflush(stderr);
        return;
    }
    dialog = GTK_DIALOG(gtk_message_dialog_new(GTK_WINDOW(parentWindow),
            GTK_DIALOG_DESTROY_WITH_PARENT,
            GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, message));
    gtk_dialog_run(dialog);
    gtk_widget_destroy(GTK_WIDGET(dialog));
}

void preview_progress(char *text, float progress)
{
    if (progressBar==NULL) return;
    gtk_progress_bar_set_text(progressBar, text);
    gtk_progress_bar_set_fraction(progressBar, progress);
    while (gtk_events_pending()) gtk_main_iteration();
}

int ufraw_preview(image_data *image, int plugin, int (*save_func)())
{
    GtkWidget *previewWindow;
    GtkTable *table, *innerTable;
    GtkBox *previewHBox, *box, *hbox;
    GtkComboBox *combo;
    GtkWidget *button, *saveButton, *event_box, *align, *label, *vBox,
        *noteBox;
    GdkPixbuf *pixbuf;
    GdkRectangle screen;
    char previewHeader[max_path];
    int max_preview_width, max_preview_height;
    int preview_width, preview_height, scale, shrinkSave, i;
    long j;
    gboolean largeScreen;
    int status;
    int rowstride, x, y, c;
    guint8 *pixies;
    int (*raw_his)[3], raw_his_max;
    image_data preview_image;
    char progressText[max_name];
    void *parentWindow;

    save_cfg = *image->cfg;
    cfg = image->cfg;
    spotOn = FALSE;
    freezeDialog = TRUE;

    toolTips = gtk_tooltips_new();
    previewWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    snprintf(previewHeader, max_path, "%s - UFRaw Photo Loader",
            image->filename);
    gtk_window_set_title(GTK_WINDOW(previewWindow), previewHeader);
    gtk_window_set_icon(GTK_WINDOW(previewWindow),
            gdk_pixbuf_new_from_inline(-1, ufraw_icon, FALSE, NULL));
    gtk_window_set_resizable(GTK_WINDOW(previewWindow), FALSE);
    g_signal_connect(G_OBJECT(previewWindow), "delete-event",
            G_CALLBACK(window_delete_event), NULL);

    parentWindow = ufraw_message_handler(UFRAW_SET_PARENT,
            (char *)previewWindow);
    if (parentWindow!=NULL) gtk_window_set_transient_for(
            GTK_WINDOW(previewWindow), GTK_WINDOW(parentWindow));

    gdk_screen_get_monitor_geometry(gdk_screen_get_default(), 0, &screen);
    max_preview_width = MIN(def_preview_width, screen.width-400);
    max_preview_height = MIN(def_preview_height, screen.height-120);
    largeScreen = (screen.height>950);
    scale = MAX((image->width-1)/max_preview_width,
            (image->height-1)/max_preview_height)+1;
    scale = MAX(2, scale);
    preview_width = image->width / scale;
    preview_height = image->height / scale;
    snprintf(progressText, max_name, "size %dx%d, scale 1/%d",
            image->height, image->width, scale);

    previewHBox = GTK_BOX(gtk_hbox_new(FALSE, 0));
    gtk_container_add(GTK_CONTAINER(previewWindow), GTK_WIDGET(previewHBox));
    previewVBox = gtk_vbox_new(FALSE, 0);
    gtk_box_pack_start(previewHBox, previewVBox, TRUE, TRUE, 2);

    table = table_with_frame(previewVBox, expanderText[raw_expander],
            cfg->expander[raw_expander]);
    pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
            raw_his_size+2, raw_his_height+2);
    rawHisto = gtk_image_new_from_pixbuf(pixbuf);
    g_object_unref(pixbuf);
    pixies = gdk_pixbuf_get_pixels(pixbuf);
    rowstride = gdk_pixbuf_get_rowstride(pixbuf);
    memset(pixies, 0, (gdk_pixbuf_get_height(pixbuf)-1)* rowstride +
            gdk_pixbuf_get_width(pixbuf));
    gtk_table_attach_defaults(table, rawHisto, 0, 4, 4, 5);

    table = table_with_frame(previewVBox, NULL, TRUE);
    adjExposure = adjustment_scale(table, NULL, 0, 0, "Exposure",
            isnan(cfg->exposure) ? 0 : cfg->exposure, &cfg->exposure,
            -3, 3, 0.01, 1.0/6, 2, "EV compensation");
    toggle_button(table, 7, 0, "Unclip", &cfg->unclip);

    if (largeScreen) noteBox = previewVBox;
    else {
        noteBox = gtk_notebook_new();
        gtk_box_pack_start(GTK_BOX(previewVBox), noteBox, FALSE, FALSE, 0);
    }
    table = table_with_frame(noteBox, expanderText[wb_expander],
            cfg->expander[wb_expander]);
    wbCombo = GTK_COMBO_BOX(gtk_combo_box_new_text());
    for (i=0; i<wb_preset_count; i++)
        gtk_combo_box_append_text(wbCombo, wb_preset[i].name);
    gtk_combo_box_set_active(wbCombo, cfg->wb);
    g_signal_connect(G_OBJECT(wbCombo), "changed",
            G_CALLBACK(combo_update), &cfg->wb);
    gtk_table_attach(table, GTK_WIDGET(wbCombo), 0, 2, 0, 1, GTK_FILL, 0, 0, 0);

    adjTemperature = adjustment_scale(table, NULL, 0, 1,
            "Temperature [K]", cfg->temperature, &cfg->temperature,
            2000, 7000, 50, 200, 0,
            "White balance color temperature (K)");
    adjGreen = adjustment_scale(table, NULL, 0, 2, "Green component",
            cfg->green, &cfg->green, 0.2, 2.5, 0.01, 0.05, 2,
            "Green component");

    innerTable = GTK_TABLE(gtk_table_new(5, 4, FALSE));
    gtk_table_attach(table, GTK_WIDGET(innerTable), 0, 6, 3, 4,
            GTK_FILL, 0, 0, 0);
    adjustment_scale(innerTable, NULL, 0, 0, "Spot size",
            cfg->spotSize, (float *)&cfg->spotSize, 1, 10, 1, 1, 0,
            "Spot box size");
    spotLabels = color_labels_new(innerTable, 0, 1, "Spot values:",
            pixel_format);
    spotPatch = GTK_LABEL(gtk_label_new(NULL));
    gtk_table_attach_defaults(innerTable, GTK_WIDGET(spotPatch), 6, 7, 1, 2);
    button = gtk_button_new_with_mnemonic("Spot\nWB");
    gtk_table_attach(table, button, 6, 7, 3, 4, GTK_FILL, 0, 0, 0);
    g_signal_connect(G_OBJECT(button), "clicked",
            G_CALLBACK(spot_wb), NULL);

    table = table_with_frame(noteBox, expanderText[color_expander],
            cfg->expander[color_expander]);
    for (j=0; j<profile_types; j++) {
        label = gtk_label_new(j==in_profile ? "Input profile" :
                j==out_profile ? "Output profile" : "Error");
        gtk_table_attach(table, label, 0, 1, 2*j+1, 2*j+2, 0, 0, 0, 0);
        profileCombo[j] = GTK_COMBO_BOX(gtk_combo_box_new_text());
        for (i=0; i<cfg->profileCount[j]; i++)
            gtk_combo_box_append_text(profileCombo[j],
                    cfg->profile[j][i].name);
        gtk_combo_box_set_active(profileCombo[j], cfg->profileIndex[j]);
        g_signal_connect(G_OBJECT(profileCombo[j]), "changed",
                G_CALLBACK(combo_update), &cfg->profileIndex[j]);
        gtk_table_attach(table, GTK_WIDGET(profileCombo[j]),
                1, 7, 2*j+1, 2*j+2, GTK_FILL, GTK_FILL, 0, 0);
        button = gtk_button_new();
        gtk_container_add(GTK_CONTAINER(button), gtk_image_new_from_stock(
                GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON));
        gtk_table_attach(table, button, 7, 8, 2*j+1, 2*j+2,
                GTK_SHRINK, GTK_FILL, 0, 0);
        g_signal_connect(G_OBJECT(button), "clicked",
                G_CALLBACK(load_profile), (void *)j);
    }
    adjProfileGamma = adjustment_scale(table, NULL, 1, 2, "C.M.Gamma",
            cfg->profile[0][cfg->profileIndex[0]].gamma,
            &cfg->profile[0][0].gamma, 0.1, 1, 0.01, 0.05, 2,
            "Color Management Gamma -\nGamma correction for the input profile");
    combo = GTK_COMBO_BOX(gtk_combo_box_new_text());
    gtk_combo_box_append_text(combo, "Intent perceptual");
    gtk_combo_box_append_text(combo, "Intent relative colorimetric");
    gtk_combo_box_append_text(combo, "Intent saturation");
    gtk_combo_box_append_text(combo, "Intent absolute colorimetric");
    gtk_combo_box_set_active(GTK_COMBO_BOX(combo), cfg->intent);
    g_signal_connect(G_OBJECT(combo), "changed",
            G_CALLBACK(combo_update), &cfg->intent);
    gtk_table_attach(table, GTK_WIDGET(combo), 1, 4, 4, 5, GTK_FILL, 0, 0, 0);

    table = table_with_frame(noteBox, expanderText[curve_expander],
            cfg->expander[curve_expander]);
    curveCombo = GTK_COMBO_BOX(gtk_combo_box_new_text());
    /* Fill in the curve names, skipping "camera curve" if there is
     * no cameraCurve. This will make some mess later with the counting */
    for (i=0; i<cfg->curveCount; i++)
        if ( i!=camera_curve || CFG_cameraCurve )
            gtk_combo_box_append_text(curveCombo, cfg->curve[i].name);
    /* This is part of the mess with the combo_box counting */
    if (cfg->curveIndex>camera_curve && !CFG_cameraCurve)
        gtk_combo_box_set_active(curveCombo, cfg->curveIndex-1);
    else
        gtk_combo_box_set_active(curveCombo, cfg->curveIndex);
    g_signal_connect(G_OBJECT(curveCombo), "changed",
            G_CALLBACK(combo_update), &cfg->curveIndex);
    gtk_table_attach(table, GTK_WIDGET(curveCombo), 0, 6, 0, 1,
            GTK_FILL, GTK_FILL, 0, 0);
    button = gtk_button_new();
    gtk_container_add(GTK_CONTAINER(button), gtk_image_new_from_stock(
            GTK_STOCK_OPEN, GTK_ICON_SIZE_BUTTON));
    gtk_table_attach(table, button, 6, 7, 0, 1, GTK_SHRINK, GTK_FILL, 0, 0);
    g_signal_connect(G_OBJECT(button), "clicked",
            G_CALLBACK(load_curve), NULL);
    i = 1;
    adjGamma = adjustment_scale(table, gammaWidget, 0, i++, "Gamma",
            CFG_gamma, &cfg->curve[0].gamma, 0.1, 2.5, 0.01, 0.05, 2,
            "Gamma correction");
    adjContrast = adjustment_scale(table, contrastWidget, 0, i++,
            "Contrast", CFG_contrast, &cfg->curve[0].contrast,
            -0.99, 2, 0.01, 0.10, 2, "Contrast");
    adjSaturation = adjustment_scale(table, NULL, 0, i++, "Saturation",
            CFG_saturation, &cfg->curve[0].saturation,
            0.0, 3.0, 0.01, 0.1, 2, "Saturation");
    gtk_table_attach(table, gtk_hseparator_new(), 0, 7, i, i+1,
            GTK_FILL, 0, 0, 0);
    i++;
    adjShadow = adjustment_scale(table, NULL, 0, i++, "Shadows",
            CFG_shadow, &cfg->curve[0].shadow, -1.0, 1.0, 0.01, 0.1, 2,
            "Shadows correction");
    adjDepth = adjustment_scale(table, NULL, 0, i++, "Depth",
            CFG_depth, &cfg->curve[0].depth, 0.01, 0.2, 0.01, 0.05, 2,
            "Depth of shadows correction");
    gtk_table_attach(table, gtk_hseparator_new(), 0, 7, i, i+1,
            GTK_FILL, 0, 0, 0);
    i++;
/*  adjBrightness = adjustment_scale(table, NULL, 0, i++, "Brightness",
            CFG_brightness, &cfg->curve[0].brightness, -3, 3, 0.01, 1.0/6, 2,
            "Brightness");
*/  adjBlack = adjustment_scale(table, NULL, 0, i++, "Black point",
            CFG_black, &cfg->curve[0].black, 0, 0.50, 0.01, 0.05, 2,
            "Black point");
    button = gtk_button_new_with_mnemonic("Reset to defaults");
    gtk_table_attach(table, button, 0, 3, i, i+1, GTK_FILL, 0, 0, 0);
    g_signal_connect(G_OBJECT(button), "clicked",
            G_CALLBACK(reset_to_defaults), NULL);
    button = gtk_button_new_with_mnemonic("Auto adjust");
    gtk_table_attach(table, button, 3, 5, i, i+1, GTK_FILL, 0, 0, 0);
    g_signal_connect(G_OBJECT(button), "clicked",
            G_CALLBACK(auto_adjust), NULL);
    button = gtk_button_new_with_mnemonic("Save curve");
    gtk_table_attach(table, button, 5, 7, i, i+1, GTK_FILL, 0, 0, 0);
    g_signal_connect(G_OBJECT(button), "clicked",
            G_CALLBACK(save_curve), NULL);

    table = table_with_frame(previewVBox, expanderText[live_expander],
            cfg->expander[live_expander]);
    pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
            live_his_size+2, live_his_height+2);
    liveHisto = gtk_image_new_from_pixbuf(pixbuf);
    g_object_unref(pixbuf);
    pixies = gdk_pixbuf_get_pixels(pixbuf);
    rowstride = gdk_pixbuf_get_rowstride(pixbuf);
    memset(pixies, 0, (gdk_pixbuf_get_height(pixbuf)-1)* rowstride +
            gdk_pixbuf_get_width(pixbuf));
    gtk_table_attach_defaults(table, liveHisto, 0, 7, 0, 1);
    i = 1;
    avrLabels = color_labels_new(table, 0, i++, "Average:", pixel_format);
    devLabels = color_labels_new(table, 0, i++, "Std. deviation:",
            pixel_format);
    combo = GTK_COMBO_BOX(gtk_combo_box_new_text());
    gtk_combo_box_append_text(combo, "RGB histogram");
    gtk_combo_box_append_text(combo, "R+G+B histogram");
    gtk_combo_box_append_text(combo, "Luminosity hist.");
    gtk_combo_box_append_text(combo, "Value(max) hist.");
    gtk_combo_box_append_text(combo, "Saturation histogram");
    gtk_combo_box_set_active(combo, cfg->histogram);
    g_signal_connect(G_OBJECT(combo), "changed",
            G_CALLBACK(combo_update), &cfg->histogram);
    gtk_table_attach(table, GTK_WIDGET(combo), 4, 7, 1, 3,
            GTK_FILL, 0, 0, 0);
    overLabels = color_labels_new(table, 0, i, "Overexposed:", percent_format);
    toggle_button(table, 4, i, NULL, &cfg->overExp);
    button = gtk_button_new_with_mnemonic("Indicate overexposure");
    gtk_table_attach(table, button, 6, 7, i, i+1,
            GTK_SHRINK|GTK_FILL, GTK_FILL, 0, 0);
    g_signal_connect(G_OBJECT(button), "pressed",
            G_CALLBACK(render_preview), (void *)render_overexposed);
    g_signal_connect(G_OBJECT(button), "released",
            G_CALLBACK(render_preview), (void *)render_default);
    i++;
    underLabels = color_labels_new(table, 0, i, "Underexposed:",
            percent_format);
    toggle_button(table, 4, i, NULL, &cfg->underExp);
    button = gtk_button_new_with_mnemonic("Indicate underexposure");
    gtk_table_attach(table, button, 6, 7, i, i+1,
            GTK_SHRINK|GTK_FILL, GTK_FILL, 0, 0);
    g_signal_connect(G_OBJECT(button), "pressed",
            G_CALLBACK(render_preview), (void *)render_underexposed);
    g_signal_connect(G_OBJECT(button), "released",
            G_CALLBACK(render_preview), (void *)render_default);
    i++;
    
/*    right side of the preview window */
    vBox = gtk_vbox_new(FALSE, 0);
    gtk_box_pack_start(previewHBox, vBox, FALSE, FALSE, 2);
    align = gtk_alignment_new(0.5, 0.5, 0, 0);
    gtk_box_pack_start(GTK_BOX(vBox), align, TRUE, TRUE, 0);
    box = GTK_BOX(gtk_vbox_new(FALSE, 0));
    gtk_container_add(GTK_CONTAINER(align), GTK_WIDGET(box));
    event_box = gtk_event_box_new();
    gtk_box_pack_start(box, event_box, FALSE, FALSE, 0);
    pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
            preview_width, preview_height);
    previewWidget = gtk_image_new_from_pixbuf(pixbuf);
    g_object_unref(pixbuf);
    gtk_container_add(GTK_CONTAINER(event_box), previewWidget);
    g_signal_connect(G_OBJECT(event_box), "button_press_event",
            G_CALLBACK(spot_event), NULL);

    progressBar = GTK_PROGRESS_BAR(gtk_progress_bar_new());
    gtk_box_pack_start(box, GTK_WIDGET(progressBar), FALSE, FALSE, 0);

    box = GTK_BOX(gtk_hbox_new(FALSE, 6));
    gtk_box_pack_start(GTK_BOX(vBox), GTK_WIDGET(box), FALSE, FALSE, 6);
    if (plugin) {
        combo = GTK_COMBO_BOX(gtk_combo_box_new_text());
        gtk_combo_box_append_text(combo, "Full interpolation");
        gtk_combo_box_append_text(combo, "Four color interpolation");
        gtk_combo_box_append_text(combo, "Quick interpolation");
        gtk_combo_box_append_text(combo, "half-size interpolation");
        gtk_combo_box_set_active(combo, cfg->interpolation);
        g_signal_connect(G_OBJECT(combo), "changed",
                G_CALLBACK(combo_update), &cfg->interpolation);
        gtk_box_pack_start(box, GTK_WIDGET(combo), FALSE, FALSE, 0);
    }
    align = gtk_alignment_new(0.99, 0.5, 0, 1);
    gtk_box_pack_start(box, align, TRUE, TRUE, 6);
    box = GTK_BOX(gtk_hbox_new(TRUE, 6));
    gtk_container_add(GTK_CONTAINER(align), GTK_WIDGET(box));
    button = gtk_button_new();
    hbox = GTK_BOX(gtk_hbox_new(FALSE, 6));
    gtk_container_add(GTK_CONTAINER(button), GTK_WIDGET(hbox));
    gtk_box_pack_start(hbox, gtk_image_new_from_stock(
            GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_BUTTON), FALSE, FALSE, 0);
    gtk_box_pack_start(hbox, gtk_label_new_with_mnemonic("Options"),
            FALSE, FALSE, 0);
    g_signal_connect(G_OBJECT(button), "clicked",
            G_CALLBACK(options_dialog), previewWindow);
    gtk_box_pack_start(box, button, TRUE, TRUE, 0);
    button = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
    g_signal_connect(G_OBJECT(button), "clicked",
            G_CALLBACK(window_response), (gpointer)GTK_RESPONSE_CANCEL);
    gtk_box_pack_start(box, button, TRUE, TRUE, 0);
    if (plugin) saveButton = gtk_button_new_from_stock(GTK_STOCK_OK);
    else saveButton = gtk_button_new_from_stock(GTK_STOCK_SAVE_AS);
    gtk_box_pack_start(box, saveButton, TRUE, TRUE, 0);
    gtk_widget_grab_focus(saveButton);
    gtk_widget_show_all(previewWindow);
    if (cfg->curveIndex==gamma_curve || cfg->curveIndex==log_curve)
        for (i=0; i<3; i++) gtk_widget_hide(contrastWidget[i]);
    else
        for (i=0; i<3; i++) gtk_widget_hide(gammaWidget[i]);

    preview_progress("Loading preview", 0.2);
    ufraw_load_raw(image, TRUE);

    shrinkSave = image->cfg->shrink;
    image->cfg->shrink = scale;
    previewImage = &preview_image;
    ufraw_convert_image(previewImage, image);
    image->cfg->shrink = shrinkSave;
    previewImage->cfg = image->cfg;
    if (preview_width!=previewImage->width ||
        preview_height!=previewImage->height) {
        pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
            previewImage->width, previewImage->height);
        gtk_image_set_from_pixbuf(GTK_IMAGE(previewWidget), pixbuf);
        g_object_unref(pixbuf);
    }
    raw_his = (void *)g_new0(int, raw_his_size*3);
    for (y=0; y<previewImage->height; y++)
        for (x=0; x<previewImage->width; x++)
            for (c=0; c<3; c++)
                raw_his[MIN( previewImage->image[y*previewImage->width+x][c] *
                             (raw_his_size-1) / previewImage->rgbMax,
                             raw_his_size-1) ][c]++;
    for (x=0, raw_his_max=1; x<raw_his_size; x++)
        for (c=0; c<3; c++)
            raw_his_max = MAX(raw_his_max, raw_his[x][c]);
    rawHistoInitPixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8,
            raw_his_size+2, raw_his_height+2);
    pixies = gdk_pixbuf_get_pixels(rawHistoInitPixbuf);
    rowstride = gdk_pixbuf_get_rowstride(rawHistoInitPixbuf);
    memset(pixies, 0, (gdk_pixbuf_get_height(rawHistoInitPixbuf)-1)*
            rowstride + gdk_pixbuf_get_width(rawHistoInitPixbuf));
    for (x=0; x<raw_his_size; x++)
        for (y=0; y<raw_his_height; y++)
            for (c=0; c<3; c++)
                pixies[(raw_his_height-y)*rowstride+3*(x+1)+c]=
                        raw_his[x][c]*raw_his_height/raw_his_max>y ? 255:0;
    ufraw_message(UFRAW_VERBOSE, "preview_run: rgbMax=%d, raw_his_max=%d\n",
            previewImage->rgbMax, raw_his_max);
    g_free(raw_his);
    preview_progress(progressText, 0);
    freezeDialog = FALSE;
    update_scales();
    /* The SAVE/OK button is connect only here so it won't be pressed
     * earlier */
    g_signal_connect(G_OBJECT(saveButton), "clicked",
            G_CALLBACK(save_func), image);
    gtk_main();
    status = (long)g_object_get_data(G_OBJECT(previewWindow),
            "WindowResponse");
    gtk_container_foreach(GTK_CONTAINER(previewVBox),
            (GtkCallback)(expander_state), NULL);
    gtk_widget_destroy(previewWindow);
    ufraw_message_handler(UFRAW_SET_PARENT, parentWindow);
    g_object_unref(rawHistoInitPixbuf);
    progressBar = NULL;
    previewImage->developer = NULL;
    ufraw_close(previewImage);
    ufraw_close(image);
    if (status==GTK_RESPONSE_OK) save_configuration(cfg, NULL, 0);
    else *image->cfg = save_cfg;
    if (status!=GTK_RESPONSE_OK) return UFRAW_CANCEL;
    return UFRAW_SUCCESS;
}

Generated by  Doxygen 1.6.0   Back to index