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.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Sep 29 09:40:08 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003