kdecore Library API Documentation

kiconeffect.cpp

00001 /* vi: ts=8 sts=4 sw=4
00002  * $Id: kiconeffect.cpp,v 1.44 2002/11/21 19:18:51 pfeiffer Exp $
00003  *
00004  * This file is part of the KDE project, module kdecore.
00005  * Copyright (C) 2000 Geert Jansen <jansen@kde.org>
00006  * with minor additions and based on ideas from
00007  * Torsten Rahn <torsten@kde.org>
00008  *
00009  * This is free software; it comes under the GNU Library General
00010  * Public License, version 2. See the file "COPYING.LIB" for the
00011  * exact licensing terms.
00012  */
00013 
00014 #include <config.h>
00015 #include <unistd.h>
00016 #include <math.h>
00017 
00018 #include <qstring.h>
00019 #include <qstringlist.h>
00020 #include <qbitmap.h>
00021 #include <qpixmap.h>
00022 #include <qimage.h>
00023 #include <qcolor.h>
00024 #include <qwidget.h>
00025 #include <qpainter.h>
00026 #include <qpen.h>
00027 
00028 #include <kdebug.h>
00029 #include <kglobal.h>
00030 #include <kconfig.h>
00031 #include <kglobalsettings.h>
00032 #include <kicontheme.h>
00033 #include "kiconeffect.h"
00034 
00035 extern bool qt_use_xrender;
00036 extern bool qt_has_xft;
00037 
00038 class KIconEffectPrivate
00039 {
00040 public:
00041     QString mKey[6][3];
00042 };
00043 
00044 KIconEffect::KIconEffect()
00045 {
00046     d = new KIconEffectPrivate;
00047     init();
00048 }
00049 
00050 KIconEffect::~KIconEffect()
00051 {
00052     delete d;
00053 }
00054 
00055 void KIconEffect::init()
00056 {
00057     KConfig *config = KGlobal::config();
00058 
00059     int i, j, effect=-1;
00060     QStringList groups;
00061     groups += "Desktop";
00062     groups += "Toolbar";
00063     groups += "MainToolbar";
00064     groups += "Small";
00065     groups += "Panel";
00066 
00067     QStringList states;
00068     states += "Default";
00069     states += "Active";
00070     states += "Disabled";
00071 
00072     QStringList::ConstIterator it, it2;
00073     QString _togray("togray");
00074     QString _colorize("colorize");
00075     QString _desaturate("desaturate");
00076     QString _togamma("togamma");
00077     QString _none("none");
00078 
00079     KConfigGroupSaver cs(config, "default");
00080 
00081     for (it=groups.begin(), i=0; it!=groups.end(); it++, i++)
00082     {
00083     // Default effects
00084     mEffect[i][0] = NoEffect;
00085     mEffect[i][1] =  ((i==0)||(i==4)) ? ToGamma : NoEffect;
00086     mEffect[i][2] = ToGray; 
00087     
00088     mTrans[i][0] = false;
00089     mTrans[i][1] = false;
00090     mTrans[i][2] = true;
00091         mValue[i][0] = 1.0;
00092         mValue[i][1] = ((i==0)||(i==4)) ? 0.7 : 1.0;
00093         mValue[i][2] = 1.0;
00094         mColor[i][0] = QColor(144,128,248);
00095         mColor[i][1] = QColor(169,156,255);
00096         mColor[i][2] = QColor(34,202,0);
00097 
00098     config->setGroup(*it + "Icons");
00099     for (it2=states.begin(), j=0; it2!=states.end(); it2++, j++)
00100     {
00101         QString tmp = config->readEntry(*it2 + "Effect");
00102         if (tmp == _togray)
00103         effect = ToGray;
00104         else if (tmp == _colorize)
00105         effect = Colorize;
00106         else if (tmp == _desaturate)
00107         effect = DeSaturate;
00108         else if (tmp == _togamma)
00109         effect = ToGamma;
00110             else if (tmp == _none)
00111         effect = NoEffect;
00112         else
00113         continue;
00114         if(effect != -1)
00115                 mEffect[i][j] = effect;
00116         mValue[i][j] = config->readDoubleNumEntry(*it2 + "Value");
00117         mColor[i][j] = config->readColorEntry(*it2 + "Color");
00118         mTrans[i][j] = config->readBoolEntry(*it2 + "SemiTransparent");
00119 
00120     }
00121     }    
00122 }
00123 
00124 bool KIconEffect::hasEffect(int group, int state) const
00125 {
00126     return mEffect[group][state] != NoEffect;
00127 }
00128 
00129 QString KIconEffect::fingerprint(int group, int state) const
00130 {
00131     if ( group >= KIcon::LastGroup ) return "";
00132     QString cached = d->mKey[group][state];
00133     if (cached.isEmpty())
00134     {
00135         QString tmp;
00136         cached = tmp.setNum(mEffect[group][state]);
00137         cached += ':';
00138         cached += tmp.setNum(mValue[group][state]);
00139         cached += ':';
00140         cached += mTrans[group][state] ? QString::fromLatin1("trans")
00141             : QString::fromLatin1("notrans");
00142         if (mEffect[group][state] == Colorize)
00143         {
00144             cached += ':';
00145             cached += mColor[group][state].name();
00146         }
00147     
00148         d->mKey[group][state] = cached;    
00149     }
00150     
00151     return cached;
00152 }
00153 
00154 QImage KIconEffect::apply(QImage image, int group, int state) const
00155 {
00156     if (state >= KIcon::LastState)
00157     {
00158     kdDebug(265) << "Illegal icon state: " << state << "\n";
00159     return image;
00160     }
00161     if (group >= KIcon::LastGroup)
00162     {
00163     kdDebug(265) << "Illegal icon group: " << group << "\n";
00164     return image;
00165     }
00166     return apply(image, mEffect[group][state], mValue[group][state],
00167         mColor[group][state], mTrans[group][state]);
00168 }
00169 
00170 QImage KIconEffect::apply(QImage image, int effect, float value, const QColor col, bool trans) const
00171 {
00172     if (effect >= LastEffect )
00173     {
00174     kdDebug(265) << "Illegal icon effect: " << effect << "\n";
00175     return image;
00176     }
00177     if (value > 1.0)
00178     value = 1.0;
00179     else if (value < 0.0)
00180     value = 0.0;
00181     switch (effect)
00182     {
00183     case ToGray:
00184     toGray(image, value);
00185     break;
00186     case DeSaturate:
00187     deSaturate(image, value);
00188     break;
00189     case Colorize:
00190         colorize(image, col, value);
00191         break;
00192     case ToGamma:
00193         toGamma(image, value);
00194         break;
00195     }
00196     if (trans == true)
00197     {
00198     semiTransparent(image);
00199     }
00200     return image;
00201 }
00202 
00203 QPixmap KIconEffect::apply(QPixmap pixmap, int group, int state) const
00204 {
00205     if (state >= KIcon::LastState)
00206     {
00207     kdDebug(265) << "Illegal icon state: " << state << "\n";
00208     return pixmap;
00209     }
00210     if (group >= KIcon::LastGroup)
00211     {
00212     kdDebug(265) << "Illegal icon group: " << group << "\n";
00213     return pixmap;
00214     }
00215     return apply(pixmap, mEffect[group][state], mValue[group][state],
00216         mColor[group][state], mTrans[group][state]);
00217 }
00218 
00219 QPixmap KIconEffect::apply(QPixmap pixmap, int effect, float value,
00220     const QColor col, bool trans) const
00221 {
00222     QPixmap result;
00223 
00224     if (effect >= LastEffect )
00225     {
00226     kdDebug(265) << "Illegal icon effect: " << effect << "\n";
00227     return result;
00228     }
00229 
00230     if ((trans == true) && (effect == NoEffect))
00231     {
00232         result = pixmap;
00233         semiTransparent(result);
00234     }
00235     else if ( effect != NoEffect )
00236     {
00237         QImage tmpImg = pixmap.convertToImage();
00238         tmpImg = apply(tmpImg, effect, value, col, trans);
00239         result.convertFromImage(tmpImg);
00240     }
00241     else
00242         result = pixmap;
00243 
00244     return result;
00245 }
00246 
00247 // Taken from KImageEffect. We don't want to link kdecore to kdeui! As long
00248 // as this code is not too big, it doesn't seem much of a problem to me.
00249 
00250 void KIconEffect::toGray(QImage &img, float value)
00251 {
00252     int pixels = (img.depth() > 8) ? img.width()*img.height()
00253         : img.numColors();
00254     unsigned int *data = img.depth() > 8 ? (unsigned int *) img.bits()
00255         : (unsigned int *) img.colorTable();
00256     int rval, gval, bval, val, alpha, i;
00257     for (i=0; i<pixels; i++)
00258     {
00259     val = qGray(data[i]);
00260     alpha = qAlpha(data[i]);
00261     if (value < 1.0)
00262     {
00263         rval = static_cast<int>(value*val+(1.0-value)*qRed(data[i]));
00264         gval = static_cast<int>(value*val+(1.0-value)*qGreen(data[i]));
00265         bval = static_cast<int>(value*val+(1.0-value)*qBlue(data[i]));
00266         data[i] = qRgba(rval, gval, bval, alpha);
00267     } else
00268         data[i] = qRgba(val, val, val, alpha);
00269     }
00270 }
00271 
00272 void KIconEffect::colorize(QImage &img, const QColor &col, float value)
00273 {
00274     int pixels = (img.depth() > 8) ? img.width()*img.height()
00275         : img.numColors();
00276     unsigned int *data = img.depth() > 8 ? (unsigned int *) img.bits()
00277         : (unsigned int *) img.colorTable();
00278     int rval, gval, bval, val, alpha, i;
00279     float rcol = col.red(), gcol = col.green(), bcol = col.blue();
00280     for (i=0; i<pixels; i++)
00281     {
00282         val = qGray(data[i]);
00283         if (val < 128)
00284         {
00285              rval = static_cast<int>(rcol/128*val);
00286              gval = static_cast<int>(gcol/128*val);
00287              bval = static_cast<int>(bcol/128*val);
00288         }
00289         else if (val > 128)
00290         {
00291              rval = static_cast<int>((val-128)*(2-rcol/128)+rcol-1);
00292              gval = static_cast<int>((val-128)*(2-gcol/128)+gcol-1);
00293              bval = static_cast<int>((val-128)*(2-bcol/128)+bcol-1);
00294         }
00295     else // val == 128
00296     {
00297              rval = static_cast<int>(rcol);
00298              gval = static_cast<int>(gcol);
00299              bval = static_cast<int>(bcol);
00300     }
00301     if (value < 1.0)
00302     {
00303         rval = static_cast<int>(value*rval+(1.0 - value)*qRed(data[i]));
00304         gval = static_cast<int>(value*gval+(1.0 - value)*qGreen(data[i]));
00305         bval = static_cast<int>(value*bval+(1.0 - value)*qBlue(data[i]));
00306     }
00307 
00308     alpha = qAlpha(data[i]);
00309     data[i] = qRgba(rval, gval, bval, alpha);
00310     }
00311 }
00312 
00313 void KIconEffect::deSaturate(QImage &img, float value)
00314 {
00315     int pixels = (img.depth() > 8) ? img.width()*img.height()
00316         : img.numColors();
00317     unsigned int *data = (img.depth() > 8) ? (unsigned int *) img.bits()
00318         : (unsigned int *) img.colorTable();
00319     QColor color;
00320     int h, s, v, i;
00321     for (i=0; i<pixels; i++)
00322     {
00323         color.setRgb(data[i]);
00324         color.hsv(&h, &s, &v);
00325         color.setHsv(h, (int) (s * (1.0 - value) + 0.5), v);
00326     data[i] = qRgba(color.red(), color.green(), color.blue(),
00327         qAlpha(data[i]));
00328     }
00329 }
00330 
00331 void KIconEffect::toGamma(QImage &img, float value)
00332 {
00333     int pixels = (img.depth() > 8) ? img.width()*img.height()
00334         : img.numColors();
00335     unsigned int *data = (img.depth() > 8) ? (unsigned int *) img.bits()
00336         : (unsigned int *) img.colorTable();
00337     QColor color;
00338     int i, rval, gval, bval;
00339     float gamma;
00340     gamma = 1/(2*value+0.5);
00341 
00342     for (i=0; i<pixels; i++)
00343     {
00344         color.setRgb(data[i]);
00345         color.rgb(&rval, &gval, &bval);
00346         rval = static_cast<int>(pow(static_cast<float>(rval)/255 , gamma)*255);
00347         gval = static_cast<int>(pow(static_cast<float>(gval)/255 , gamma)*255);
00348         bval = static_cast<int>(pow(static_cast<float>(bval)/255 , gamma)*255);
00349     data[i] = qRgba(rval, gval, bval, qAlpha(data[i]));
00350     }
00351 }
00352 
00353 void KIconEffect::semiTransparent(QImage &img)
00354 {
00355     img.setAlphaBuffer(true);
00356 
00357     int x, y;
00358     if (img.depth() == 32)
00359     {
00360     int width  = img.width();
00361     int height = img.height();
00362     
00363     if (qt_use_xrender && qt_has_xft )
00364       for (y=0; y<height; y++)
00365       {
00366 #ifdef WORDS_BIGENDIAN
00367         uchar *line = (uchar*) img.scanLine(y);
00368 #else
00369         uchar *line = (uchar*) img.scanLine(y) + 3;
00370 #endif
00371         for (x=0; x<width; x++)
00372         {
00373         *line >>= 1;
00374         line += 4;
00375         }
00376       }
00377     else
00378       for (y=0; y<height; y++)
00379       {
00380         QRgb *line = (QRgb *) img.scanLine(y);
00381         for (x=(y%2); x<width; x+=2)
00382         line[x] &= 0x00ffffff;
00383       }
00384 
00385     } else
00386     {
00387     // Insert transparent pixel into the clut.
00388     int transColor = -1;
00389 
00390         // search for a color that is already transparent
00391         for (x=0; x<img.numColors(); x++)
00392         {
00393             // try to find already transparent pixel
00394             if (qAlpha(img.color(x)) < 127)
00395             {
00396                 transColor = x;
00397                 break;
00398             }
00399         }
00400 
00401 
00402         // FIXME: image must have transparency
00403         if(transColor < 0 || transColor >= img.numColors())
00404             return;
00405 
00406     img.setColor(transColor, 0);
00407         if(img.depth() == 8)
00408         {
00409             for (y=0; y<img.height(); y++)
00410             {
00411                 unsigned char *line = img.scanLine(y);
00412                 for (x=(y%2); x<img.width(); x+=2)
00413                     line[x] = transColor;
00414             }
00415     }
00416         else
00417         {
00418             // SLOOW, but simple, as we would have to
00419             // deal with endianess etc on our own here
00420             for (y=0; y<img.height(); y++)
00421                 for (x=(y%2); x<img.width(); x+=2)
00422                     img.setPixel(x, y, transColor);
00423         }
00424     }
00425 }
00426 
00427 void KIconEffect::semiTransparent(QPixmap &pix)
00428 {
00429     if ( qt_use_xrender && qt_has_xft )
00430     {
00431     QImage img=pix.convertToImage();
00432     semiTransparent(img);
00433     pix.convertFromImage(img);
00434     return;
00435     }
00436 
00437     QImage img;
00438     if (pix.mask() != 0L)
00439     img = pix.mask()->convertToImage();
00440     else
00441     {
00442     img.create(pix.size(), 1, 2, QImage::BigEndian);
00443     img.fill(1);
00444     }
00445 
00446     for (int y=0; y<img.height(); y++)
00447     {
00448     QRgb *line = (QRgb *) img.scanLine(y);
00449     QRgb pattern = (y % 2) ? 0x55555555 : 0xaaaaaaaa;
00450     for (int x=0; x<(img.width()+31)/32; x++)
00451         line[x] &= pattern;
00452     }
00453     QBitmap mask;
00454     mask.convertFromImage(img);
00455     pix.setMask(mask);
00456 }
00457 
00458 QImage KIconEffect::doublePixels(QImage src) const
00459 {
00460     QImage dst;
00461     if (src.depth() == 1)
00462     {
00463     kdDebug(265) << "image depth 1 not supported\n";
00464     return dst;
00465     }
00466 
00467     int w = src.width();
00468     int h = src.height();
00469     dst.create(w*2, h*2, src.depth());
00470     dst.setAlphaBuffer(src.hasAlphaBuffer());
00471 
00472     int x, y;
00473     if (src.depth() == 32)
00474     {
00475     QRgb *l1, *l2;
00476     for (y=0; y<h; y++)
00477     {
00478         l1 = (QRgb *) src.scanLine(y);
00479         l2 = (QRgb *) dst.scanLine(y*2);
00480         for (x=0; x<w; x++)
00481         {
00482         l2[x*2] = l2[x*2+1] = l1[x];
00483         }
00484         memcpy(dst.scanLine(y*2+1), l2, dst.bytesPerLine());
00485     }
00486     } else
00487     {
00488     for (x=0; x<src.numColors(); x++)
00489         dst.setColor(x, src.color(x));
00490 
00491     unsigned char *l1, *l2;
00492     for (y=0; y<h; y++)
00493     {
00494         l1 = src.scanLine(y);
00495         l2 = dst.scanLine(y*2);
00496         for (x=0; x<w; x++)
00497         {
00498         l2[x*2] = l1[x];
00499         l2[x*2+1] = l1[x];
00500         }
00501         memcpy(dst.scanLine(y*2+1), l2, dst.bytesPerLine());
00502     }
00503     }
00504     return dst;
00505 }
00506 
00507 void KIconEffect::overlay(QImage &src, QImage &overlay)
00508 {
00509     if (src.depth() != overlay.depth())
00510     {
00511     kdDebug(265) << "Image depth src != overlay!\n";
00512     return;
00513     }
00514     if (src.size() != overlay.size())
00515     {
00516     kdDebug(265) << "Image size src != overlay\n";
00517     return;
00518     }
00519     if (!overlay.hasAlphaBuffer())
00520     {
00521     kdDebug(265) << "Overlay doesn't have alpha buffer!\n";
00522     return;
00523     }
00524 
00525     int i, j;
00526 
00527     // We don't do 1 bpp
00528 
00529     if (src.depth() == 1)
00530     {
00531     kdDebug(265) << "1bpp not supported!\n";
00532     return;
00533     }
00534 
00535     // Overlay at 8 bpp doesn't use alpha blending
00536 
00537     if (src.depth() == 8)
00538     {
00539     if (src.numColors() + overlay.numColors() > 255)
00540     {
00541         kdDebug(265) << "Too many colors in src + overlay!\n";
00542         return;
00543     }
00544 
00545     // Find transparent pixel in overlay
00546     int trans;
00547     for (trans=0; trans<overlay.numColors(); trans++)
00548     {
00549         if (qAlpha(overlay.color(trans)) == 0)
00550         {
00551         kdDebug(265) << "transparent pixel found at " << trans << "\n";
00552         break;
00553         }
00554     }
00555     if (trans == overlay.numColors())
00556     {
00557         kdDebug(265) << "transparent pixel not found!\n";
00558         return;
00559     }
00560 
00561     // Merge color tables
00562     int nc = src.numColors();
00563     src.setNumColors(nc + overlay.numColors());
00564     for (i=0; i<overlay.numColors(); i++)
00565     {
00566         src.setColor(nc+i, overlay.color(i));
00567     }
00568 
00569     // Overwrite nontransparent pixels.
00570     unsigned char *oline, *sline;
00571     for (i=0; i<src.height(); i++)
00572     {
00573         oline = overlay.scanLine(i);
00574         sline = src.scanLine(i);
00575         for (j=0; j<src.width(); j++)
00576         {
00577         if (oline[j] != trans)
00578             sline[j] = oline[j]+nc;
00579         }
00580     }
00581     }
00582 
00583     // Overlay at 32 bpp does use alpha blending
00584 
00585     if (src.depth() == 32)
00586     {
00587     QRgb *oline, *sline;
00588     int r1, g1, b1, a1;
00589     int r2, g2, b2, a2;
00590 
00591     for (i=0; i<src.height(); i++)
00592     {
00593         oline = (QRgb *) overlay.scanLine(i);
00594         sline = (QRgb *) src.scanLine(i);
00595 
00596         for (j=0; j<src.width(); j++)
00597         {
00598         r1 = qRed(oline[j]);
00599         g1 = qGreen(oline[j]);
00600         b1 = qBlue(oline[j]);
00601         a1 = qAlpha(oline[j]);
00602 
00603         r2 = qRed(sline[j]);
00604         g2 = qGreen(sline[j]);
00605         b2 = qBlue(sline[j]);
00606         a2 = qAlpha(sline[j]);
00607 
00608         r2 = (a1 * r1 + (0xff - a1) * r2) >> 8;
00609         g2 = (a1 * g1 + (0xff - a1) * g2) >> 8;
00610         b2 = (a1 * b1 + (0xff - a1) * b2) >> 8;
00611         a2 = QMAX(a1, a2);
00612 
00613         sline[j] = qRgba(r2, g2, b2, a2);
00614         }
00615     }
00616     }
00617 
00618     return;
00619 }
00620 
00621     void
00622 KIconEffect::visualActivate(QWidget * widget, QRect rect)
00623 {
00624     if (!KGlobalSettings::visualActivate())
00625         return;
00626 
00627     uint actSpeed = KGlobalSettings::visualActivateSpeed();
00628 
00629     uint actCount = QMIN(rect.width(), rect.height()) / 2;
00630 
00631     // Clip actCount to range 1..10.
00632 
00633     if (actCount < 1)
00634         actCount = 1;
00635 
00636     else if (actCount > 10)
00637         actCount = 10;
00638 
00639     // Clip actSpeed to range 1..100.
00640 
00641     if (actSpeed < 1)
00642         actSpeed = 1;
00643 
00644     else if (actSpeed > 100)
00645         actSpeed = 100;
00646 
00647     // actSpeed needs to be converted to actDelay.
00648     // actDelay is inversely proportional to actSpeed and needs to be
00649     // divided up into actCount portions.
00650     // We also convert the us value to ms.
00651 
00652     unsigned int actDelay = (1000 * (100 - actSpeed)) / actCount;
00653 
00654     //kdDebug() << "actCount=" << actCount << " actDelay=" << actDelay << endl;
00655 
00656     QPoint c = rect.center();
00657 
00658     QPainter p(widget);
00659 
00660     // Use NotROP to avoid having to repaint the pixmap each time.
00661     p.setPen(QPen(Qt::black, 2, Qt::DotLine));
00662     p.setRasterOp(Qt::NotROP);
00663 
00664     // The spacing between the rects we draw.
00665     // Use the minimum of width and height to avoid painting outside the
00666     // pixmap area.
00667     //unsigned int delta(QMIN(rect.width() / actCount, rect.height() / actCount));
00668 
00669     // Support for rectangles by David
00670     unsigned int deltaX = rect.width() / actCount;
00671     unsigned int deltaY = rect.height() / actCount;
00672 
00673     for (unsigned int i = 1; i < actCount; i++) {
00674 
00675         int w = i * deltaX;
00676         int h = i * deltaY;
00677 
00678         rect.setRect(c.x() - w / 2, c.y() - h / 2, w, h);
00679 
00680         p.drawRect(rect);
00681         p.flush();
00682 
00683         usleep(actDelay);
00684 
00685         p.drawRect(rect);
00686     }
00687 }
00688 
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.4.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Sun Feb 27 22:14:46 2005 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001