/*
* This file is part of rasdaman community.
*
* Rasdaman community is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Rasdaman community is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with rasdaman community. If not, see .
*
* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann /
rasdaman GmbH.
*
* For more information please see
* or contact Peter Baumann via .
/
/**
* PURPOSE:
* Colourspace mapper class and support classes.
* COMMENTS:
* No comments
*/
// Standard wxWindows preamble.
#ifdef __GNUG__
#pragma implementation
#endif
// changed in wxWindows 2.4.2:
//#include "wx_prec.h"
#include
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include
#endif
#include
#include
#include
#include
#include
#include
#ifdef EARLY_TEMPLATE
#define __EXECUTABLE__
#endif
#include "raslib/rmdebug.hh"
#include "wx_pixmap_translate.h"
#include "rviewUtils.hh"
#include "labelManager.hh"
#include "rviewColMap.hh"
#include "rviewPrefs.hh"
#include "rviewMDD.hh"
/*
* Conversion functions shared by colourspaceCanvas and colourspaceMapper:
* convert a value normalized to [0,1] to a value normalized to [0,1]
*/
static inline double valueToGauss(double value, double peak, double invSig)
{
double h;
h = (value - peak) * invSig; return exp(-h*h);
}
static inline double valueToLinear(double value, double peak, double invSig)
{
double h;
if (value < peak)
h = 1.0 - invSig*(peak - value);
else
h = 1.0 - invSig*(value - peak);
if (h < 0) h = 0;
return h;
}
static inline double valueToRectangle(double value, double peak, double invSig)
{
double h;
if (value < peak)
h = 1.0 - invSig*(peak - value);
else
h = 1.0 - invSig*(value - peak);
if (h > 0) h = 1.0; else h = 0.0;
return h;
}
static inline double valueToAsymptotic(double value, double peak, double invSig)
{
double h;
if (value < peak) h = 0;
else
h = 1.0 - exp((peak - value) * invSig);
return h;
}
/*
* Colourspace mapper editor canvas class.
*/
const int colourspaceCanvas::colcanv_cborder = 8;
const int colourspaceCanvas::colcanv_mheight = 8;
colourspaceCanvas::colourspaceCanvas(colourspaceFrame *parent, colourspace_params *p, int x, int y, int w, int h, long style) : wxCanvas((wxWindow*)parent, x, y, w, h, style)
{
params = p;
canvX = w; canvY = h;
parentObj = parent;
canvX=100; canvY=100;
values = NULL;
setDrawingFunction();
brush.SetStyle(wxSOLID); brush.SetColour((char)0xe0, (char)0xe0, (char)0xe0);
redPen.SetStyle(wxSOLID); redPen.SetColour((char)0xff, 0, 0);
greenPen.SetStyle(wxSOLID); greenPen.SetColour(0, (char)0xff, 0);
bluePen.SetStyle(wxSOLID); bluePen.SetColour(0, 0, (char)0xff);
blackPen.SetStyle(wxSOLID); blackPen.SetColour(0, 0, 0);
font = new wxFont(12, wxROMAN, wxNORMAL, wxNORMAL);
SetBackground(&brush);
SetFont(font);
}
colourspaceCanvas::~colourspaceCanvas(void)
{
SetFont(NULL);
SetBackground(NULL);
delete font;
if (values != NULL) delete [] values;
}
void colourspaceCanvas::setDrawingFunction(void)
{
switch (params->type)
{
case cst_gauss:
conversionFunction = valueToGauss; break;
case cst_linear:
conversionFunction = valueToLinear; break;
case cst_rectangle:
conversionFunction = valueToRectangle; break;
case cst_asympt:
conversionFunction = valueToAsymptotic; break;
default:
cerr << "Unknown drawing function (" << (int)(params->type) << ")" << endl;
conversionFunction = NULL;
break;
}
//cout << "cspace type " << (int)(params->type) << endl;
}
void colourspaceCanvas::enableOutlineSum(bool enable)
{
if (enable)
{
if (values == NULL)
{
values = new float[canvX]; Redraw();
}
}
else
{
if (values != NULL)
{
delete [] values; values = NULL; Redraw();
}
}
}
void colourspaceCanvas::OnSize(int w, int h)
{
canvX = w; canvY = h;
height = (float)(canvY - 2*colcanv_cborder);
base = (float)(canvY - colcanv_cborder);
step = 1.0 / (canvX - 2*colcanv_cborder);
cmin = colcanv_cborder; cmax = canvX - colcanv_cborder;
if (values != NULL)
{
delete [] values; values = new float[canvX];
}
}
void colourspaceCanvas::Redraw(void)
{
Refresh(TRUE);
}
void colourspaceCanvas::OnPaint(void)
{
wxRect rect;
int y = canvY - colcanv_cborder;
float x, xhigh, markstep;
markstep = (canvX - 2*colcanv_cborder) / 10.0;
xhigh = (float)(canvX - colcanv_cborder) + 0.9;
BeginDrawing();
wxUpdateIterator upd(this);
while (upd)
{
upd.GetRect(&rect);
if (values != NULL)
memset(values, 0, canvX*sizeof(float));
if (conversionFunction != NULL)
{
drawOutline(params->peak_red, params->sigm_red, &redPen, &rect);
drawOutline(params->peak_green, params->sigm_green, &greenPen, &rect);
drawOutline(params->peak_blue, params->sigm_blue, &bluePen, &rect);
}
if (values != NULL)
drawOutlineSum(&blackPen, &rect);
SetPen(&blackPen);
IntDrawLine(colcanv_cborder, y, canvX - colcanv_cborder, y);
for (x=(float)colcanv_cborder; x<=xhigh; x += markstep)
{
IntDrawLine((int)x, y + colcanv_mheight/2, (int)x, y - colcanv_mheight/2);
}
upd++;
}
SetPen(NULL);
EndDrawing();
}
void colourspaceCanvas::OnEvent(wxMouseEvent &mevt)
{
parentObj->processMouseEvent(mevt);
}
int colourspaceCanvas::setupRectangle(int &from, int &to, float &x, wxRect *rect)
{
if (rect == NULL)
{
from = cmin; from = cmax; x = 0.0;
}
else
{
from = rect->x; to = from + rect->width + 1;
if (((from < cmin) && (to < cmin)) || ((from > cmax) && (to > cmax)))
return -1;
if (from < cmin) from = cmin;
if (to > cmax) to = cmax;
x = (from - cmin) * step;
}
return 0;
}
void colourspaceCanvas::drawOutline(double peak, double sigma, wxPen *pen, wxRect *rect)
{
float mid = (canvX - 2*colcanv_cborder) * peak + colcanv_cborder;
float x, y, lastY;
float invSigma;
int i, j;
SetPen(pen);
DrawLine(mid, base, mid, 0.0);
invSigma = 1.0 / sigma;
if (setupRectangle(i, j, x, rect) != 0) return;
lastY = height * conversionFunction(x, peak, invSigma);
if (values != NULL)
values[i] += lastY;
for (i++, x += step; i < j; i++, x+=step)
{
y = height * conversionFunction(x, peak, invSigma);
DrawLine((float)i-1, base - lastY, (float)i, base - y);
lastY = y;
if (values != NULL)
values[i] += y;
}
}
void colourspaceCanvas::drawOutlineSum(wxPen *pen, wxRect *rect)
{
int i, j;
float x, y, lastY;
pen->SetStyle(wxSHORT_DASH); SetPen(pen);
DrawLine((float)colcanv_cborder, base - height/3, (float)(canvX - colcanv_cborder), base - height/3);
pen->SetStyle(wxSOLID); SetPen(pen);
if (setupRectangle(i, j, x, rect) != 0) return;
lastY = values[i] / 3;
for (i++, x += step; i < j; i++, x+= step)
{
y = values[i] / 3;
DrawLine((float)i-1, base - lastY, (float)i, base - y);
lastY = y;
}
}
/*
* Colourspace mapper editor frame class.
*/
const int colourspaceFrame::colspc_border = 8;
const int colourspaceFrame::colspc_width = 450;
const int colourspaceFrame::colspc_height = 400;
const int colourspaceFrame::colspc_bwidth = 80;
const int colourspaceFrame::colspc_bheight = 30;
const int colourspaceFrame::colspc_twidth = 100;
const int colourspaceFrame::colspc_theight = 30;
const int colourspaceFrame::colspc_chkheight = 20;
const int colourspaceFrame::colspc_chwidth = colourspaceFrame::colspc_twidth - rview_choice_sub_width;
const int colourspaceFrame::colspc_chheight = 20;
const int colourspaceFrame::colspc_cheight = colourspaceFrame::colspc_bheight + 4*colourspaceFrame::colspc_theight + 5*colourspaceFrame::colspc_border;
colourspaceFrame::colourspaceFrame(colourspaceMapper *parent, const colourspace_params *p) : rviewFrame(NULL, lman->lookup("titleColourspace"), 0, 0, colspc_width, colspc_height)
{
// defaults
doImmediateUpdate = TRUE; doDrawSum = TRUE;
parentObj = parent;
memcpy(&newParams, p, sizeof(colourspace_params));
memcpy(&origParams, p, sizeof(colourspace_params));
canvX = 0; canvY = 0;
mousebut = 0; didUpdate = 0;
canvas = new colourspaceCanvas(this, &newParams, 0, 0, 100, 100, wxBORDER);
panel = new wxPanel((wxWindow*)this, 0, 100, 100, 100);
okBut = new rviewButton(panel);
cancelBut = new rviewButton(panel);
defaultBut = new rviewButton(panel);
immediateUpdate = new rviewCheckBox(panel);
immediateUpdate->SetValue(doImmediateUpdate);
drawSum = new rviewCheckBox(panel);
drawSum->SetValue(doDrawSum);
// the text widgets displaying the current setup
posR = new rviewText(panel);
posG = new rviewText(panel);
posB = new rviewText(panel);
sigR = new rviewText(panel);
sigG = new rviewText(panel);
sigB = new rviewText(panel);
minVal = new rviewText(panel);
maxVal = new rviewText(panel);
const char *cstypes[4];
cstypes[0] = lman->lookup("cspaceTypeGauss");
cstypes[1] = lman->lookup("cspaceTypeLin");
cstypes[2] = lman->lookup("cspaceTypeRect");
cstypes[3] = lman->lookup("cspaceTypeAsympt");
csType = new rviewChoice(panel, 4, cstypes);
csType->SetLabel("");
updateDisplay();
Show(TRUE);
label();
frameWidth = -1; frameHeight = -1;
OnSize(colspc_width, colspc_height);
canvas->enableOutlineSum(doDrawSum);
}
colourspaceFrame::~colourspaceFrame(void)
{
if (parentObj != NULL) parentObj->closeEditor(FALSE);
}
const char *colourspaceFrame::getFrameName(void) const
{
return "colourspaceFrame";
}
rviewFrameType colourspaceFrame::getFrameType(void) const
{
return rviewFrameTypeCspace;
}
void colourspaceFrame::unlinkParent(void)
{
parentObj = NULL;
}
void colourspaceFrame::setRange(double newMinVal, double newMaxVal)
{
char buffer[STRINGSIZE];
newParams.minVal = newMinVal; newParams.maxVal = newMaxVal;
// Just update the text widgets. Rebuilding the pixmap is done automatically.
sprintf(buffer, "%f", newMinVal);
minVal->SetValue(buffer);
sprintf(buffer, "%f", newMaxVal);
maxVal->SetValue(buffer);
}
void colourspaceFrame::OnSize(int w, int h)
{
int x, y, px, py;
GetClientSize(&x, &y);
if ((x == frameWidth) && (y == frameHeight))
return;
frameWidth = x;
frameHeight = y;
y -= colspc_cheight;
canvX = x - 2*colspc_border;
canvY = y - 2*colspc_border;
canvas->SetSize(colspc_border, colspc_border, canvX, canvY);
panel->SetSize(0, y, x, colspc_cheight);
px = (x - 3*colspc_twidth) / 5;
if(px<0) px=0;
py = colspc_border;
posR->SetSize(px, py, colspc_twidth, colspc_theight);
posG->SetSize(2*px + colspc_twidth, py, colspc_twidth, colspc_theight);
posB->SetSize(3*px + 2*colspc_twidth, py, colspc_twidth, colspc_theight);
py += colspc_border + colspc_theight;
sigR->SetSize(px, py, colspc_twidth, colspc_theight);
sigG->SetSize(2*px + colspc_twidth, py, colspc_twidth, colspc_theight);
sigB->SetSize(3*px+ 2*colspc_twidth, py, colspc_twidth, colspc_theight);
py += colspc_border + colspc_theight;
px = (x - 3*colspc_twidth)/4;
immediateUpdate->SetSize(px, py, colspc_twidth, colspc_chkheight);
drawSum->SetSize(px, py + colspc_chkheight, colspc_twidth, colspc_chkheight);
minVal->SetSize(2*px + colspc_twidth, py, colspc_twidth, colspc_theight);
maxVal->SetSize(2*px + colspc_twidth, py + colspc_theight, colspc_twidth, colspc_theight);
csType->SetSize(3*px + 2*colspc_twidth, py, colspc_chwidth, colspc_chheight);
py += colspc_border + 2*colspc_theight;
px = (x - 3*colspc_bwidth)/3;
okBut->SetSize(px/2, py, colspc_bwidth, colspc_bheight);
cancelBut->SetSize((3*px)/2 + colspc_bwidth, py, colspc_bwidth, colspc_bheight);
defaultBut->SetSize((5*px)/2 + 2*colspc_bwidth, py, colspc_bwidth, colspc_bheight);
}
void colourspaceFrame::label(void)
{
okBut->SetLabel(lman->lookup("textOK"));
cancelBut->SetLabel(lman->lookup("textCancel"));
defaultBut->SetLabel(lman->lookup("textDefaults"));
immediateUpdate->SetLabel(lman->lookup("cspaceImmUpdt"));
drawSum->SetLabel(lman->lookup("cspaceDrawSum"));
posR->SetLabel(lman->lookup("cspacePeakRed"));
posG->SetLabel(lman->lookup("cspacePeakGreen"));
posB->SetLabel(lman->lookup("cspacePeakBlue"));
sigR->SetLabel(lman->lookup("cspaceSigmaRed"));
sigG->SetLabel(lman->lookup("cspaceSigmaGreen"));
sigB->SetLabel(lman->lookup("cspaceSigmaBlue"));
minVal->SetLabel(lman->lookup("cspaceMinVal"));
maxVal->SetLabel(lman->lookup("cspaceMaxVal"));
}
void colourspaceFrame::makeUpdate(void)
{
canvas->Redraw();
if (doImmediateUpdate)
{
if (parentObj != NULL) parentObj->colourspaceChanged(&newParams);
didUpdate++;
}
}
int colourspaceFrame::process(wxObject &obj, wxEvent &evt)
{
int type = evt.GetEventType();
if (type == wxEVENT_TYPE_BUTTON_COMMAND)
{
if (&obj == (wxObject*)okBut)
{
updateSettings();
if (parentObj != NULL) parentObj->colourspaceChanged(&newParams);
Close(TRUE);
return 1;
}
else if (&obj == (wxObject*)cancelBut)
{
// return to old values, but we did updates?
if (didUpdate != 0)
{
if (parentObj != NULL) parentObj->colourspaceChanged(&origParams);
Close(TRUE);
return 1;
}
if (parentObj != NULL) parentObj->closeEditor();
return 1;
}
else if (&obj == (wxObject*)defaultBut)
{
const colourspace_params *cp = &(prefs->csp);
newParams.peak_red = cp->peak_red;
newParams.peak_green = cp->peak_green;
newParams.peak_blue = cp->peak_blue;
newParams.sigm_red = cp->sigm_red;
newParams.sigm_green = cp->sigm_green;
newParams.sigm_blue = cp->sigm_blue;
newParams.type = cp->type;
updateDisplay();
makeUpdate();
return 1;
}
}
if (type == wxEVENT_TYPE_TEXT_ENTER_COMMAND)
{
if ((&obj == (wxObject*)posR) || (&obj == (wxObject*)posG) || (&obj == (wxObject*)posB) ||
(&obj == (wxObject*)sigR) || (&obj == (wxObject*)sigG) || (&obj == (wxObject*)sigB) ||
(&obj == (wxObject*)minVal) || (&obj == (wxObject*)maxVal))
{
updateSettings();
makeUpdate();
}
}
if (type == wxEVENT_TYPE_CHECKBOX_COMMAND)
{
if (&obj == (wxObject*)immediateUpdate)
{
doImmediateUpdate = immediateUpdate->GetValue();
return 1;
}
if (&obj == (wxObject*)drawSum)
{
doDrawSum = drawSum->GetValue();
canvas->enableOutlineSum(doDrawSum);
return 1;
}
}
if (type == wxEVENT_TYPE_CHOICE_COMMAND)
{
if (&obj == (wxObject*)csType)
{
updateSettings();
canvas->setDrawingFunction();
makeUpdate();
return 1;
}
}
return 0;
}
void colourspaceFrame::updateSettings(void)
{
newParams.peak_red = atof(posR->GetValue());
newParams.peak_green = atof(posG->GetValue());
newParams.peak_blue = atof(posB->GetValue());
newParams.sigm_red = atof(sigR->GetValue());
newParams.sigm_green = atof(sigG->GetValue());
newParams.sigm_blue = atof(sigB->GetValue());
newParams.minVal = atof(minVal->GetValue());
newParams.maxVal = atof(maxVal->GetValue());
switch (csType->GetSelection())
{
case 0: newParams.type = cst_gauss; break;
case 1: newParams.type = cst_linear; break;
case 2: newParams.type = cst_rectangle; break;
case 3: newParams.type = cst_asympt; break;
default:
cerr << "Unknown colourspace type" << endl;
break;
}
}
void colourspaceFrame::updateDisplay(const colourspace_params *cp)
{
if (cp != NULL)
{
memcpy(&newParams, cp, sizeof(colourspace_params));
memcpy(&origParams, cp, sizeof(colourspace_params));
}
posR->SetValue(newParams.peak_red);
posG->SetValue(newParams.peak_green);
posB->SetValue(newParams.peak_blue);
sigR->SetValue(newParams.sigm_red);
sigG->SetValue(newParams.sigm_green);
sigB->SetValue(newParams.sigm_blue);
minVal->SetValue(newParams.minVal);
maxVal->SetValue(newParams.maxVal);
switch (newParams.type)
{
case cst_linear:
csType->SetSelection(1); break;
case cst_rectangle:
csType->SetSelection(2); break;
case cst_asympt:
csType->SetSelection(3); break;
default:
csType->SetSelection(0); break;
}
canvas->setDrawingFunction();
canvas->Redraw();
}
void colourspaceFrame::processMouseEvent(wxMouseEvent &mevt)
{
int type = mevt.GetEventType();
if ((type == wxEVENT_TYPE_LEFT_DOWN) || (type == wxEVENT_TYPE_RIGHT_DOWN))
{
float delta;
mevt.Position(&mousex, &mousey); dragColour = -1;
delta = mousex - newParams.peak_red * (canvX -2*colourspaceCanvas::colcanv_cborder) - colourspaceCanvas::colcanv_cborder;
if ((delta*delta) <= MOUSE_HOTZONE * MOUSE_HOTZONE) dragColour = 0;
else
{
delta = mousex - newParams.peak_green * (canvX - 2*colourspaceCanvas::colcanv_cborder) - colourspaceCanvas::colcanv_cborder;
if ((delta*delta) <= MOUSE_HOTZONE * MOUSE_HOTZONE) dragColour = 1;
else
{
delta = mousex - newParams.peak_blue * (canvX - 2*colourspaceCanvas::colcanv_cborder) - colourspaceCanvas::colcanv_cborder;
if ((delta*delta) <= MOUSE_HOTZONE * MOUSE_HOTZONE) dragColour = 2;
}
}
if (dragColour >= 0)
{
if (type == wxEVENT_TYPE_LEFT_DOWN)
mousebut = MOUSE_LEFT;
else
mousebut = MOUSE_RIGHT;
}
}
else if ((type == wxEVENT_TYPE_MOTION) && (mousebut != 0))
{
float newx, newy;
double *newVal=NULL;
rviewText *newText;
mevt.Position(&newx, &newy);
// left button & left/right movement: move peak position
if ((mousebut & MOUSE_LEFT) != 0)
{
switch (dragColour)
{
case 0: newVal = &newParams.peak_red; newText = posR; break;
case 1: newVal = &newParams.peak_green; newText = posG; break;
case 2: newVal = &newParams.peak_blue; newText = posB; break;
default: return;
}
*newVal = (newx - colourspaceCanvas::colcanv_cborder) / (canvX - 2*colourspaceCanvas::colcanv_cborder);
if (*newVal < 0.0) *newVal = 0.0;
if (*newVal > 1.0) *newVal = 1.0;
}
// right button & up/down movement: change variance
else if ((mousebut & MOUSE_RIGHT) != 0)
{
switch (dragColour)
{
case 0: newVal = &newParams.sigm_red; newText = sigR; break;
case 1: newVal = &newParams.sigm_green; newText = sigG; break;
case 2: newVal = &newParams.sigm_blue; newText = sigB; break;
default: return;
}
*newVal -= 0.5 * (newy - mousey) / (canvX - 2*colourspaceCanvas::colcanv_cborder);
// Must not let sigma become too small (floating exceptions)
if (*newVal < 1e-6) *newVal = 1e-6;
}
mousex = newx; mousey = newy;
if (newVal != NULL)
{
newText->SetValue(*newVal);
canvas->Redraw();
if (doImmediateUpdate)
{
if (parentObj != NULL) parentObj->colourspaceChanged(&newParams);
didUpdate++;
}
}
}
else
{
mousebut = 0;
}
}
// Threshold for lookup table size.
#define COLOURSPACE_TABLE_THRESHOLD 0x10000
/*
* Colourspace mapper class. For mapping large range integer values to RGB space
* baseType == rbt_none implies just the colourspace object without an associated MDD
*/
colourspaceMapper::colourspaceMapper(r_Ref &mdd, rviewBaseType bt, const colourspace_params *cp, bool fullrange, const r_Minterval *domain, unsigned long frange)
{
didRange = FALSE;
mddObj = mdd; baseType = bt;
IntToRGBTab15 = NULL; IntToRGBTab24 = NULL;
csFrame = NULL;
if (baseType != rbt_none)
{
objInterv = mddObj->spatial_domain(); dimMDD = objInterv.dimension();
lastInterv = objInterv;
}
// Defaults
if (cp == NULL)
{
memcpy(&par, &(prefs->csp), sizeof(colourspace_params));
}
else
{
memcpy(&par, cp, sizeof(colourspace_params));
}
par.minVal = 0.0; par.maxVal = 0.0; par.floatRange = frange;
rangeModeFull = fullrange; useInterv = domain;
scalingFactor = 1.0; // needed for FP types only
tableKind = getTableForType(baseType);
tableType = (cspaceType)-1; // initialize to some illegal value
setMappingFunctions();
processRange((rangeModeFull) ? CSPACE_RANGE_FULL : CSPACE_RANGE_ACTUAL);
//cout << "mustep " << mustep << ", sigma " << sigma << ", min " << par.minVal << ", max " << par.maxVal << endl;
}
int colourspaceMapper::getTableForType(rviewBaseType bt)
{
int tkind;
switch (bt)
{
case rbt_long:
case rbt_ulong:
case rbt_float:
case rbt_double: tkind = 1; break;
default: tkind = 0; break;
}
return tkind;
}
int colourspaceMapper::bindMapper(r_Ref &mdd, rviewBaseType bt, bool fullrange, const r_Minterval *domain, const colourspace_params *cp)
{
bool cparChanged;
if (cp == NULL)
cparChanged = 0;
else
cparChanged =
((cp->peak_red != par.peak_red) || (cp->sigm_red != par.sigm_red) ||
(cp->peak_green != par.peak_green) || (cp->sigm_green != par.sigm_green) ||
(cp->peak_blue != par.peak_blue) || (cp->sigm_blue != par.sigm_blue) ||
(cp->type != par.type));
if ((mddObj.ptr() == mdd.ptr()) && (bt == baseType) && (fullrange == rangeModeFull) && (domain == useInterv) && (cparChanged == 0))
return 0; // nothing to do
if (cparChanged)
{
colourspace_params cpar;
memcpy(&cpar, cp, sizeof(colourspace_params));
cpar.minVal = par.minVal; cpar.maxVal = par.maxVal; cpar.floatRange = par.floatRange;
colourspaceChanged(&cpar);
if (csFrame != NULL)
{
csFrame->updateDisplay(&cpar);
}
}
if ((mddObj.ptr() != mdd.ptr()) || (bt != baseType))
{
mddObj = mdd; baseType = bt; useInterv = domain; scalingFactor = 1.0;
tableKind = getTableForType(baseType);
rangeModeFull = fullrange; didRange = FALSE;
processRange((rangeModeFull) ? CSPACE_RANGE_FULL : CSPACE_RANGE_ACTUAL);
return 1;
}
if (fullrange != rangeModeFull)
{
rangeModeFull = fullrange;
processRange((rangeModeFull) ? CSPACE_RANGE_FULL : CSPACE_RANGE_ACTUAL);
return 1;
}
if ((domain != useInterv) || ((domain != NULL) && (*domain != *useInterv)) || (cparChanged != 0))
{
updateProjection(domain);
}
return 1;
}
colourspaceMapper::~colourspaceMapper(void)
{
if (IntToRGBTab15 != NULL) delete [] IntToRGBTab15;
if (IntToRGBTab24 != NULL) delete [] IntToRGBTab24;
if (csFrame != NULL)
{
csFrame->unlinkParent();
csFrame->Close(TRUE);
}
}
void colourspaceMapper::setMappingFunctions(void)
{
switch (par.type)
{
default:
cerr << "Unknown mapping function, default to gauss" << endl;
case cst_gauss:
convert15 = &colourspaceMapper::ValToGauss15; convert24 = &colourspaceMapper::ValToGauss24;
break;
case cst_linear:
convert15 = &colourspaceMapper::ValToLinear15; convert24 = &colourspaceMapper::ValToLinear24;
break;
case cst_rectangle:
convert15 = &colourspaceMapper::ValToRectangle15; convert24 = &colourspaceMapper::ValToRectangle24;
break;
case cst_asympt:
convert15 = &colourspaceMapper::ValToAsymptotic15; convert24 = &colourspaceMapper::ValToAsymptotic24;
break;
}
}
// Visual C compiler bug: with optimizations sigma is undefined when !fullrange
#ifdef __VISUALC__
#pragma optimize ("", off)
#endif
void colourspaceMapper::processRange(int rangeMode)
{
double h;
if (baseType == rbt_none) return;
if (rangeMode != CSPACE_RANGE_OLD)
{
int i;
lastInterv = (useInterv == NULL) ? objInterv : (*useInterv);
rangeModeFull = (rangeMode == CSPACE_RANGE_FULL) ? TRUE : FALSE;
// The number of projected pixels is a measure whether a lookup table pays off.
projPixels = lastInterv[0].high() - lastInterv[0].low() + 1;
for (i=1; i 0!
case rbt_float: par.minVal = (double)(-FLT_MAX); par.maxVal = (double)FLT_MAX; break;
case rbt_double: par.minVal = (double)(-DBL_MAX); par.maxVal = (double)DBL_MAX; break;
default: break;
}
}
else
{
if (!didRange)
{
int state;
::wxBeginBusyCursor();
state = mdd_objectRange(mddObj, lastInterv, realMinVal, realMaxVal);
::wxEndBusyCursor();
if (state == 0)
{
realMinVal = 0.0; realMaxVal = 1.0;
}
//cout << lastInterv << ": " << realMinVal << ", " << realMaxVal << endl;
didRange = TRUE;
}
par.minVal = realMinVal; par.maxVal = realMaxVal;
}
if (csFrame != NULL)
{
csFrame->setRange(par.minVal, par.maxVal);
}
}
if ((baseType == rbt_float) || (baseType == rbt_double))
{
// Avoid overflows; min/max could be the lowest/highest values, so the difference is NaN!
h = ((par.maxVal) / 256.0) - ((par.minVal) / 256.0);
if (h == 0.0) h = 1.0;
scalingFactor = ((par.floatRange - 1) / 256.0 ) / h;
}
h = (par.maxVal - par.minVal);
peakR = h * par.peak_red; peakG = h * par.peak_green; peakB = h * par.peak_blue;
invSigR = 1.0 / (h * par.sigm_red); invSigG = 1.0 / (h * par.sigm_green); invSigB = 1.0 / (h * par.sigm_blue);
buildCSTab15(TRUE); buildCSTab24(TRUE);
}
#ifdef __VISUALC__
#pragma optimize ("", on)
#endif
void colourspaceMapper::updateProjection(const r_Minterval *domain)
{
if (!rangeModeFull)
{
if ((domain == NULL) && (useInterv == NULL)) return;
if (domain != NULL)
{
int i;
for (i=0; i nothing to do
if (i >= dimMDD) return;
}
didRange = FALSE;
useInterv = domain;
processRange(CSPACE_RANGE_ACTUAL);
}
}
/*
* Shortcuts for defining convertor functions
*/
#define CSPACE_CONVERT(returntype, funcname, convname, rgbname) \
returntype colourspaceMapper::funcname(double value) \
{ \
unsigned int red, green, blue; \
blue = (unsigned int)(255.99*convname(value, peakB, invSigB)); \
green = (unsigned int)(255.99*convname(value, peakG, invSigG)); \
red = (unsigned int)(255.99*convname(value, peakR, invSigR)); \
return rgbname(red, green, blue); \
}
#define CSPACE_CONVERT15(funcname, convname) \
CSPACE_CONVERT(unsigned short, funcname, convname, RGBL_TO_PALETTE_SHORT)
#define CSPACE_CONVERT24(funcname, convname) \
CSPACE_CONVERT(unsigned long, funcname, convname, RGB_TO_PALETTE_LONG)
CSPACE_CONVERT15(ValToGauss15, valueToGauss)
CSPACE_CONVERT24(ValToGauss24, valueToGauss)
CSPACE_CONVERT15(ValToLinear15, valueToLinear)
CSPACE_CONVERT24(ValToLinear24, valueToLinear)
CSPACE_CONVERT15(ValToRectangle15, valueToRectangle)
CSPACE_CONVERT24(ValToRectangle24, valueToRectangle)
CSPACE_CONVERT15(ValToAsymptotic15, valueToAsymptotic)
CSPACE_CONVERT24(ValToAsymptotic24, valueToAsymptotic)
unsigned short *colourspaceMapper::buildCSTab15(bool forceRebuild)
{
int i, j;
double val;
// Don't build a 15bpp table if it's not needed
if (tableKind != 0) return NULL;
if (IntToRGBTab15 != NULL)
{
if ((!forceRebuild) && (tableType == par.type)) return IntToRGBTab15;
delete [] IntToRGBTab15; IntToRGBTab15 = NULL;
}
// The code always assumes a table for this depth!
//if ((par.maxVal - par.minVal + 1) > (double)COLOURSPACE_TABLE_THRESHOLD) return NULL;
j = (int)((par.maxVal - par.minVal) * scalingFactor) + 1;
IntToRGBTab15 = new unsigned short[j];
for (i=0, val=0.0; i COLOURSPACE_TABLE_THRESHOLD) return NULL;
// If we use the range of the current projection and there are fewer visible pixels than
// table entries then don't build a table (or we'd sacrifice a lot of speed)
if ((!rangeModeFull) && (useInterv != NULL) && (j > projPixels)) return NULL;
invScale = (scalingFactor == 0.0) ? 1.0 : 1/scalingFactor;
IntToRGBTab24 = new unsigned long[j];
for (i=0, val=0.0; iupdateDisplay(newParams);
if (autoUpdate)
{
// Notify whoever owns this object that its settings have changed (can't have one
// specific type of parent because it can be owned by images, thumbnails, prefs, ...)
ue.type = usr_cspace_changed;
ue.data = (void*)this;
//parentFrame->userEvent(ue);
if (frameManager != NULL)
frameManager->broadcastUserEvent(ue);
}
}
void colourspaceMapper::openEditor(void)
{
if (csFrame != NULL) return;
csFrame = new colourspaceFrame(this, &par);
}
void colourspaceMapper::closeEditor(bool activeClose)
{
if (activeClose) csFrame->Close(TRUE);
csFrame = NULL;
}
void colourspaceMapper::getParameters(colourspace_params *dest)
{
if (csFrame != NULL)
{
csFrame->updateSettings();
}
memcpy(dest, &par, sizeof(colourspace_params));
}