kdecore Library API Documentation

kiconeffect.cpp

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