00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
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
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
00134
00135
00136
00137
00138
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
00255
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
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
00395 int transColor = -1;
00396
00397
00398 for (x=0; x<img.numColors(); x++)
00399 {
00400
00401 if (qAlpha(img.color(x)) < 127)
00402 {
00403 transColor = x;
00404 break;
00405 }
00406 }
00407
00408
00409
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
00426
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
00535
00536 if (src.depth() == 1)
00537 {
00538 kdDebug(265) << "1bpp not supported!\n";
00539 return;
00540 }
00541
00542
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
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
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
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
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
00639
00640 if (actCount < 1)
00641 actCount = 1;
00642
00643 else if (actCount > 10)
00644 actCount = 10;
00645
00646
00647
00648 if (actSpeed < 1)
00649 actSpeed = 1;
00650
00651 else if (actSpeed > 100)
00652 actSpeed = 100;
00653
00654
00655
00656
00657
00658
00659 unsigned int actDelay = (1000 * (100 - actSpeed)) / actCount;
00660
00661
00662
00663 QPoint c = rect.center();
00664
00665 QPainter p(widget);
00666
00667
00668 p.setPen(QPen(Qt::black, 2, Qt::DotLine));
00669 p.setRasterOp(Qt::NotROP);
00670
00671
00672
00673
00674
00675
00676
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