Files
rawTherapee/rtgui/histogrampanel.cc
ffsup2 eef14f76dd Added dark frame subtraction
Moved debayer and preprocessing parameters to class ProcParams for every single image.
Added tab RAW for changing those parameters.
Progress bar shows only load step (work to do)
2010-08-19 00:37:53 +02:00

475 lines
14 KiB
C++

/*
* This file is part of RawTherapee.
*
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
*
* RawTherapee 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.
*
* RawTherapee 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 RawTherapee. If not, see <http://www.gnu.org/licenses/>.
*/
#include <histogrampanel.h>
#include <multilangmgr.h>
#include <string.h>
HistogramPanel::HistogramPanel () {
histogramArea = Gtk::manage (new HistogramArea ());
showRed = Gtk::manage (new Gtk::ToggleButton (M("HISTOGRAM_BUTTON_R")));
showGreen = Gtk::manage (new Gtk::ToggleButton (M("HISTOGRAM_BUTTON_G")));
showBlue = Gtk::manage (new Gtk::ToggleButton (M("HISTOGRAM_BUTTON_B")));
showValue = Gtk::manage (new Gtk::ToggleButton (M("HISTOGRAM_BUTTON_L")));
Gtk::VBox* vbox = Gtk::manage (new Gtk::VBox (false, 0));
showRed->set_active (true);
showGreen->set_active (true);
showBlue->set_active (true);
showValue->set_active (true);
vbox->pack_start (*showRed, Gtk::PACK_SHRINK, 2);
vbox->pack_start (*showGreen, Gtk::PACK_SHRINK, 2);
vbox->pack_start (*showBlue, Gtk::PACK_SHRINK, 2);
vbox->pack_start (*showValue, Gtk::PACK_SHRINK, 2);
pack_start (*histogramArea);
pack_end (*vbox, Gtk::PACK_SHRINK, 2);
showRed->signal_toggled().connect( sigc::mem_fun(*this, &HistogramPanel::rgbv_toggled) );
showGreen->signal_toggled().connect( sigc::mem_fun(*this, &HistogramPanel::rgbv_toggled) );
showBlue->signal_toggled().connect( sigc::mem_fun(*this, &HistogramPanel::rgbv_toggled) );
showValue->signal_toggled().connect( sigc::mem_fun(*this, &HistogramPanel::rgbv_toggled) );
show_all ();
showRed->set_tooltip_text (M("HISTOGRAM_TOOLTIP_R"));
showGreen->set_tooltip_text (M("HISTOGRAM_TOOLTIP_G"));
showBlue->set_tooltip_text (M("HISTOGRAM_TOOLTIP_B"));
showValue->set_tooltip_text (M("HISTOGRAM_TOOLTIP_L"));
rconn = signal_size_allocate().connect( sigc::mem_fun(*this, &HistogramPanel::resized) );
}
void HistogramPanel::resized (Gtk::Allocation& req) {
rconn.block (true);
if (req.get_width()/2>150)
set_size_request (req.get_width(), 150);
else
set_size_request (req.get_width(), req.get_width()/2);
rconn.block (false);
histogramArea->renderHistogram ();
histogramArea->queue_draw ();
}
void HistogramPanel::rgbv_toggled () {
histogramArea->updateOptions (showRed->get_active(), showGreen->get_active(), showBlue->get_active(), showValue->get_active());
histogramArea->queue_draw ();
}
HistogramArea::HistogramArea () :
needVal(true), needRed(true), needGreen(true), needBlue(true), oldwidth(-1), valid(false), showFull(true) {
haih = new HistogramAreaIdleHelper;
haih->harea = this;
haih->destroyed = false;
haih->pending = 0;
signal_style_changed().connect( sigc::mem_fun(*this, &HistogramArea::styleChanged) );
}
HistogramArea::~HistogramArea () {
if (haih->pending)
haih->destroyed = true;
else
delete haih;
}
void HistogramArea::updateOptions (bool r, bool g, bool b, bool v) {
needRed = r;
needGreen = g;
needBlue = b;
needVal = v;
renderHistogram ();
}
int histupdate (void* data) {
gdk_threads_enter ();
HistogramAreaIdleHelper* haih = (HistogramAreaIdleHelper*)data;
if (haih->destroyed) {
if (haih->pending == 1)
delete haih;
else
haih->pending--;
gdk_threads_leave ();
return 0;
}
haih->harea->renderHistogram ();
haih->harea->queue_draw ();
haih->pending--;
gdk_threads_leave ();
return 0;
}
void HistogramArea::update (unsigned int* rh, unsigned int* gh, unsigned int* bh, unsigned int* lh) {
if (rh!=NULL) {
memcpy (lhist, lh, 256*sizeof(unsigned int));
memcpy (rhist, rh, 256*sizeof(unsigned int));
memcpy (ghist, gh, 256*sizeof(unsigned int));
memcpy (bhist, bh, 256*sizeof(unsigned int));
valid = true;
}
else
valid = false;
haih->pending++;
g_idle_add (histupdate, haih);
}
void HistogramArea::renderHistogram () {
if (!is_realized ())
return;
Glib::RefPtr<Gdk::Window> window = get_window();
int winx, winy, winw, winh, wind;
window->get_geometry(winx, winy, winw, winh, wind);
backBuffer = Gdk::Pixmap::create (window, winw, winh, -1);
Glib::RefPtr<Gdk::GC> bgc = Gdk::GC::create(backBuffer);
bgc->set_foreground (white);
backBuffer->draw_rectangle (bgc, true, 0, 0, winw, winh);
if (valid) {
// compute height of the full histogram (realheight) and
// does not take into account 0 and 255 values
// them are handled separately
int fullhistheight = 0;
for (int i=1; i<255; i++) {
if (needVal && lhist[i]>fullhistheight)
fullhistheight = lhist[i];
if (needRed && rhist[i]>fullhistheight)
fullhistheight = rhist[i];
if (needGreen && ghist[i]>fullhistheight)
fullhistheight = ghist[i];
if (needBlue && bhist[i]>fullhistheight)
fullhistheight = bhist[i];
}
int realhistheight = fullhistheight;
if (!showFull) {
int area1thres = 0;
int area2thres = 0;
int area = 0;
for (int i=0; i<fullhistheight; i++) {
for (int j=0; j<256; j++)
if ((needVal && lhist[j]>i) || (needRed && rhist[j]>i) || (needGreen && ghist[j]>i) || (needBlue && bhist[j]>i))
area++;
if (area1thres==0 && (double)area / (256*(i+1)) < 0.3)
area1thres = i;
if (area2thres==0 && (double)area / (256*(i+1)) < 0.3)
area2thres = i;
if (area1thres && area2thres)
break;
}
if (area1thres>0 && area2thres>0 && area1thres<fullhistheight)
realhistheight = area2thres;
}
if (realhistheight<winh-2)
realhistheight = winh-2;
Cairo::RefPtr<Cairo::Context> cr = backBuffer->create_cairo_context();
cr->set_antialias (Cairo::ANTIALIAS_SUBPIXEL);
cr->set_line_width (1.0);
int ui = 0, oi = 0;
if (needVal) {
drawCurve(cr, lhist, realhistheight, winw, winh);
cr->set_source_rgb (0.75, 0.75, 0.75);
cr->fill_preserve ();
cr->set_source_rgb (0.5, 0.5, 0.5);
cr->stroke ();
drawMarks(cr, lhist, realhistheight, winw, ui, oi);
}
if (needRed) {
drawCurve(cr, rhist, realhistheight, winw, winh);
cr->set_source_rgb (1.0, 0.0, 0.0);
cr->stroke ();
drawMarks(cr, rhist, realhistheight, winw, ui, oi);
}
if (needGreen) {
drawCurve(cr, ghist, realhistheight, winw, winh);
cr->set_source_rgb (0.0, 1.0, 0.0);
cr->stroke ();
drawMarks(cr, ghist, realhistheight, winw, ui, oi);
}
if (needBlue) {
drawCurve(cr, bhist, realhistheight, winw, winh);
cr->set_source_rgb (0.0, 0.0, 1.0);
cr->stroke ();
drawMarks(cr, bhist, realhistheight, winw, ui, oi);
}
}
/*
// scale histogram to width winw-1
int* vval = new int[winw-1];
int* vred = new int[winw-1];
int* vgreen = new int[winw-1];
int* vblue = new int[winw-1];
memset (vval, 0, sizeof(int)*(winw-1));
memset (vred, 0, sizeof(int)*(winw-1));
memset (vgreen, 0, sizeof(int)*(winw-1));
memset (vblue, 0, sizeof(int)*(winw-1));
int index = 0;
double scale = 256.0 / (winw-2);
for (int i=0; i<=winw-2; i++) {
int samples = 0;
while (index < 256 && (int)(index/scale) == i) {
vval[i] += lhist[index];
vred[i] += rhist[index];
vgreen[i] += ghist[index];
vblue[i] += bhist[index];
index++;
samples++;
}
if (samples>0) {
vval[i] /= samples;
vred[i] /= samples;
vgreen[i] /= samples;
vblue[i] /= samples;
}
}
// compute height of the full histogram (realheight) and
int fullhistheight = 0;
for (int i=0; i<=winw-2; i++) {
if (needVal && vval[i]>fullhistheight)
fullhistheight = vval[i];
if (needRed && vred[i]>fullhistheight)
fullhistheight = vred[i];
if (needGreen && vgreen[i]>fullhistheight)
fullhistheight = vgreen[i];
if (needBlue && vblue[i]>fullhistheight)
fullhistheight = vblue[i];
}
// compute two hights, one for the magnified view and one for the threshold
int realhistheight = fullhistheight;
if (!showFull) {
int area1thres = 0;
int area2thres = 0;
int area = 0;
for (int i=0; i<fullhistheight; i++) {
for (int j=0; j<winw-1; j++)
if ((needVal && vval[j]>i) || (needRed && vred[j]>i) || (needGreen && vgreen[j]>i) || (needBlue && vblue[j]>i))
area++;
if (area1thres==0 && (double)area / ((winw-1)*(i+1)) < 0.3)
area1thres = i;
if (area2thres==0 && (double)area / ((winw-1)*(i+1)) < 0.3)
area2thres = i;
if (area1thres && area2thres)
break;
}
if (area1thres>0 && area2thres>0 && area1thres<fullhistheight)
realhistheight = area2thres;
}
if (realhistheight<winh-2)
realhistheight = winh-2;
Cairo::RefPtr<Cairo::Context> cr = backBuffer->create_cairo_context();
cr->set_antialias (Cairo::ANTIALIAS_SUBPIXEL);
cr->set_line_width (1.0);
if (needVal) {
cr->move_to (0, winh-1);
cr->set_source_rgb (0.75, 0.75, 0.75);
for (int i=0; i<=winw-2; i++) {
int val = (int)(vval[i] * (double)(winh-2) / realhistheight);
if (val>winh-1)
val = winh-1;
if (i>0)
cr->line_to (i+1, winh-1-val);
}
cr->fill_preserve ();
cr->set_source_rgb (0.5, 0.5, 0.5);
cr->stroke ();
}
if (needRed) {
cr->move_to (0, winh-1);
cr->set_source_rgb (1.0, 0.0, 0.0);
for (int i=0; i<=winw-2; i++) {
int val = (int)(vred[i] * (double)(winh-2) / realhistheight);
if (val>winh-1)
val = winh-1;
if (i>0)
cr->line_to (i+1, winh-1-val);
}
cr->stroke ();
}
if (needGreen) {
cr->move_to (0, winh-1);
cr->set_source_rgb (0.0, 1.0, 0.0);
for (int i=0; i<=winw-2; i++) {
int val = (int)(vgreen[i] * (double)(winh-2) / realhistheight);
if (val>winh-1)
val = winh-1;
if (i>0)
cr->line_to (i+1, winh-1-val);
}
cr->stroke ();
}
if (needBlue) {
cr->move_to (0, winh-1);
cr->set_source_rgb (0.0, 0.0, 1.0);
for (int i=0; i<=winw-2; i++) {
int val = (int)(vblue[i] * (double)(winh-2) / realhistheight);
if (val>winh-1)
val = winh-1;
if (i>0)
cr->line_to (i+1, winh-1-val);
}
cr->stroke ();
}
delete [] vval;
delete [] vred;
delete [] vgreen;
delete [] vblue;
}
*/
bgc->set_foreground (mgray);
backBuffer->draw_rectangle (bgc, false, 0, 0, winw-1, winh-1);
bgc->set_line_attributes (1, Gdk::LINE_ON_OFF_DASH, Gdk::CAP_NOT_LAST, Gdk::JOIN_MITER);
backBuffer->draw_line (bgc, winw/3, 0, winw/3, winh-1);
backBuffer->draw_line (bgc, 2*winw/3, 0, 2*winw/3, winh-1);
backBuffer->draw_line (bgc, 0, winh/3, winw-1, winh/3);
backBuffer->draw_line (bgc, 0, 2*winh/3, winw-1, 2*winh/3);
bgc->set_line_attributes (1, Gdk::LINE_SOLID, Gdk::CAP_NOT_LAST, Gdk::JOIN_MITER);
oldwidth = winw;
oldheight = winh;
}
void HistogramArea::on_realize () {
Gtk::DrawingArea::on_realize();
Glib::RefPtr<Gdk::Window> window = get_window();
gc_ = Gdk::GC::create(window);
add_events(Gdk::EXPOSURE_MASK | Gdk::BUTTON_PRESS_MASK);
Glib::RefPtr<Gdk::Colormap> colormap = get_default_colormap();
black = Gdk::Color ("black");
red = Gdk::Color ("red");
green = Gdk::Color ("green");
blue = Gdk::Color ("blue");
lgray = Gdk::Color ("gray75");
mgray = Gdk::Color ("gray50");
dgray = Gdk::Color ("gray25");
colormap->alloc_color(black);
colormap->alloc_color(white);
colormap->alloc_color(red);
colormap->alloc_color(green);
colormap->alloc_color(blue);
colormap->alloc_color(lgray);
colormap->alloc_color(mgray);
colormap->alloc_color(dgray);
}
void HistogramArea::drawCurve(Cairo::RefPtr<Cairo::Context> &cr,
unsigned int * data, double scale, int hsize, int vsize)
{
cr->move_to (0, vsize-1);
for (int i = 0; i < 256; i++) {
double val = data[i] * (double)(vsize-2) / scale;
if (val > vsize - 1)
val = vsize - 1;
cr->line_to ((i/255.0)*(hsize - 1), vsize - 1 - val);
}
cr->line_to (hsize - 1, vsize - 1);
}
void HistogramArea::drawMarks(Cairo::RefPtr<Cairo::Context> &cr,
unsigned int * data, double scale, int hsize, int & ui, int & oi)
{
int s = 8;
if(data[0] > scale) {
cr->rectangle(0, (ui++)*s, s, s);
}
if(data[255] > scale) {
cr->rectangle(hsize - s, (oi++)*s, s, s);
}
cr->fill();
}
void HistogramArea::styleChanged (const Glib::RefPtr<Gtk::Style>& style) {
white = get_style()->get_base(Gtk::STATE_NORMAL);
queue_draw ();
}
bool HistogramArea::on_expose_event(GdkEventExpose* event) {
Glib::RefPtr<Gdk::Window> window = get_window();
int winx, winy, winw, winh, wind;
window->get_geometry(winx, winy, winw, winh, wind);
if (winw!=oldwidth && winh!=oldheight)
renderHistogram ();
window->draw_drawable (gc_, backBuffer, 0, 0, 0, 0, -1, -1);
return true;
}
bool HistogramArea::on_button_press_event (GdkEventButton* event) {
if (event->type==GDK_2BUTTON_PRESS && event->button==1) {
showFull = !showFull;
renderHistogram ();
queue_draw ();
}
return true;
}