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     if (t < DBL_EPSILON) {
00146         t = 0;
00147     }
00148     else
00149     if (t > 1 - DBL_EPSILON) {
00150         t = 1;
00151     }
00152 
00153     Q_ASSERT(m_segments.count() != 0);
00154 
00155     KisGradientSegment *segment = 0;
00156 
00157     for (uint i = 0; i < m_segments.count(); i++) {
00158         if (t > m_segments[i]->startOffset() - DBL_EPSILON && t < m_segments[i]->endOffset() + DBL_EPSILON) {
00159             segment = m_segments[i];
00160             break;
00161         }
00162     }
00163 
00164     return segment;
00165 }
00166 
00167 void KisGradient::colorAt(double t, QColor *color, Q_UINT8 *opacity) const
00168 {
00169     const KisGradientSegment *segment = segmentAt(t);
00170     Q_ASSERT(segment != 0);
00171 
00172     if (segment) {
00173         Color col = segment->colorAt(t);
00174         *color = col.color();
00175         *opacity = static_cast<Q_UINT8>(col.alpha() * OPACITY_OPAQUE + 0.5);
00176     }
00177 }
00178 
00179 QImage KisGradient::generatePreview(int width, int height) const
00180 {
00181     QImage img(width, height, 32);
00182 
00183     for (int y = 0; y < img.height(); y++) {
00184         for (int x = 0; x < img.width(); x++) {
00185 
00186             int backgroundRed = 128 + 63 * ((x / 4 + y / 4) % 2);
00187             int backgroundGreen = backgroundRed;
00188             int backgroundBlue = backgroundRed;
00189 
00190             QColor color;
00191             Q_UINT8 opacity;
00192             double t = static_cast<double>(x) / (img.width() - 1);
00193 
00194             colorAt(t,  &color, &opacity);
00195 
00196             double alpha = static_cast<double>(opacity) / OPACITY_OPAQUE;
00197 
00198             int red = static_cast<int>((1 - alpha) * backgroundRed + alpha * color.red() + 0.5);
00199             int green = static_cast<int>((1 - alpha) * backgroundGreen + alpha * color.green() + 0.5);
00200             int blue = static_cast<int>((1 - alpha) * backgroundBlue + alpha * color.blue() + 0.5);
00201 
00202             img.setPixel(x, y, qRgb(red, green, blue));
00203         }
00204     }
00205 
00206     return img;
00207 }
00208 
00209 KisGradientSegment::KisGradientSegment(int interpolationType, int colorInterpolationType, double startOffset, double middleOffset, double endOffset, const Color& startColor, const Color& endColor)
00210 {
00211     m_interpolator = 0;
00212 
00213     switch (interpolationType) {
00214     case INTERP_LINEAR:
00215         m_interpolator = LinearInterpolationStrategy::instance();
00216         break;
00217     case INTERP_CURVED:
00218         m_interpolator = CurvedInterpolationStrategy::instance();
00219         break;
00220     case INTERP_SINE:
00221         m_interpolator = SineInterpolationStrategy::instance();
00222         break;
00223     case INTERP_SPHERE_INCREASING:
00224         m_interpolator = SphereIncreasingInterpolationStrategy::instance();
00225         break;
00226     case INTERP_SPHERE_DECREASING:
00227         m_interpolator = SphereDecreasingInterpolationStrategy::instance();
00228         break;
00229     }
00230 
00231     m_colorInterpolator = 0;
00232 
00233     switch (colorInterpolationType) {
00234     case COLOR_INTERP_RGB:
00235         m_colorInterpolator = RGBColorInterpolationStrategy::instance();
00236         break;
00237     case COLOR_INTERP_HSV_CCW:
00238         m_colorInterpolator = HSVCCWColorInterpolationStrategy::instance();
00239         break;
00240     case COLOR_INTERP_HSV_CW:
00241         m_colorInterpolator = HSVCWColorInterpolationStrategy::instance();
00242         break;
00243     }
00244 
00245     if (startOffset < DBL_EPSILON) {
00246         m_startOffset = 0;
00247     }
00248     else
00249     if (startOffset > 1 - DBL_EPSILON) {
00250         m_startOffset = 1;
00251     }
00252     else {
00253         m_startOffset = startOffset;
00254     }
00255 
00256     if (middleOffset < m_startOffset + DBL_EPSILON) {
00257         m_middleOffset = m_startOffset;
00258     }
00259     else
00260     if (middleOffset > 1 - DBL_EPSILON) {
00261         m_middleOffset = 1;
00262     }
00263     else {
00264         m_middleOffset = middleOffset;
00265     }
00266 
00267     if (endOffset < m_middleOffset + DBL_EPSILON) {
00268         m_endOffset = m_middleOffset;
00269     }
00270     else
00271     if (endOffset > 1 - DBL_EPSILON) {
00272         m_endOffset = 1;
00273     }
00274     else {
00275         m_endOffset = endOffset;
00276     }
00277 
00278     m_length = m_endOffset - m_startOffset;
00279 
00280     if (m_length < DBL_EPSILON) {
00281         m_middleT = 0.5;
00282     }
00283     else {
00284         m_middleT = (m_middleOffset - m_startOffset) / m_length;
00285     }
00286 
00287     m_startColor = startColor;
00288     m_endColor = endColor;
00289 }
00290 
00291 const Color& KisGradientSegment::startColor() const
00292 {
00293     return m_startColor;
00294 }
00295 
00296 const Color& KisGradientSegment::endColor() const
00297 {
00298     return m_endColor;
00299 }
00300 
00301 double KisGradientSegment::startOffset() const
00302 {
00303     return m_startOffset;
00304 }
00305 
00306 double KisGradientSegment::middleOffset() const
00307 {
00308     return m_middleOffset;
00309 }
00310 
00311 double KisGradientSegment::endOffset() const
00312 {
00313     return m_endOffset;
00314 }
00315 
00316 void KisGradientSegment::setStartOffset(double t)
00317 {
00318     m_startOffset = t;
00319     m_length = m_endOffset - m_startOffset;
00320 
00321     if (m_length < DBL_EPSILON) {
00322         m_middleT = 0.5;
00323     }
00324     else {
00325         m_middleT = (m_middleOffset - m_startOffset) / m_length;
00326     }
00327 }
00328 void KisGradientSegment::setMiddleOffset(double t)
00329 {
00330     m_middleOffset = t;
00331 
00332     if (m_length < DBL_EPSILON) {
00333         m_middleT = 0.5;
00334     }
00335     else {
00336         m_middleT = (m_middleOffset - m_startOffset) / m_length;
00337     }
00338 }
00339 
00340 void KisGradientSegment::setEndOffset(double t)
00341 {
00342     m_endOffset = t;
00343     m_length = m_endOffset - m_startOffset;
00344 
00345     if (m_length < DBL_EPSILON) {
00346         m_middleT = 0.5;
00347     }
00348     else {
00349         m_middleT = (m_middleOffset - m_startOffset) / m_length;
00350     }
00351 }
00352 
00353 int KisGradientSegment::interpolation() const
00354 {
00355     return m_interpolator->type();
00356 }
00357 
00358 void KisGradientSegment::setInterpolation(int interpolationType)
00359 {
00360     switch (interpolationType) {
00361     case INTERP_LINEAR:
00362         m_interpolator = LinearInterpolationStrategy::instance();
00363         break;
00364     case INTERP_CURVED:
00365         m_interpolator = CurvedInterpolationStrategy::instance();
00366         break;
00367     case INTERP_SINE:
00368         m_interpolator = SineInterpolationStrategy::instance();
00369         break;
00370     case INTERP_SPHERE_INCREASING:
00371         m_interpolator = SphereIncreasingInterpolationStrategy::instance();
00372         break;
00373     case INTERP_SPHERE_DECREASING:
00374         m_interpolator = SphereDecreasingInterpolationStrategy::instance();
00375         break;
00376     }
00377 }
00378 
00379 int KisGradientSegment::colorInterpolation() const
00380 {
00381     return m_colorInterpolator->type();
00382 }
00383 
00384 void KisGradientSegment::setColorInterpolation(int colorInterpolationType)
00385 {
00386     switch (colorInterpolationType) {
00387     case COLOR_INTERP_RGB:
00388         m_colorInterpolator = RGBColorInterpolationStrategy::instance();
00389         break;
00390     case COLOR_INTERP_HSV_CCW:
00391         m_colorInterpolator = HSVCCWColorInterpolationStrategy::instance();
00392         break;
00393     case COLOR_INTERP_HSV_CW:
00394         m_colorInterpolator = HSVCWColorInterpolationStrategy::instance();
00395         break;
00396     }
00397 }
00398 
00399 Color KisGradientSegment::colorAt(double t) const
00400 {
00401     Q_ASSERT(t > m_startOffset - DBL_EPSILON && t < m_endOffset + DBL_EPSILON);
00402 
00403     double segmentT;
00404 
00405     if (m_length < DBL_EPSILON) {
00406         segmentT = 0.5;
00407     }
00408     else {
00409         segmentT = (t - m_startOffset) / m_length;
00410     }
00411 
00412     double colorT = m_interpolator->valueAt(segmentT, m_middleT);
00413 
00414     Color color = m_colorInterpolator->colorAt(colorT, m_startColor, m_endColor);
00415 
00416     return color;
00417 }
00418 
00419 bool KisGradientSegment::isValid() const
00420 {
00421     if (m_interpolator == 0 || m_colorInterpolator ==0)
00422         return false;
00423     return true;
00424 }
00425 
00426 KisGradientSegment::RGBColorInterpolationStrategy *KisGradientSegment::RGBColorInterpolationStrategy::instance()
00427 {
00428     if (m_instance == 0) {
00429         m_instance = new RGBColorInterpolationStrategy();
00430         Q_CHECK_PTR(m_instance);
00431     }
00432 
00433     return m_instance;
00434 }
00435 
00436 Color KisGradientSegment::RGBColorInterpolationStrategy::colorAt(double t, Color start, Color end) const
00437 {
00438     int red = static_cast<int>(start.color().red() + t * (end.color().red() - start.color().red()) + 0.5);
00439     int green = static_cast<int>(start.color().green() + t * (end.color().green() - start.color().green()) + 0.5);
00440     int blue = static_cast<int>(start.color().blue() + t * (end.color().blue() - start.color().blue()) + 0.5);
00441     double alpha = start.alpha() + t * (end.alpha() - start.alpha());
00442 
00443     return Color(QColor(red, green, blue), alpha);
00444 }
00445 
00446 KisGradientSegment::HSVCWColorInterpolationStrategy *KisGradientSegment::HSVCWColorInterpolationStrategy::instance()
00447 {
00448     if (m_instance == 0) {
00449         m_instance = new HSVCWColorInterpolationStrategy();
00450         Q_CHECK_PTR(m_instance);
00451     }
00452 
00453     return m_instance;
00454 }
00455 
00456 Color KisGradientSegment::HSVCWColorInterpolationStrategy::colorAt(double t, Color start, Color end) const
00457 {
00458     KoColor sc = KoColor(start.color());
00459     KoColor ec = KoColor(end.color());
00460     
00461     int s = static_cast<int>(sc.S() + t * (ec.S() - sc.S()) + 0.5);
00462     int v = static_cast<int>(sc.V() + t * (ec.V() - sc.V()) + 0.5);
00463     int h;
00464     
00465     if (ec.H() < sc.H()) {
00466         h = static_cast<int>(ec.H() + (1 - t) * (sc.H() - ec.H()) + 0.5);
00467     }
00468     else {
00469         h = static_cast<int>(ec.H() + (1 - t) * (360 - ec.H() + sc.H()) + 0.5);
00470         
00471         if (h > 359) {
00472             h -= 360;
00473         }
00474     }
00475     
00476     double alpha = start.alpha() + t * (end.alpha() - start.alpha());
00477 
00478     return Color(KoColor(h, s, v, KoColor::csHSV).color(), alpha);
00479 }
00480 
00481 KisGradientSegment::HSVCCWColorInterpolationStrategy *KisGradientSegment::HSVCCWColorInterpolationStrategy::instance()
00482 {
00483     if (m_instance == 0) {
00484         m_instance = new HSVCCWColorInterpolationStrategy();
00485         Q_CHECK_PTR(m_instance);
00486     }
00487 
00488     return m_instance;
00489 }
00490 
00491 Color KisGradientSegment::HSVCCWColorInterpolationStrategy::colorAt(double t, Color start, Color end) const
00492 {
00493     KoColor sc = KoColor(start.color());
00494     KoColor se = KoColor(end.color());
00495 
00496     int s = static_cast<int>(sc.S() + t * (se.S() - sc.S()) + 0.5);
00497     int v = static_cast<int>(sc.V() + t * (se.V() - sc.V()) + 0.5);
00498     int h;
00499 
00500     if (sc.H() < se.H()) {
00501         h = static_cast<int>(sc.H() + t * (se.H() - sc.H()) + 0.5);
00502     }
00503     else {
00504         h = static_cast<int>(sc.H() + t * (360 - sc.H() + se.H()) + 0.5);
00505 
00506         if (h > 359) {
00507             h -= 360;
00508         }
00509     }
00510 
00511     double alpha = start.alpha() + t * (end.alpha() - start.alpha());
00512 
00513     return Color(KoColor(h, s, v, KoColor::csHSV).color(), alpha);
00514 }
00515 
00516 KisGradientSegment::LinearInterpolationStrategy *KisGradientSegment::LinearInterpolationStrategy::instance()
00517 {
00518     if (m_instance == 0) {
00519         m_instance = new LinearInterpolationStrategy();
00520         Q_CHECK_PTR(m_instance);
00521     }
00522 
00523     return m_instance;
00524 }
00525 
00526 double KisGradientSegment::LinearInterpolationStrategy::calcValueAt(double t, double middle)
00527 {
00528     Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON);
00529     Q_ASSERT(middle > -DBL_EPSILON && middle < 1 + DBL_EPSILON);
00530 
00531     double value = 0;
00532 
00533     if (t <= middle) {
00534         if (middle < DBL_EPSILON) {
00535             value = 0;
00536         }
00537         else {
00538             value = (t / middle) * 0.5;
00539         }
00540     }
00541     else {
00542         if (middle > 1 - DBL_EPSILON) {
00543             value = 1;
00544         }
00545         else {
00546             value = ((t - middle) / (1 - middle)) * 0.5 + 0.5;
00547         }
00548     }
00549 
00550     return value;
00551 }
00552 
00553 double KisGradientSegment::LinearInterpolationStrategy::valueAt(double t, double middle) const
00554 {
00555     return calcValueAt(t, middle);
00556 }
00557 
00558 KisGradientSegment::CurvedInterpolationStrategy::CurvedInterpolationStrategy()
00559 {
00560     m_logHalf = log(0.5);
00561 }
00562 
00563 KisGradientSegment::CurvedInterpolationStrategy *KisGradientSegment::CurvedInterpolationStrategy::instance()
00564 {
00565     if (m_instance == 0) {
00566         m_instance = new CurvedInterpolationStrategy();
00567         Q_CHECK_PTR(m_instance);
00568     }
00569 
00570     return m_instance;
00571 }
00572 
00573 double KisGradientSegment::CurvedInterpolationStrategy::valueAt(double t, double middle) const
00574 {
00575     Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON);
00576     Q_ASSERT(middle > -DBL_EPSILON && middle < 1 + DBL_EPSILON);
00577 
00578     double value = 0;
00579 
00580     if (middle < DBL_EPSILON) {
00581         middle = DBL_EPSILON;
00582     }
00583 
00584     value = pow(t, m_logHalf / log(middle));
00585 
00586     return value;
00587 }
00588 
00589 KisGradientSegment::SineInterpolationStrategy *KisGradientSegment::SineInterpolationStrategy::instance()
00590 {
00591     if (m_instance == 0) {
00592         m_instance = new SineInterpolationStrategy();
00593         Q_CHECK_PTR(m_instance);
00594     }
00595 
00596     return m_instance;
00597 }
00598 
00599 double KisGradientSegment::SineInterpolationStrategy::valueAt(double t, double middle) const
00600 {
00601     double lt = LinearInterpolationStrategy::calcValueAt(t, middle);
00602     double value = (sin(-M_PI_2 + M_PI * lt) + 1.0) / 2.0;
00603 
00604     return value;
00605 }
00606 
00607 KisGradientSegment::SphereIncreasingInterpolationStrategy *KisGradientSegment::SphereIncreasingInterpolationStrategy::instance()
00608 {
00609     if (m_instance == 0) {
00610         m_instance = new SphereIncreasingInterpolationStrategy();
00611         Q_CHECK_PTR(m_instance);
00612     }
00613 
00614     return m_instance;
00615 }
00616 
00617 double KisGradientSegment::SphereIncreasingInterpolationStrategy::valueAt(double t, double middle) const
00618 {
00619     double lt = LinearInterpolationStrategy::calcValueAt(t, middle) - 1;
00620     double value = sqrt(1 - lt * lt);
00621 
00622     return value;
00623 }
00624 
00625 KisGradientSegment::SphereDecreasingInterpolationStrategy *KisGradientSegment::SphereDecreasingInterpolationStrategy::instance()
00626 {
00627     if (m_instance == 0) {
00628         m_instance = new SphereDecreasingInterpolationStrategy();
00629         Q_CHECK_PTR(m_instance);
00630     }
00631 
00632     return m_instance;
00633 }
00634 
00635 double KisGradientSegment::SphereDecreasingInterpolationStrategy::valueAt(double t, double middle) const
00636 {
00637     double lt = LinearInterpolationStrategy::calcValueAt(t, middle);
00638     double value = 1 - sqrt(1 - lt * lt);
00639 
00640     return value;
00641 }
00642 
00643 #include "kis_gradient.moc"
00644 
KDE Home | KDE Accessibility Home | Description of Access Keys