krita

kis_gradient.cc

00001 /*
00002  *  kis_gradient.cc - part of Krayon
00003  *
00004  *  Copyright (c) 2000 Matthias Elter <elter@kde.org>
00005  *                2001 John Califf
00006  *                2004 Boudewijn Rempt <boud@valdyas.org>
00007  *                2004 Adrian Page <adrian@pagenet.plus.com>
00008  *                2004 Sven Langkamp <longamp@reallygood.de>
00009  *
00010  *  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version.
00014  *
00015  *  This program is distributed in the hope that it will be useful,
00016  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  *  GNU General Public License for more details.
00019  *
00020  *  You should have received a copy of the GNU General Public License
00021  *  along with this program; if not, write to the Free Software
00022  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00023  */
00024 
00025 #include <cfloat>
00026 #include <cmath>
00027 
00028 #include <qimage.h>
00029 #include <qtextstream.h>
00030 #include <qfile.h>
00031 
00032 #include <koColor.h>
00033 #include <kogradientmanager.h>
00034 
00035 #include <kdebug.h>
00036 #include <klocale.h>
00037 
00038 #include "kis_gradient.h"
00039 
00040 #define PREVIEW_WIDTH 64
00041 #define PREVIEW_HEIGHT 64
00042 
00043 KisGradientSegment::RGBColorInterpolationStrategy *KisGradientSegment::RGBColorInterpolationStrategy::m_instance = 0;
00044 KisGradientSegment::HSVCWColorInterpolationStrategy *KisGradientSegment::HSVCWColorInterpolationStrategy::m_instance = 0;
00045 KisGradientSegment::HSVCCWColorInterpolationStrategy *KisGradientSegment::HSVCCWColorInterpolationStrategy::m_instance = 0;
00046 
00047 KisGradientSegment::LinearInterpolationStrategy *KisGradientSegment::LinearInterpolationStrategy::m_instance = 0;
00048 KisGradientSegment::CurvedInterpolationStrategy *KisGradientSegment::CurvedInterpolationStrategy::m_instance = 0;
00049 KisGradientSegment::SineInterpolationStrategy *KisGradientSegment::SineInterpolationStrategy::m_instance = 0;
00050 KisGradientSegment::SphereIncreasingInterpolationStrategy *KisGradientSegment::SphereIncreasingInterpolationStrategy::m_instance = 0;
00051 KisGradientSegment::SphereDecreasingInterpolationStrategy *KisGradientSegment::SphereDecreasingInterpolationStrategy::m_instance = 0;
00052 
00053 KisGradient::KisGradient(const QString& file) : super(file)
00054 {
00055 }
00056 
00057 KisGradient::~KisGradient()
00058 {
00059     for (uint i = 0; i < m_segments.count(); i++) {
00060         delete m_segments[i];
00061         m_segments[i] = 0;
00062     }
00063 }
00064 
00065 bool KisGradient::load()
00066 {
00067     return init();
00068 }
00069 
00070 bool KisGradient::save()
00071 {
00072     return false;
00073 }
00074 
00075 QImage KisGradient::img()
00076 {
00077     return m_img;
00078 }
00079 
00080 bool KisGradient::init()
00081 {
00082     KoGradientManager gradLoader;
00083     KoGradient* grad = gradLoader.loadGradient(filename());
00084 
00085     if( !grad )
00086         return false;
00087 
00088     m_segments.clear();
00089 
00090     if( grad->colorStops.count() > 1 ) {
00091         KoColorStop *colstop;
00092         for(colstop = grad->colorStops.first(); colstop; colstop = grad->colorStops.next()) {
00093             KoColorStop *colstopNext = grad->colorStops.next();
00094 
00095             if(colstopNext) {
00096                 KoColor leftRgb((int)(colstop->color1 * 255 + 0.5), (int)(colstop->color2 * 255 + 0.5), (int)(colstop->color3 * 255 + 0.5));
00097                 KoColor rightRgb((int)(colstopNext->color1 * 255 + 0.5), (int)(colstopNext->color2 * 255 + 0.5), (int)(colstopNext->color3 * 255 + 0.5));
00098 
00099                 double midp = colstop->midpoint;
00100                 midp = colstop->offset + ((colstopNext->offset - colstop->offset) * midp);
00101 
00102                 Color leftColor(leftRgb.color(), colstop->opacity);
00103                 Color rightColor(rightRgb.color(), colstopNext->opacity);
00104 
00105                 KisGradientSegment *segment = new KisGradientSegment(colstop->interpolation, colstop->colorType, colstop->offset, midp, colstopNext->offset, leftColor, rightColor);
00106                 Q_CHECK_PTR(segment);
00107 
00108                 if ( !segment->isValid() ) {
00109                     delete segment;
00110                     return false;
00111                 }
00112 
00113                 m_segments.push_back(segment);
00114                 grad->colorStops.prev();
00115             }
00116             else {
00117                 grad->colorStops.prev();
00118                 break;
00119             }
00120         }
00121     }
00122     else
00123         return false;
00124 
00125     if (!m_segments.isEmpty()) {
00126         m_img = generatePreview(PREVIEW_WIDTH, PREVIEW_HEIGHT);
00127         setValid(true);
00128         return true;
00129     }
00130     else {
00131         return false;
00132     }
00133 }
00134 
00135 void KisGradient::setImage(const QImage& img)
00136 {
00137     m_img = img;
00138     m_img.detach();
00139 
00140     setValid(true);
00141 }
00142 
00143 KisGradientSegment *KisGradient::segmentAt(double t) const
00144 {
00145     Q_ASSERT(t >= 0 || t <= 1);
00146     Q_ASSERT(!m_segments.empty());
00147 
00148     for(QValueVector<KisGradientSegment *>::const_iterator it = m_segments.begin(); it!= m_segments.end(); ++it)
00149     {
00150         if (t > (*it)->startOffset() - DBL_EPSILON && t < (*it)->endOffset() + DBL_EPSILON) {
00151             return *it;
00152         }
00153     }
00154 
00155     return 0;
00156 }
00157 
00158 void KisGradient::colorAt(double t, QColor *color, Q_UINT8 *opacity) const
00159 {
00160     const KisGradientSegment *segment = segmentAt(t);
00161     Q_ASSERT(segment != 0);
00162 
00163     if (segment) {
00164         Color col = segment->colorAt(t);
00165         *color = col.color();
00166         *opacity = static_cast<Q_UINT8>(col.alpha() * OPACITY_OPAQUE + 0.5);
00167     }
00168 }
00169 
00170 QImage KisGradient::generatePreview(int width, int height) const
00171 {
00172     QImage img(width, height, 32);
00173 
00174     for (int y = 0; y < img.height(); y++) {
00175         for (int x = 0; x < img.width(); x++) {
00176 
00177             int backgroundRed = 128 + 63 * ((x / 4 + y / 4) % 2);
00178             int backgroundGreen = backgroundRed;
00179             int backgroundBlue = backgroundRed;
00180 
00181             QColor color;
00182             Q_UINT8 opacity;
00183             double t = static_cast<double>(x) / (img.width() - 1);
00184 
00185             colorAt(t,  &color, &opacity);
00186 
00187             double alpha = static_cast<double>(opacity) / OPACITY_OPAQUE;
00188 
00189             int red = static_cast<int>((1 - alpha) * backgroundRed + alpha * color.red() + 0.5);
00190             int green = static_cast<int>((1 - alpha) * backgroundGreen + alpha * color.green() + 0.5);
00191             int blue = static_cast<int>((1 - alpha) * backgroundBlue + alpha * color.blue() + 0.5);
00192 
00193             img.setPixel(x, y, qRgb(red, green, blue));
00194         }
00195     }
00196 
00197     return img;
00198 }
00199 
00200 KisGradientSegment::KisGradientSegment(int interpolationType, int colorInterpolationType, double startOffset, double middleOffset, double endOffset, const Color& startColor, const Color& endColor)
00201 {
00202     m_interpolator = 0;
00203 
00204     switch (interpolationType) {
00205     case INTERP_LINEAR:
00206         m_interpolator = LinearInterpolationStrategy::instance();
00207         break;
00208     case INTERP_CURVED:
00209         m_interpolator = CurvedInterpolationStrategy::instance();
00210         break;
00211     case INTERP_SINE:
00212         m_interpolator = SineInterpolationStrategy::instance();
00213         break;
00214     case INTERP_SPHERE_INCREASING:
00215         m_interpolator = SphereIncreasingInterpolationStrategy::instance();
00216         break;
00217     case INTERP_SPHERE_DECREASING:
00218         m_interpolator = SphereDecreasingInterpolationStrategy::instance();
00219         break;
00220     }
00221 
00222     m_colorInterpolator = 0;
00223 
00224     switch (colorInterpolationType) {
00225     case COLOR_INTERP_RGB:
00226         m_colorInterpolator = RGBColorInterpolationStrategy::instance();
00227         break;
00228     case COLOR_INTERP_HSV_CCW:
00229         m_colorInterpolator = HSVCCWColorInterpolationStrategy::instance();
00230         break;
00231     case COLOR_INTERP_HSV_CW:
00232         m_colorInterpolator = HSVCWColorInterpolationStrategy::instance();
00233         break;
00234     }
00235 
00236     if (startOffset < DBL_EPSILON) {
00237         m_startOffset = 0;
00238     }
00239     else
00240     if (startOffset > 1 - DBL_EPSILON) {
00241         m_startOffset = 1;
00242     }
00243     else {
00244         m_startOffset = startOffset;
00245     }
00246 
00247     if (middleOffset < m_startOffset + DBL_EPSILON) {
00248         m_middleOffset = m_startOffset;
00249     }
00250     else
00251     if (middleOffset > 1 - DBL_EPSILON) {
00252         m_middleOffset = 1;
00253     }
00254     else {
00255         m_middleOffset = middleOffset;
00256     }
00257 
00258     if (endOffset < m_middleOffset + DBL_EPSILON) {
00259         m_endOffset = m_middleOffset;
00260     }
00261     else
00262     if (endOffset > 1 - DBL_EPSILON) {
00263         m_endOffset = 1;
00264     }
00265     else {
00266         m_endOffset = endOffset;
00267     }
00268 
00269     m_length = m_endOffset - m_startOffset;
00270 
00271     if (m_length < DBL_EPSILON) {
00272         m_middleT = 0.5;
00273     }
00274     else {
00275         m_middleT = (m_middleOffset - m_startOffset) / m_length;
00276     }
00277 
00278     m_startColor = startColor;
00279     m_endColor = endColor;
00280 }
00281 
00282 const Color& KisGradientSegment::startColor() const
00283 {
00284     return m_startColor;
00285 }
00286 
00287 const Color& KisGradientSegment::endColor() const
00288 {
00289     return m_endColor;
00290 }
00291 
00292 double KisGradientSegment::startOffset() const
00293 {
00294     return m_startOffset;
00295 }
00296 
00297 double KisGradientSegment::middleOffset() const
00298 {
00299     return m_middleOffset;
00300 }
00301 
00302 double KisGradientSegment::endOffset() const
00303 {
00304     return m_endOffset;
00305 }
00306 
00307 void KisGradientSegment::setStartOffset(double t)
00308 {
00309     m_startOffset = t;
00310     m_length = m_endOffset - m_startOffset;
00311 
00312     if (m_length < DBL_EPSILON) {
00313         m_middleT = 0.5;
00314     }
00315     else {
00316         m_middleT = (m_middleOffset - m_startOffset) / m_length;
00317     }
00318 }
00319 void KisGradientSegment::setMiddleOffset(double t)
00320 {
00321     m_middleOffset = t;
00322 
00323     if (m_length < DBL_EPSILON) {
00324         m_middleT = 0.5;
00325     }
00326     else {
00327         m_middleT = (m_middleOffset - m_startOffset) / m_length;
00328     }
00329 }
00330 
00331 void KisGradientSegment::setEndOffset(double t)
00332 {
00333     m_endOffset = t;
00334     m_length = m_endOffset - m_startOffset;
00335 
00336     if (m_length < DBL_EPSILON) {
00337         m_middleT = 0.5;
00338     }
00339     else {
00340         m_middleT = (m_middleOffset - m_startOffset) / m_length;
00341     }
00342 }
00343 
00344 int KisGradientSegment::interpolation() const
00345 {
00346     return m_interpolator->type();
00347 }
00348 
00349 void KisGradientSegment::setInterpolation(int interpolationType)
00350 {
00351     switch (interpolationType) {
00352     case INTERP_LINEAR:
00353         m_interpolator = LinearInterpolationStrategy::instance();
00354         break;
00355     case INTERP_CURVED:
00356         m_interpolator = CurvedInterpolationStrategy::instance();
00357         break;
00358     case INTERP_SINE:
00359         m_interpolator = SineInterpolationStrategy::instance();
00360         break;
00361     case INTERP_SPHERE_INCREASING:
00362         m_interpolator = SphereIncreasingInterpolationStrategy::instance();
00363         break;
00364     case INTERP_SPHERE_DECREASING:
00365         m_interpolator = SphereDecreasingInterpolationStrategy::instance();
00366         break;
00367     }
00368 }
00369 
00370 int KisGradientSegment::colorInterpolation() const
00371 {
00372     return m_colorInterpolator->type();
00373 }
00374 
00375 void KisGradientSegment::setColorInterpolation(int colorInterpolationType)
00376 {
00377     switch (colorInterpolationType) {
00378     case COLOR_INTERP_RGB:
00379         m_colorInterpolator = RGBColorInterpolationStrategy::instance();
00380         break;
00381     case COLOR_INTERP_HSV_CCW:
00382         m_colorInterpolator = HSVCCWColorInterpolationStrategy::instance();
00383         break;
00384     case COLOR_INTERP_HSV_CW:
00385         m_colorInterpolator = HSVCWColorInterpolationStrategy::instance();
00386         break;
00387     }
00388 }
00389 
00390 Color KisGradientSegment::colorAt(double t) const
00391 {
00392     Q_ASSERT(t > m_startOffset - DBL_EPSILON && t < m_endOffset + DBL_EPSILON);
00393 
00394     double segmentT;
00395 
00396     if (m_length < DBL_EPSILON) {
00397         segmentT = 0.5;
00398     }
00399     else {
00400         segmentT = (t - m_startOffset) / m_length;
00401     }
00402 
00403     double colorT = m_interpolator->valueAt(segmentT, m_middleT);
00404 
00405     Color color = m_colorInterpolator->colorAt(colorT, m_startColor, m_endColor);
00406 
00407     return color;
00408 }
00409 
00410 bool KisGradientSegment::isValid() const
00411 {
00412     if (m_interpolator == 0 || m_colorInterpolator ==0)
00413         return false;
00414     return true;
00415 }
00416 
00417 KisGradientSegment::RGBColorInterpolationStrategy *KisGradientSegment::RGBColorInterpolationStrategy::instance()
00418 {
00419     if (m_instance == 0) {
00420         m_instance = new RGBColorInterpolationStrategy();
00421         Q_CHECK_PTR(m_instance);
00422     }
00423 
00424     return m_instance;
00425 }
00426 
00427 Color KisGradientSegment::RGBColorInterpolationStrategy::colorAt(double t, Color start, Color end) const
00428 {
00429     int startRed = start.color().red();
00430     int startGreen = start.color().green();
00431     int startBlue = start.color().blue();
00432     double startAlpha = start.alpha();
00433     int red = static_cast<int>(startRed + t * (end.color().red() - startRed) + 0.5);
00434     int green = static_cast<int>(startGreen + t * (end.color().green() - startGreen) + 0.5);
00435     int blue = static_cast<int>(startBlue + t * (end.color().blue() - startBlue) + 0.5);
00436     double alpha = startAlpha + t * (end.alpha() - startAlpha);
00437 
00438     return Color(QColor(red, green, blue), alpha);
00439 }
00440 
00441 KisGradientSegment::HSVCWColorInterpolationStrategy *KisGradientSegment::HSVCWColorInterpolationStrategy::instance()
00442 {
00443     if (m_instance == 0) {
00444         m_instance = new HSVCWColorInterpolationStrategy();
00445         Q_CHECK_PTR(m_instance);
00446     }
00447 
00448     return m_instance;
00449 }
00450 
00451 Color KisGradientSegment::HSVCWColorInterpolationStrategy::colorAt(double t, Color start, Color end) const
00452 {
00453     KoColor sc = KoColor(start.color());
00454     KoColor ec = KoColor(end.color());
00455     
00456     int s = static_cast<int>(sc.S() + t * (ec.S() - sc.S()) + 0.5);
00457     int v = static_cast<int>(sc.V() + t * (ec.V() - sc.V()) + 0.5);
00458     int h;
00459     
00460     if (ec.H() < sc.H()) {
00461         h = static_cast<int>(ec.H() + (1 - t) * (sc.H() - ec.H()) + 0.5);
00462     }
00463     else {
00464         h = static_cast<int>(ec.H() + (1 - t) * (360 - ec.H() + sc.H()) + 0.5);
00465         
00466         if (h > 359) {
00467             h -= 360;
00468         }
00469     }
00470     
00471     double alpha = start.alpha() + t * (end.alpha() - start.alpha());
00472 
00473     return Color(KoColor(h, s, v, KoColor::csHSV).color(), alpha);
00474 }
00475 
00476 KisGradientSegment::HSVCCWColorInterpolationStrategy *KisGradientSegment::HSVCCWColorInterpolationStrategy::instance()
00477 {
00478     if (m_instance == 0) {
00479         m_instance = new HSVCCWColorInterpolationStrategy();
00480         Q_CHECK_PTR(m_instance);
00481     }
00482 
00483     return m_instance;
00484 }
00485 
00486 Color KisGradientSegment::HSVCCWColorInterpolationStrategy::colorAt(double t, Color start, Color end) const
00487 {
00488     KoColor sc = KoColor(start.color());
00489     KoColor se = KoColor(end.color());
00490 
00491     int s = static_cast<int>(sc.S() + t * (se.S() - sc.S()) + 0.5);
00492     int v = static_cast<int>(sc.V() + t * (se.V() - sc.V()) + 0.5);
00493     int h;
00494 
00495     if (sc.H() < se.H()) {
00496         h = static_cast<int>(sc.H() + t * (se.H() - sc.H()) + 0.5);
00497     }
00498     else {
00499         h = static_cast<int>(sc.H() + t * (360 - sc.H() + se.H()) + 0.5);
00500 
00501         if (h > 359) {
00502             h -= 360;
00503         }
00504     }
00505 
00506     double alpha = start.alpha() + t * (end.alpha() - start.alpha());
00507 
00508     return Color(KoColor(h, s, v, KoColor::csHSV).color(), alpha);
00509 }
00510 
00511 KisGradientSegment::LinearInterpolationStrategy *KisGradientSegment::LinearInterpolationStrategy::instance()
00512 {
00513     if (m_instance == 0) {
00514         m_instance = new LinearInterpolationStrategy();
00515         Q_CHECK_PTR(m_instance);
00516     }
00517 
00518     return m_instance;
00519 }
00520 
00521 double KisGradientSegment::LinearInterpolationStrategy::calcValueAt(double t, double middle)
00522 {
00523     Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON);
00524     Q_ASSERT(middle > -DBL_EPSILON && middle < 1 + DBL_EPSILON);
00525 
00526     double value = 0;
00527 
00528     if (t <= middle) {
00529         if (middle < DBL_EPSILON) {
00530             value = 0;
00531         }
00532         else {
00533             value = (t / middle) * 0.5;
00534         }
00535     }
00536     else {
00537         if (middle > 1 - DBL_EPSILON) {
00538             value = 1;
00539         }
00540         else {
00541             value = ((t - middle) / (1 - middle)) * 0.5 + 0.5;
00542         }
00543     }
00544 
00545     return value;
00546 }
00547 
00548 double KisGradientSegment::LinearInterpolationStrategy::valueAt(double t, double middle) const
00549 {
00550     return calcValueAt(t, middle);
00551 }
00552 
00553 KisGradientSegment::CurvedInterpolationStrategy::CurvedInterpolationStrategy()
00554 {
00555     m_logHalf = log(0.5);
00556 }
00557 
00558 KisGradientSegment::CurvedInterpolationStrategy *KisGradientSegment::CurvedInterpolationStrategy::instance()
00559 {
00560     if (m_instance == 0) {
00561         m_instance = new CurvedInterpolationStrategy();
00562         Q_CHECK_PTR(m_instance);
00563     }
00564 
00565     return m_instance;
00566 }
00567 
00568 double KisGradientSegment::CurvedInterpolationStrategy::valueAt(double t, double middle) const
00569 {
00570     Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON);
00571     Q_ASSERT(middle > -DBL_EPSILON && middle < 1 + DBL_EPSILON);
00572 
00573     double value = 0;
00574 
00575     if (middle < DBL_EPSILON) {
00576         middle = DBL_EPSILON;
00577     }
00578 
00579     value = pow(t, m_logHalf / log(middle));
00580 
00581     return value;
00582 }
00583 
00584 KisGradientSegment::SineInterpolationStrategy *KisGradientSegment::SineInterpolationStrategy::instance()
00585 {
00586     if (m_instance == 0) {
00587         m_instance = new SineInterpolationStrategy();
00588         Q_CHECK_PTR(m_instance);
00589     }
00590 
00591     return m_instance;
00592 }
00593 
00594 double KisGradientSegment::SineInterpolationStrategy::valueAt(double t, double middle) const
00595 {
00596     double lt = LinearInterpolationStrategy::calcValueAt(t, middle);
00597     double value = (sin(-M_PI_2 + M_PI * lt) + 1.0) / 2.0;
00598 
00599     return value;
00600 }
00601 
00602 KisGradientSegment::SphereIncreasingInterpolationStrategy *KisGradientSegment::SphereIncreasingInterpolationStrategy::instance()
00603 {
00604     if (m_instance == 0) {
00605         m_instance = new SphereIncreasingInterpolationStrategy();
00606         Q_CHECK_PTR(m_instance);
00607     }
00608 
00609     return m_instance;
00610 }
00611 
00612 double KisGradientSegment::SphereIncreasingInterpolationStrategy::valueAt(double t, double middle) const
00613 {
00614     double lt = LinearInterpolationStrategy::calcValueAt(t, middle) - 1;
00615     double value = sqrt(1 - lt * lt);
00616 
00617     return value;
00618 }
00619 
00620 KisGradientSegment::SphereDecreasingInterpolationStrategy *KisGradientSegment::SphereDecreasingInterpolationStrategy::instance()
00621 {
00622     if (m_instance == 0) {
00623         m_instance = new SphereDecreasingInterpolationStrategy();
00624         Q_CHECK_PTR(m_instance);
00625     }
00626 
00627     return m_instance;
00628 }
00629 
00630 double KisGradientSegment::SphereDecreasingInterpolationStrategy::valueAt(double t, double middle) const
00631 {
00632     double lt = LinearInterpolationStrategy::calcValueAt(t, middle);
00633     double value = 1 - sqrt(1 - lt * lt);
00634 
00635     return value;
00636 }
00637 
00638 #include "kis_gradient.moc"
00639 
KDE Home | KDE Accessibility Home | Description of Access Keys