krita

kcurve.cc

00001 /* ============================================================ 
00002  * Copyright 2004-2005 by Gilles Caulier
00003  * Copyright 2005 by Casper Boemann (reworked to be generic)
00004  *
00005  * This program is free software; you can redistribute it
00006  * and/or modify it under the terms of the GNU General
00007  * Public License as published by the Free Software Foundation;
00008  * either version 2, or (at your option)
00009  * any later version.
00010  * 
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  * 
00016  * ============================================================ */
00017  
00018 // C++ includes.
00019 
00020 #include <cmath>
00021 #include <cstdlib>
00022 
00023 // Qt includes.
00024 
00025 #include <qpixmap.h>
00026 #include <qpainter.h>
00027 #include <qpoint.h>
00028 #include <qpen.h>
00029 #include <qevent.h>
00030 #include <qtimer.h>
00031 #include <qrect.h> 
00032 #include <qfont.h> 
00033 #include <qfontmetrics.h> 
00034 
00035 // KDE includes.
00036 
00037 #include <kdebug.h>
00038 #include <kcursor.h>
00039 #include <klocale.h>
00040 
00041 // Local includes.
00042 
00043 #include "kcurve.h"
00044 
00045 KCurve::KCurve(QWidget *parent, const char *name, WFlags f)
00046             : QWidget(parent, name, f)
00047 {
00048     m_grab_point     = NULL;
00049     m_readOnlyMode   = false;
00050     m_guideVisible   = false;
00051     m_dragging = false;
00052     m_pix = NULL;
00053     
00054     setMouseTracking(true);
00055     setPaletteBackgroundColor(Qt::NoBackground);
00056     setMinimumSize(150, 50);
00057     QPair<double,double> *p = new QPair<double,double>;
00058     p->first = 0.0; p->second=0.0;
00059     m_points.append(p);
00060     p = new QPair<double,double>;
00061     p->first = 1.0; p->second=1.0;
00062     m_points.append(p);
00063     m_points.setAutoDelete(true);
00064     setFocusPolicy(QWidget::StrongFocus);
00065 }
00066 
00067 KCurve::~KCurve()
00068 {
00069     if (m_pix) delete m_pix;
00070 }
00071 
00072 void KCurve::reset(void)
00073 {
00074     m_grab_point   = NULL;    
00075     m_guideVisible = false;
00076     repaint(false);
00077 }
00078 
00079 void KCurve::setCurveGuide(QColor color)
00080 {
00081     m_guideVisible = true;
00082     m_colorGuide   = color;
00083     repaint(false);
00084 }
00085 
00086 void KCurve::setPixmap(QPixmap pix)
00087 {
00088     if (m_pix) delete m_pix;
00089     m_pix = new QPixmap(pix);
00090     repaint(false);
00091 }
00092 
00093 void KCurve::keyPressEvent(QKeyEvent *e)
00094 {
00095     if(e->key() == Qt::Key_Delete || e->key() == Qt::Key_Backspace)
00096     {
00097         QPair<double,double> *closest_point=NULL;
00098         if(m_grab_point)
00099         {
00100             //first find closest point to get focus afterwards
00101             QPair<double,double> *p = m_points.first();
00102             double distance = 1000; // just a big number
00103             while(p)
00104             {
00105                 if(p!=m_grab_point)
00106                 if (fabs (m_grab_point->first - p->first) < distance)
00107                 {
00108                     distance = fabs(m_grab_point->first - p->first);
00109                     closest_point = p;
00110                 }
00111                 p = m_points.next();
00112             }
00113             m_points.remove(m_grab_point);
00114         }
00115         m_grab_point = closest_point;
00116         repaint(false);
00117     }
00118     else
00119         QWidget::keyPressEvent(e);
00120 }
00121 
00122 void KCurve::paintEvent(QPaintEvent *)
00123 {
00124     int    x, y;
00125     int    wWidth = width();
00126     int    wHeight = height();
00127     
00128     x  = 0; 
00129     y  = 0;
00130     
00131     // Drawing selection or all histogram values.
00132     // A QPixmap is used for enable the double buffering.
00133     
00134     QPixmap pm(size());
00135     QPainter p1;
00136     p1.begin(&pm, this);
00137     
00138     //  draw background
00139     if(m_pix)
00140     {
00141         p1.scale(1.0*wWidth/m_pix->width(), 1.0*wHeight/m_pix->height());
00142         p1.drawPixmap(0, 0, *m_pix);
00143         p1.resetXForm();
00144     }
00145     else
00146         pm.fill();
00147     
00148     // Draw grid separators.
00149     p1.setPen(QPen::QPen(Qt::gray, 1, Qt::SolidLine));
00150     p1.drawLine(wWidth/3, 0, wWidth/3, wHeight);                 
00151     p1.drawLine(2*wWidth/3, 0, 2*wWidth/3, wHeight);                 
00152     p1.drawLine(0, wHeight/3, wWidth, wHeight/3);                 
00153     p1.drawLine(0, 2*wHeight/3, wWidth, 2*wHeight/3);     
00154 
00155     // Draw curve.
00156     double curvePrevVal = getCurveValue(0.0);
00157     p1.setPen(QPen::QPen(Qt::black, 1, Qt::SolidLine));    
00158     for (x = 0 ; x < wWidth ; x++)
00159     {
00160         double curveX;
00161         double curveVal;
00162         
00163         curveX = (x + 0.5) / wWidth;
00164         
00165         curveVal = getCurveValue(curveX);
00166         
00167         p1.drawLine(x - 1, wHeight - int(curvePrevVal * wHeight),
00168             x,     wHeight - int(curveVal * wHeight));                             
00169         
00170         curvePrevVal = curveVal;
00171     }
00172     p1.drawLine(x - 1, wHeight - int(curvePrevVal * wHeight),
00173         x,     wHeight - int(getCurveValue(1.0) * wHeight));                             
00174     
00175     // Drawing curve handles.
00176     if ( !m_readOnlyMode )
00177     {
00178         QPair<double,double> *p = m_points.first();
00179         
00180         while(p)
00181         {
00182             double curveX = p->first;
00183             double curveY = p->second;
00184         
00185             if(p == m_grab_point)
00186             {
00187                 p1.setPen(QPen::QPen(Qt::red, 3, Qt::SolidLine));
00188                 p1.drawEllipse( int(curveX * wWidth) - 2, 
00189                     wHeight - 2 - int(curveY * wHeight), 4, 4 ); 
00190             }
00191             else
00192             {
00193                 p1.setPen(QPen::QPen(Qt::red, 1, Qt::SolidLine));
00194             
00195                 p1.drawEllipse( int(curveX * wWidth) - 3, 
00196                     wHeight - 3 - int(curveY * wHeight), 6, 6 ); 
00197             }
00198             
00199             p = m_points.next();
00200         }
00201     }
00202     
00203     p1.end();
00204     bitBlt(this, 0, 0, &pm);
00205 }
00206 
00207 void KCurve::mousePressEvent ( QMouseEvent * e )
00208 {
00209     if (m_readOnlyMode) return;
00210     
00211     QPair<double,double> *closest_point=NULL;
00212     double distance;
00213     
00214     if (e->button() != Qt::LeftButton)
00215         return;
00216     
00217     double x = e->pos().x() / (float)width();
00218     double y = 1.0 - e->pos().y() / (float)height();
00219 
00220     distance = 1000; // just a big number
00221 
00222     QPair<double,double> *p = m_points.first();
00223     int insert_pos,pos=0;
00224     while(p)
00225     {
00226         if (fabs (x - p->first) < distance)
00227         {
00228             distance = fabs(x - p->first);
00229             closest_point = p;
00230             if(x < p->first)
00231                 insert_pos = pos;
00232             else
00233                 insert_pos = pos + 1;
00234         }
00235         p = m_points.next();
00236         pos++;
00237     }
00238 
00239 
00240     if(closest_point == NULL)
00241     {
00242         closest_point = new QPair<double,double>;
00243         closest_point->first = x;
00244         closest_point->second = y;
00245         m_points.append(closest_point);
00246     }
00247     else if(distance * width() > 5)
00248     {
00249         closest_point = new QPair<double,double>;
00250         closest_point->first = x;
00251         closest_point->second = y;
00252         m_points.insert(insert_pos, closest_point);
00253     }
00254     else
00255         if(fabs(y - closest_point->second) * width() > 5)
00256             return;
00257     
00258     
00259     m_grab_point = closest_point;
00260     m_grabOffsetX = m_grab_point->first - x;
00261     m_grabOffsetY = m_grab_point->second - y;
00262     m_grab_point->first = x + m_grabOffsetX;
00263     m_grab_point->second = y + m_grabOffsetY;
00264     m_dragging = true;
00265 
00266     setCursor( KCursor::crossCursor() );
00267     
00268     // Determine the leftmost and rightmost points.
00269     m_leftmost = 0;
00270     m_rightmost = 1;
00271     
00272     p = m_points.first();
00273     while(p)
00274     {
00275         if (p != m_grab_point)
00276         {
00277             if(p->first> m_leftmost && p->first < x)
00278                 m_leftmost = p->first;
00279             if(p->first < m_rightmost && p->first > x)
00280                 m_rightmost = p->first;
00281         }
00282         p = m_points.next();
00283     }
00284     repaint(false);
00285 }
00286 
00287 void KCurve::mouseReleaseEvent ( QMouseEvent * e )
00288 {
00289     if (m_readOnlyMode) return;
00290     
00291     if (e->button() != Qt::LeftButton)
00292         return;
00293     
00294     setCursor( KCursor::arrowCursor() );    
00295     m_dragging = false;
00296     repaint(false);
00297     emit modified();
00298 }
00299 
00300 void KCurve::mouseMoveEvent ( QMouseEvent * e )
00301 {
00302     if (m_readOnlyMode) return;
00303 
00304     double x = e->pos().x() / (float)width();
00305     double y = 1.0 - e->pos().y() / (float)height();
00306     
00307     if (m_dragging == false)   // If no point is selected set the the cursor shape if on top
00308     {
00309         double distance = 1000;
00310         double ydistance = 1000;
00311         QPair<double,double> *p = m_points.first();
00312         while(p)
00313         {
00314             if (fabs (x - p->first) < distance)
00315             {
00316                 distance = fabs(x - p->first);
00317                 ydistance = fabs(y - p->second);
00318             }
00319             p = m_points.next();
00320         }
00321     
00322         if (distance * width() > 5 || ydistance * height() > 5)
00323             setCursor( KCursor::arrowCursor() );    
00324         else
00325             setCursor( KCursor::crossCursor() );
00326     }
00327     else  // Else, drag the selected point
00328     {
00329         setCursor( KCursor::crossCursor() );
00330         
00331         x += m_grabOffsetX;
00332         y += m_grabOffsetY;
00333         
00334         if (x <= m_leftmost)
00335             x = m_leftmost + 1E-4; // the addition so we can grab the dot later.
00336             
00337         if(x >= m_rightmost)
00338             x = m_rightmost - 1E-4;
00339         
00340         if(y > 1.0)
00341             y = 1.0;
00342             
00343         if(y < 0.0)
00344             y = 0.0;
00345             
00346         m_grab_point->first = x;
00347         m_grab_point->second = y;
00348         
00349         emit modified();
00350     }
00351         
00352     repaint(false);
00353 }
00354 
00355 double KCurve::getCurveValue(double x)
00356 {
00357     return getCurveValue(m_points, x);
00358 }
00359 
00360 double KCurve::getCurveValue(QPtrList<QPair<double,double> > &curve, double x)
00361 {
00362     double t;
00363     QPair<double,double> *p;
00364     QPair<double,double> *p0,*p1,*p2,*p3;
00365     double c0,c1,c2,c3;
00366     double val;
00367     
00368     if(curve.count() == 0)
00369         return 0.5;
00370     
00371     // First find curve segment
00372     p = curve.first();
00373     if(x < p->first)
00374         return p->second;
00375         
00376     p = curve.last();
00377     if(x >= p->first)
00378         return p->second;
00379     
00380     // Find the four control points (two on each side of x)    
00381     p = curve.first();
00382     while(x >= p->first)
00383     {
00384         p = curve.next();
00385     }
00386     curve.prev();
00387     
00388     if((p0 = curve.prev()) == NULL)
00389         p1 = p0 = curve.first();
00390     else
00391         p1 = curve.next();
00392     
00393     p2 = curve.next();
00394     if( (p = curve.next()) )
00395         p3 = p;
00396     else
00397         p3 = p2;
00398     
00399     // Calculate the value
00400     t = (x - p1->first) / (p2->first - p1->first);
00401     c2 = (p2->second - p0->second) * (p2->first-p1->first) / (p2->first-p0->first);
00402     c3 = p1->second;
00403     c0 = -2*p2->second + 2*c3 + c2 + (p3->second - p1->second) * (p2->first - p1->first) / (p3->first - p1->first);
00404     c1 = p2->second - c3 - c2 - c0;
00405     val = ((c0*t + c1)*t + c2)*t + c3;
00406     
00407     if(val < 0.0)
00408         val = 0.0;
00409     if(val > 1.0)
00410         val = 1.0;
00411     return val;
00412 }
00413 
00414 QPtrList<QPair<double,double> > KCurve::getCurve()
00415 {
00416     QPtrList<QPair<double,double> > outlist;
00417     QPair<double,double> *p;
00418     QPair<double,double> *outpoint;
00419 
00420     p = m_points.first();
00421     while(p)
00422     {
00423         outpoint = new QPair<double,double>(p->first, p->second);
00424         outlist.append(outpoint);
00425         p = m_points.next();
00426     }
00427     return outlist;
00428 }
00429 
00430 void KCurve::setCurve(QPtrList<QPair<double,double> >inlist)
00431 {
00432     QPair<double,double> *p;
00433     QPair<double,double> *inpoint;
00434 
00435     m_points.clear();
00436 
00437     inpoint = inlist.first();
00438     while(inpoint)
00439     {
00440         p = new QPair<double,double>(inpoint->first, inpoint->second);
00441         m_points.append(p);
00442         inpoint = inlist.next();
00443     }
00444 }
00445 
00446 void KCurve::leaveEvent( QEvent * )
00447 {
00448 }
00449 
00450 #include "kcurve.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys