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)
475 lines
14 KiB
C++
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;
|
|
}
|
|
|