
/*
 *   IMPROV
 *
 *   by Michael Rudolph <rudolpml@hermes.informatik.uni-stuttgart.de>
 *   based on XFQCAM by Paul Chinn <loomer@svpal.org>
 *
 *   QuickCam code by Thomas Davis
 *   Additional QuickCam code Scott Laird <scott@laird.com>
 *   Adapted by Thomas Braunl, Uni Stuttgart, 1996
 *
 *   $Id: xfqcam.c,v 1.10 1996/07/04 12:08:30 rudolpml Exp $
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>
#include <unistd.h>
#include <math.h>
#include <malloc.h>
#include <forms.h>
#include "controlpanel.h"
#include "xfqcam.h"
#include "improc.h"



/*
 *    Static variables visible only to the main module
 */

static void (*RawToX)();
static void (*GetImage)(BYTE *);
static Display * disp;
static Window  win;
static XGCValues values;
static Colormap cmap;
static int xDepth;
static XImage *ximage;
static BYTE ximagemem[IMAGE_SIZE*4];
static GC gc;
static int imageSize = IMAGE_SIZE;
static int screen_num;
static int quitFlag = 0;
static int flagAutoBrightness = 0;
static int flagHistogram;

static char fpstext[80] = "";
static char changetext[80]= "";
static int fakeargc = 1;
static char *fakeargv[2];
static int privatecmap=0;
static struct timeval tv1, tv2;
static double framerate=0, fr;

static double * iplParam;
static FL_OBJECT * lastBrowser;
static int lastObj = -1;
static int lastIndex = -1;
static E_IPLOP editChoice;
static int editMode;


static FD_AboutBox * aboutBox;
static FD_HGram * hg;


/*
 *    some global variable for the IPL parameters and operations
 */

FD_QuickCam * mainwin;
IPL_CONFIG _IPLConfig;
int colortable[64];
volatile int stopFlag;
BYTE lastImageBuffer[IMAGE_SIZE];
BYTE extraImageBuffer[IMAGE_SIZE];


typedef struct _stag
{
    int w, h;
    char label[10];
} SCREENMENU;


static const char * const algogrey[] = {
     "0NOP",
     "0Identity",
     "0Negation",
     "0Dither\t\t(2x2)",
     "0Difference",
     "1Count",
     "1Threshold (t)",
     "-----------------",
     "0Minimum\t(3x3) ",
     "0Maximum\t(3x3) ",
     "0Mean\t\t(3x3) ",
     "0Median\t\t(3x3) ",
     "0Laplace\t\t(3x3) ",
     "0Sobel\t\t(3x3) ",
     "0Corner\t\t(3x3)",
     "-----------------",
     "0Erosion",
     "0Dilation",
     "0Open",
     "0Close",
     "2Fill (x,y)",
     "2Connected (x,y)",
     "0Boundary",
     "0Skeleton",
     "-----------------",
     "1Noise",
     "0Gray stretch",
     "0Gray reduce",
     "2Show (x,y)",
     "1Overlay (g)",
     "1Region (t)",
     "0Find Circles",
     NULL
};


static const char * const helpText[] = {
"IMPROV - Help File",
" ",
" ",
"1. IMPROV - Image Processing for Robot Vision",
" ",
"IMPROV is a simple tool to test image processing functions.",
"Images are taken from the QUICKCAM. Operations may be concatenated, so",
"even a more complex task can also be tested. The captured camera image is",
"shown in the image window #1.",
" ",
"2. Selecting and adding image processing operations",
" ",
"The dropchoice on top of each image allows for the selection of",
"an image processing operation to be performed on the previous image",
"and displayed in the current image window.",
"The chosen operation is displayed in a browser control below the",
"image window. Multiple operations can be successively applied by",
"concatenating them pushing the '+' button to the left of the browser.",
"As long as the operation is not appended to the existing list of",
"operations it may be replaced by some other selection from the dropchoice.",
"Some operations take arguments like the threshold function. Up to three",
"paramters can be set using the slider controls labeld p1, p2 and p3.",
"Paramter values lie in the range [0.0..1.0]. A 0.5 value for the threshold",
"functions means an actual greyscale threshold of 8 (4bpp = 16 different",
"grey values).",
" ",
" ",
"3. Modifying and deleting image processing operations",
" ",
"Changes of the parameter slider controls are effective for the most",
"recent selected image processing operation. Parameters can be changed",
"afterwards by first selecting them in the corresponding browser and then",
"adjusting the parameter slider. A selected operation gets highlighted the",
"'+' push button changes into a '-' push button. Pressing it will remove the",
"operation from the list.",
" ",
"4. The image processing operations",
" ",
"Here is a brief summary of the image processing operations available",
"in IMPROV.",
" ",
"NOP		No Operation",
"Identity  	Copy source image to target image",
"Negation	Negate source image (p`=maxcol-p)",
"Dither(2x2)	Target image contains only black/white pixels",
"Difference	Difference image of source and previous source",
"Count		Count pixels of a given (P1) grey value. Result is shown",
"		in text window.",
"Threshold	Binarize image. Threshold is given by P1",
" ",
"Minimum	3x3 Minimum Filter",
"Maximum	3x3 Maximum Filter",
"Mean   		3x3 Mean Filter",
"Median	   	3x3 Median Filter",
"Laplace   	3x3 Laplace Filter",
"Sobel		3x3 Sobel Filter (absolute gradient value)",
"Corner		3x3 Corner Filter",
" ",
"Erosion	    	3x3 Erosion",
"Dilation    	3x3 Dilation",
"Open	  	3x3 Erosion and then Dilation",
"Close		3x3 Dilation and the Erosion",
"Fill",
"Connected",
"Boundary",
"Skeleton",
" ",
"Noise		Generate Noise in target image (P1=degree of noise)",
"Grey stretch	Make use of all grey values",
"Grey reduce",
"Show (x,y)	Print grey value of pixel at (x=P1,y=P2)",
"Overlay		Copy all pixels of the original where source image pixels",
"		are black",
"Region (t)	Segementation (t=P1 as threshold)",
"Find Circles	Find small circles in source images",
" ",
" ",
" ",
"5. Links",
" ",
"IMPROV - Image Processing for Robot Vision",
"http://www.informatik.uni-stuttgart.de/ipvr/bv/improv",
"ftp://ftp.informatik.uni-stuttgart.de/pub/improv",
" ",
NULL
};




static const char * const aboutText[] = {
	" ",
	"@c@m@bIMPROV - Image Processing for Robot Vision",
        "@c@iSee it live at the IMPROV !",
	"@c@i@bversion 1.10 07/01/96",
	" ",
        "@cby Michael Rudolph (rudolpml@hermes.informatik.uni-stuttgart.de)",
        "@cparts of the IPL by Gerrit Heitsch (heitscgt@hermes...)",
	"@cand Thomas Braunl (braunl@informatik.uni-stuttgart.de)",
        "@cUni Stuttgart, IPVR 1996",
        "@coriginally based on XFQCam and QuickCam code",
	"@cby Paul Chinn(loomer@1000klub.com)",
	"@cThomas Davis and Scott Laird (scott@laird.com)",
	"@cwith additions and modifications by Paul Chinn",
	" ",
        "@chttp://www.informatik.uni-stuttgart.de/ipvr/bv/improv",
        "@cftp://ftp.informatik.uni-stuttgart.de/pub/improv",
	" ",
	"@cThis program is free for any use.",
	NULL};





static void CalcXferMode(void);
static int NewImage(int, int);
static void RawTo8BitX(BYTE * raw, BYTE * xbuf);
static void RawTo24BitX(BYTE * raw, int * xbuf);
static void xqc_createpalette(Colormap cmap);
static void ExitXWindows(void);
static char * GenerateBrowserText(int imageNumber, int n);
static void DeselectBrowser(FL_OBJECT * browser);




/*
 *    FORMS callback functions
 */

void About(FL_OBJECT *obj, long val)
{
    int i;

    fl_clear_browser(aboutBox -> AboutText);

    for(i = 0; aboutText[i] != NULL; i++)
        fl_add_browser_line(aboutBox -> AboutText, aboutText[i]);

    fl_show_form(aboutBox -> AboutBox,
                 FL_PLACE_MOUSE,
                 FL_TRANSIENT,
                 "About IMPROV");
}

void AboutOk(FL_OBJECT *obj, long val)
{
    fl_hide_form(aboutBox -> AboutBox);
}

void Help(FL_OBJECT * obj, long val)
{
    int i;

    fl_clear_browser(aboutBox -> AboutText);

    for(i = 0; helpText[i] != NULL; i++)
        fl_add_browser_line(aboutBox -> AboutText, helpText[i]);

    fl_show_form(aboutBox -> AboutBox,
                 FL_PLACE_MOUSE,
                 FL_TRANSIENT,
                 "IMPROV Help");
}

void LoadSave(FL_OBJECT * obj, long val)
{
    const char * msg = (val == 0) ? "Load" : "Save";
    const char * fileName = fl_show_fselector(msg,
                                              ".",
                                              "*.dat", 
                                              "noname.dat");


    if (fileName != NULL && val == 1)
        QCAMSaveFile(fileName);
    if (fileName != NULL && val == 0)
        QCAMLoadFile(fileName);
}

void QCAMSaveFile(const char * fileName)
{
    int i, j;

    FILE * outFile = fopen(fileName, "w");
    if (outFile == NULL)
        return;

    fputs("IMPROV Data File\n\n", outFile);

    fprintf(outFile, "%d %d %d\n", _QCConfig.brightness,
                                   _QCConfig.whitebalance,
	                           _QCConfig.contrast);

    for (i = 0; i < NO_OF_IMAGES; i++)
    {
        fprintf(outFile, "%d\n", iplCount[i]);
        for (j = 0; j < iplCount[i]; j++)
	{
            fprintf(outFile, "%d ", iplOps[i][j].operation);
            fprintf(outFile, "%1.4f ", iplOps[i][j].param[0]);
            fprintf(outFile, "%1.4f ", iplOps[i][j].param[1]);
            fprintf(outFile, "%1.4f ", iplOps[i][j].param[2]);
            fprintf(outFile, "\n");
	}
    }

    fclose(outFile);

    chown(fileName, getuid(), getgid());
}

void QCAMLoadFile(const char * fileName)
{
    int i, j;
    char line[256];

    FILE * inFile = fopen(fileName, "r");
    if (inFile == NULL)
        return;

    fgets(line, sizeof(line), inFile);
    fgets(line, sizeof(line), inFile);

    fscanf(inFile, "%d %d %d\n", &_QCConfig.brightness,
                                 &_QCConfig.whitebalance,
	                         &_QCConfig.contrast);

    for (i = 0; i < NO_OF_IMAGES; i++)
    {
        fscanf(inFile, "%d\n", &iplCount[i]);

        for (j = 0; j < iplCount[i]; j++)
	{
            int temp;

            fscanf(inFile, "%d ", &temp);
            iplOps[i][j].operation = (E_IPLOP)temp;

            fscanf(inFile, "%lf ", &iplOps[i][j].param[0]);
            fscanf(inFile, "%lf ", &iplOps[i][j].param[1]);
            fscanf(inFile, "%lf ", &iplOps[i][j].param[2]);
	}
    }

    fclose(inFile);
}


void Stop(FL_OBJECT * obj, long val)
{
    stopFlag = fl_get_button(obj);
}

void Slider(FL_OBJECT *obj, long val)
{
    switch (val)
    {
        case 0:
	    _QCConfig.brightness = fl_get_slider_value(obj);
	    QC_set_brightness();
	    break;

	case 1:
	    _QCConfig.whitebalance = fl_get_slider_value(obj);
	    QC_set_whitebalance();
	    break;

	case 2:
	    _QCConfig.contrast = fl_get_slider_value(obj);
	    QC_set_contrast();
	    break;
    }
}

static void DeselectBrowser(FL_OBJECT * browser)
{
    editMode = MODE_ADD;
    fl_deselect_browser(browser);

    if (browser == mainwin -> FD_BROWSER_1)
        fl_set_object_label(mainwin -> FD_DELETE_1, "+");
    else if (browser == mainwin -> FD_BROWSER_2)
        fl_set_object_label(mainwin -> FD_DELETE_2, "+");
    else if (browser == mainwin -> FD_BROWSER_3)
        fl_set_object_label(mainwin -> FD_DELETE_3, "+");
    else if (browser == mainwin -> FD_BROWSER_4)
        fl_set_object_label(mainwin -> FD_DELETE_4, "+");
    else if (browser == mainwin -> FD_BROWSER_5)
        fl_set_object_label(mainwin -> FD_DELETE_5, "+");
}

void XQC_Browser(FL_OBJECT * obj, long val)
{
    int selection;
    int imageNumber = (int)val;

    if (lastBrowser != NULL && lastBrowser != obj &&
        fl_get_browser(lastBrowser) != 0)
            DeselectBrowser(lastBrowser);

    lastBrowser = obj;
    selection = fl_get_browser(obj);

    if (selection > 0)
    {
        lastObj = imageNumber;
        lastIndex = selection - 1;
        iplParam = &iplOps[imageNumber][selection-1].param[0];

        fl_set_slider_value(mainwin -> FD_PARAM_1, iplParam[0]);
        fl_set_slider_value(mainwin -> FD_PARAM_2, iplParam[1]);
        fl_set_slider_value(mainwin -> FD_PARAM_3, iplParam[2]);

        editMode = MODE_DEL;

        switch (imageNumber)
	{
	    case 0:
                fl_set_object_label(mainwin -> FD_DELETE_1, "-");
                break;
	    case 1:
                fl_set_object_label(mainwin -> FD_DELETE_2, "-");
                break;
	    case 2:
                fl_set_object_label(mainwin -> FD_DELETE_3, "-");
                break;
	    case 3:
                fl_set_object_label(mainwin -> FD_DELETE_4, "-");
                break;
	    case 4:
                fl_set_object_label(mainwin -> FD_DELETE_5, "-");
                break;
	}
    }
}

void XQC_Button(FL_OBJECT * obj, long val)
{
    int selection;
    int imageNumber = (int)val;
    FL_OBJECT * browser = NULL;
    char * browserText;

    switch (imageNumber)
    {
        case 0:
            browser = mainwin -> FD_BROWSER_1;
            break;
        case 1:
            browser = mainwin -> FD_BROWSER_2;
            break;
        case 2:
            browser = mainwin -> FD_BROWSER_3;
            break;
        case 3:
            browser = mainwin -> FD_BROWSER_4;
            break;
        case 4:
            browser = mainwin -> FD_BROWSER_5;
            break;
        default:
            return;
    }

    if (editMode == MODE_DEL)
    {
        selection = fl_get_browser(browser);

        if (selection > 0)
        {
            fl_delete_browser_line(browser, selection);

            while (selection < iplCount[imageNumber])
            {
                iplOps[imageNumber][selection-1] = iplOps[imageNumber]
                                                         [selection];

                browserText = GenerateBrowserText(imageNumber, selection-1);
                fl_replace_browser_line(browser, selection, browserText);
                selection++;
            }

            iplCount[imageNumber]--;

            if (iplCount[imageNumber] == 0)
	    {
                iplCount[imageNumber] = 1;
                iplOps[imageNumber][0].operation = E_NOP;
                browserText = GenerateBrowserText(imageNumber, 0);
                fl_add_browser_line(browser, browserText);
	    }

            DeselectBrowser(browser);
	}
    }
    else if (editMode == MODE_ADD)
    {
        int n;

        if (lastBrowser != NULL && fl_get_browser(lastBrowser) != 0)
	{
            DeselectBrowser(lastBrowser);
            lastBrowser = NULL;
	}

        /*
         *    Get the current number of successive image operations and
         *    increment the counter. Store the operation code and parameters
         *    in the op array.
         */

        n = iplCount[imageNumber];
        if (n < MAX_IPL_OPS)
        {
            iplParam = &iplOps[imageNumber][n-1].param[0];
            iplParam[0] = fl_get_slider_value(mainwin -> FD_PARAM_1);
            iplParam[1] = fl_get_slider_value(mainwin -> FD_PARAM_2);
            iplParam[2] = fl_get_slider_value(mainwin -> FD_PARAM_3);

            iplCount[imageNumber]++;
            iplOps[imageNumber][n].operation = E_NOP;
            iplParam = &iplOps[imageNumber][n].param[0];
            lastIndex = n;
            lastObj = imageNumber;

            browserText = GenerateBrowserText(imageNumber, n-1);
            fl_replace_browser_line(browser, n, browserText);
            browserText = GenerateBrowserText(imageNumber, n);
            fl_add_browser_line(browser, browserText);
            fl_set_browser_topline(browser, n + 1);
        }
    }
}

void XQC_Param(FL_OBJECT * obj, long val)
{
    if (iplParam != NULL)
        iplParam[val] = fl_get_slider_value(obj);

    if (lastObj >= 0 && lastIndex >= 0)
    {
        char * browserText = GenerateBrowserText(lastObj, lastIndex);
        FL_OBJECT * browser = NULL;

        if (lastObj == 0)
            browser = mainwin -> FD_BROWSER_1;
        else if (lastObj == 1)
            browser = mainwin -> FD_BROWSER_2;
        else if (lastObj == 2)
            browser = mainwin -> FD_BROWSER_3;
        else if (lastObj == 3)
            browser = mainwin -> FD_BROWSER_4;
        else if (lastObj == 4)
            browser = mainwin -> FD_BROWSER_5;

        fl_replace_browser_line(browser, lastIndex + 1, browserText);
    }
}

void XQC_graph1(FL_OBJECT *obj, long val)
{
    int n;
    FL_OBJECT * browser = NULL;

    /*
     *    If we switch to anoter image save the parameter values (slider
     *    values) in the corresponding structure.
     */

    if (iplParam != NULL)
    {
        iplParam[0] = fl_get_slider_value(mainwin -> FD_PARAM_1);
        iplParam[1] = fl_get_slider_value(mainwin -> FD_PARAM_2);
        iplParam[2] = fl_get_slider_value(mainwin -> FD_PARAM_3);
    }

    /*
     *    Determine the new image and the operation to be performed
     */

    editChoice = fl_get_choice(obj) - 1;
    editMode = MODE_ADD;

    if (obj == mainwin -> FD_graph1)
    {
        lastObj = 0;
        browser = mainwin -> FD_BROWSER_1;
        fl_set_object_label(mainwin -> FD_DELETE_1, "+");
    }
    else if (obj == mainwin -> FD_graph2)
    {
        lastObj = 1;
        browser = mainwin -> FD_BROWSER_2;
        fl_set_object_label(mainwin -> FD_DELETE_2, "+");
    }
    else if (obj == mainwin -> FD_graph3)
    {
        lastObj = 2;
        browser = mainwin -> FD_BROWSER_3;
        fl_set_object_label(mainwin -> FD_DELETE_3, "+");
    }
    else if (obj == mainwin -> FD_graph4)
    {
        lastObj = 3;
        browser = mainwin -> FD_BROWSER_4;
        fl_set_object_label(mainwin -> FD_DELETE_4, "+");
    }
    else if (obj == mainwin -> FD_graph5)
    {
        lastObj = 4;
        browser = mainwin -> FD_BROWSER_5;
        fl_set_object_label(mainwin -> FD_DELETE_5, "+");
    }

    n = iplCount[lastObj];
    if (n < MAX_IPL_OPS)
    {
        lastIndex = n - 1;
        iplParam = &iplOps[lastObj][n-1].param[0];
        iplParam[0] = fl_get_slider_value(mainwin -> FD_PARAM_1);
        iplParam[1] = fl_get_slider_value(mainwin -> FD_PARAM_2);
        iplParam[2] = fl_get_slider_value(mainwin -> FD_PARAM_3);
        iplOps[lastObj][n-1].operation = editChoice;
        fl_replace_browser_line(browser,
                                n,
                                GenerateBrowserText(lastObj, n-1));

        if (lastBrowser != NULL && fl_get_browser(lastBrowser) != 0)
	{
            DeselectBrowser(lastBrowser);
            lastBrowser = NULL;
	}

    }
}


static char * GenerateBrowserText(int imageNumber, int n)
{
    static char browserText[80];
    char partialText[80];
    E_IPLOP operation = iplOps[imageNumber][n].operation;
    const char * s = &algogrey[operation][1];
    int args = algogrey[operation][0] - '0';
    int len = strcspn(s, "(");

    strncpy(partialText, s, len);
    partialText[len] = '\0';

    switch (args)
    {
        case 1:
            sprintf(&browserText[0],
                    "%d.%s(%1.2g)",
                    n + 1,
                    partialText,
                    iplOps[imageNumber][n].param[0]);
            break;

	case 2:
            sprintf(&browserText[0],
                    "%d.%s(%1.2g,%1.2g)",
                    n + 1,
                    partialText,
                    iplOps[imageNumber][n].param[0],
                    iplOps[imageNumber][n].param[1]);
            break;

        default:
            sprintf(&browserText[0], "%d.%s", n + 1, s);
    }

    return &browserText[0];
}

void Zoom(FL_OBJECT *obj, long val)
{
    _QCConfig.zoom = val;
    CalcXferMode();
}

void Histogram(FL_OBJECT *obj, long val)
{
}


void automode(FL_OBJECT *obj, long val)
{
    flagAutoBrightness = fl_get_button(obj);
}

void Graph(FL_OBJECT *obj, long val)
{
    flagHistogram = fl_get_button(obj);

    if (flagHistogram) 
        fl_show_form(hg -> HGram, FL_PLACE_FREE, FL_TRANSIENT, "Histogram");
    else
        fl_hide_form(hg -> HGram);
}

void Quit(FL_OBJECT *obj, long val)
{
    quitFlag = 1;
}

int QCAMinit()
{
    int i;

    if (_QCConfig.quickcam_port != 0)
    {
        if(_QCConfig.quickcam_port != 0x278 && 
           _QCConfig.quickcam_port != 0x378 &&
           _QCConfig.quickcam_port != 0x3bc)
	{
	    fprintf(stderr, "Invalid port address\n");
	    return (1);
	}

	ioperm(_QCConfig.quickcam_port,3,1);

    }
    else
    {
        _QCConfig.quickcam_port = QC_find();
	if (_QCConfig.quickcam_port < 0)
        {
	    fprintf(stderr,"Can't get IO perms\n");
	    return(1);
	}
	else if(_QCConfig.quickcam_port == 0)
	{
	    fprintf(stderr,"Can't find a camera\n");
	    return 1;
	}
    }

    QC_reset(_QCConfig.quickcam_port);


    fakeargv[0] = malloc(10);
    fakeargv[1] = malloc(10);
    strcpy(fakeargv[0], "robot");
    strcpy(fakeargv[1], "hallo");
    disp = fl_initialize(&fakeargc, fakeargv, 0, 0, 0);

    if(_QCConfig.zoom > 2 || _QCConfig.zoom < 0)
    {
        fprintf(stderr, "Zoom values are: 0(no zoom), 1(1.5x), 2(2x)\n");
	return 1;
    }

    mainwin = create_form_QuickCam();
    aboutBox = create_form_AboutBox();
    hg = create_form_HGram();

    /*intialize various control setting*/
    fl_set_slider_bounds(mainwin->Brightness, 0, 255);
    fl_set_slider_step(mainwin->Brightness, 1);
    fl_set_slider_precision(mainwin->Brightness, 0);
    fl_set_slider_bounds(mainwin->Contrast, 0, 255);
    fl_set_slider_step(mainwin->Contrast, 1);
    fl_set_slider_precision(mainwin->Contrast, 0);
    fl_set_slider_bounds(mainwin->WhiteBal, 0, 255);
    fl_set_slider_step(mainwin->WhiteBal, 1);
    fl_set_slider_precision(mainwin->WhiteBal, 0);

    mainwin->FD_fps->label = fpstext;
    mainwin->FD_rez->label = changetext;

  
    /* New menues for image processing are defined below */

    for(i=0; algogrey[i] != NULL; i++)
    {
        fl_addto_choice(mainwin -> FD_graph1, &algogrey[i][1]);
        fl_addto_choice(mainwin -> FD_graph2, &algogrey[i][1]);
        fl_addto_choice(mainwin -> FD_graph3, &algogrey[i][1]);
        fl_addto_choice(mainwin -> FD_graph4, &algogrey[i][1]);
        fl_addto_choice(mainwin -> FD_graph5, &algogrey[i][1]);

        if (algogrey[i][0] == '-')
	{
            fl_set_choice_item_mode(mainwin -> FD_graph1, i + 1, FL_PUP_GREY);
            fl_set_choice_item_mode(mainwin -> FD_graph2, i + 1, FL_PUP_GREY);
            fl_set_choice_item_mode(mainwin -> FD_graph3, i + 1, FL_PUP_GREY);
            fl_set_choice_item_mode(mainwin -> FD_graph4, i + 1, FL_PUP_GREY);
            fl_set_choice_item_mode(mainwin -> FD_graph5, i + 1, FL_PUP_GREY);
	}
    }

    for (i = 0; i < iplCount[0]; i++)
        fl_add_browser_line(mainwin -> FD_BROWSER_1,
                            GenerateBrowserText(0, i));

    for (i = 0; i < iplCount[1]; i++)
        fl_add_browser_line(mainwin -> FD_BROWSER_2,
                            GenerateBrowserText(1, i));

    for (i = 0; i < iplCount[2]; i++)
        fl_add_browser_line(mainwin -> FD_BROWSER_3,
                            GenerateBrowserText(2, i));

    for (i = 0; i < iplCount[3]; i++)
        fl_add_browser_line(mainwin -> FD_BROWSER_4,
                            GenerateBrowserText(3, i));

    for (i = 0; i < iplCount[4]; i++)
        fl_add_browser_line(mainwin -> FD_BROWSER_5,
                            GenerateBrowserText(4, i));

    /* New menues for image processing are defined above */

    /*set the sliders to qcam values*/
    fl_set_slider_value(mainwin->Brightness, _QCConfig.brightness);
    fl_set_slider_value(mainwin->Contrast, _QCConfig.contrast);
    fl_set_slider_value(mainwin->WhiteBal, _QCConfig.whitebalance);
	
    CalcXferMode();
	
    fl_show_form(mainwin -> QuickCam,
                 FL_PLACE_SIZE,
                 FL_FULLBORDER,
                 "IMPROV - Image Processing for Robot Vision");


    xDepth = fl_state[fl_get_vclass()].depth;

    /*fprintf(stderr, "X color depth = %d bits\n", xDepth);*/

    if(xDepth == 8 || xDepth == 4)
        RawToX = RawTo8BitX;
    else if (xDepth == 24)
        RawToX = RawTo24BitX;
    else
    {
	fprintf(stderr, "IMPROV only works in 8/24 bit X depths\n");
	fprintf(stderr, "This diplay has a %d bit color depth\n", xDepth);
        QC_exit();
	exit(1);
    }

    screen_num = DefaultScreen(disp);
    gc = XCreateGC(disp, FL_ObjWin(mainwin -> FD_image1), 0, &values);

    if (privatecmap)
    {
        cmap = XCreateColormap(disp,
                               win,
			       DefaultVisual(disp, screen_num),
			       AllocNone);

        XSetWindowColormap(disp, win, cmap);
    }
    else
        cmap = DefaultColormap(disp, screen_num);

    xqc_createpalette(cmap);

    ximage = NULL;
    NewImage(_QCConfig.xsize, _QCConfig.ysize);

    /* Attempt to get permission to access IO ports.  Must be root */

    setuid(getuid());

    QC_reset(_QCConfig.quickcam_port);
    QC_set_all();
       
    gettimeofday(&tv1,NULL);

    return 0;
}


int QCAMset(double thr, int con, int bri, int whi)
{
    if (thr != 0.0) _QCConfig.thresh       = thr;
    if (con != 0)   _QCConfig.contrast     = con;
    if (bri != 0)   _QCConfig.brightness   = bri;
    if (whi != 0)   _QCConfig.whitebalance = whi;

    return 0;
}

int QCAMget(BYTE rawbuf[], int * ret_chg, int * ret_quit)
{
    int i;
    long pixelDelta;

    fl_check_forms();

    *ret_chg = 0;
    *ret_quit = quitFlag;

    if (stopFlag)
        return 0;

    GetImage(rawbuf);
    RawToX(rawbuf, &ximagemem[0]);

    XPutImage(disp,
              FL_ObjWin(mainwin -> FD_image1),
              gc,
              ximage,
              0,
              0,
	      mainwin -> FD_image1 -> x + 4,
	      mainwin -> FD_image1 -> y + 4,
              ximage -> width,
              ximage -> height);

    XFlush(disp);
    
    /* Calculate frame rate */
    gettimeofday(&tv2, NULL);
    
    /* The frame rate is calculated using the TCP RTT algorithm */
    fr = (1.0 / (tv2.tv_sec - tv1.tv_sec +
                (tv2.tv_usec-tv1.tv_usec) / 1000000.0));

    if (framerate != 0)
        framerate = 0.9 * framerate + 0.1 * fr;
    else
        framerate = fr;
    
    sprintf(fpstext, "Rate: %.2f fps", framerate);
    fl_redraw_object(mainwin -> FD_fps);

    tv1.tv_sec  = tv2.tv_sec;
    tv1.tv_usec = tv2.tv_usec;

    for (i = 0, pixelDelta = 0; i < imageSize; i++)
        pixelDelta += (lastImageBuffer[i] - rawbuf[i]) *
                      (lastImageBuffer[i] - rawbuf[i]);

    /*    memcpy(extraImageBuffer, lastImageBuffer, imageSize); */
    memcpy(lastImageBuffer, rawbuf, imageSize);

    sprintf(changetext, "change: %2.2f", sqrt((double)pixelDelta / imageSize));
    fl_redraw_object(mainwin -> FD_rez);

    if (flagHistogram)
    {
        int i;
        int histo[256];
        float fhisto[256];
        float fxz[256];

        IPL_gen_histogram(rawbuf, &histo[0]);

        for (i = _IPLConfig.colorBlack; i <= _IPLConfig.colorWhite; i++)
	{
	    fxz[i] = i;
            fhisto[i] = histo[i];
        }

        fl_set_xyplot_ybounds(hg -> Histogram, 0, imageSize / 3);
        fl_set_xyplot_data(hg -> Histogram,
                           &fxz[0],
                           &fhisto[0],
                           _IPLConfig.colorWhite - _IPLConfig.colorBlack + 1,
                           "Histogram",
                           "Intensity",
                           "Pixels");  
    }

    if (flagAutoBrightness)
    {
        int i;
        long sum = 0;
        double mean;
        static double tempz = 0.0;
        static double tempint = 0.0;

        for (i = 0; i < imageSize; i++)
            sum += rawbuf[i];
        mean = (double)sum / imageSize;

        if (mean > 8)
            tempint = - MAX_AUTO_ADJUST * ((mean - 8) / 7);
        else if (mean < 7)
            tempint = MAX_AUTO_ADJUST * (1 - (mean / 7));

        if (mean > 8 || mean < 7)
	{
	    tempz = fl_get_slider_value(mainwin -> Brightness);
	    tempz += tempint;

	    if (tempz >= 255)
                tempz = 254;

	    if (tempz <= 0)
                tempz = 1;

	    fl_set_slider_value(mainwin -> Brightness, tempz);
	    Slider(mainwin -> Brightness, 0);
        }
    }

    return 0;
}

int QCAMput(BYTE raw[], int imageNumber)
{
    int x, y;

    switch (imageNumber)
    {
        case 0:
            x = mainwin -> FD_image1 -> x + 5;
            y = mainwin -> FD_image1 -> y + 5;
            break;
        case 1:
            x = mainwin -> FD_image2 -> x + 5;
            y = mainwin -> FD_image2 -> y + 5;
            break;
        case 2:
            x = mainwin -> FD_image3 -> x + 5;
            y = mainwin -> FD_image3 -> y + 5;
            break;
        case 3:
            x = mainwin -> FD_image4 -> x + 5;
            y = mainwin -> FD_image4 -> y + 5;
            break;
        case 4:
            x = mainwin -> FD_image5 -> x + 5;
            y = mainwin -> FD_image5 -> y + 5;
            break;
        case 5:
            x = mainwin -> FD_image6 -> x + 5;
            y = mainwin -> FD_image6 -> y + 5;
            break;
        default:
            return -1;
    }

    RawToX(raw, &ximagemem[0]); 

    XPutImage(disp,
              FL_ObjWin(mainwin -> FD_image1),
              gc,
              ximage,
              0,
              0,
              x,
              y,
              ximage -> width, 
              ximage -> height);

    XFlush(disp);

    return 0;
}

int QCAMexit()
{
    QC_reset(_QCConfig.quickcam_port);  
    ExitXWindows();

    return 0;
}


static void ExitXWindows(void)
{
}

static void RawTo8BitX(BYTE * raw, BYTE * xbuf)
{
    int i;

    for(i = 0; i < imageSize; i++)
        *xbuf++ = colortable[(int)*raw++];
}

static void RawTo24BitX(BYTE * raw, int * xbuf)
{
    int i;
    int c;

    for(i = 0; i < imageSize; i++)
    {
        c = (int)*raw++;
        *xbuf++ = c;
    }
}

static void xqc_createpalette(Colormap cmap)
{
    int i;
    int rc;
    XColor col;

    for(i = 0; i < 16; i++)
    {
        col.red =col.green = col.blue = i * 1024 * 4;
	rc = XAllocColor(disp, cmap, &col);
	colortable[i] = col.pixel;
	/* fprintf(stdout, "rc=%d %08lX\n", rc, col.pixel);*/
    }

    /* fprintf(stdout, "xdepth=%d\n", xDepth);*/

    return;
}

static int NewImage(int w, int h)
{
    if (ximage == NULL || w != ximage -> width || h != ximage -> height)
    {
        if (ximage != NULL)
            XDestroyImage(ximage);

        ximage = XCreateImage(disp,
			      DefaultVisual(disp,screen_num),
                              xDepth,
			      ZPixmap,
                              0,
                              &ximagemem[0],
                              w,
                              h,
			      8,
                              0);
	return 1;
    }

    return 0;
}


static void CalcXferMode()
{
    if (_QCConfig.xsize > 160 || _QCConfig.ysize > 120)
        _QCConfig.xfermode = 0;
    else if(_QCConfig.xsize > 80 || _QCConfig.ysize > 60)
    {
        if (_QCConfig.zoom)
            _QCConfig.xfermode = 0;
	else
            _QCConfig.xfermode = 4;
    }
    else
    {
        if(_QCConfig.zoom == 2)
            _QCConfig.xfermode = 0;
        else if (_QCConfig.zoom == 1)
            _QCConfig.xfermode = 4;
        else
            _QCConfig.xfermode = 8;
    }

    if (_QCConfig.bpp6)
        _QCConfig.xfermode += 2;

    _QCConfig.xfermode += _QCConfig.biMode;

    GetImage = QC_Get4bppImageUni;
}

