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 }
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
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
00248
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
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
00388 int transColor = -1;
00389
00390
00391 for (x=0; x<img.numColors(); x++)
00392 {
00393
00394 if (qAlpha(img.color(x)) < 127)
00395 {
00396 transColor = x;
00397 break;
00398 }
00399 }
00400
00401
00402
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
00419
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
00528
00529 if (src.depth() == 1)
00530 {
00531 kdDebug(265) << "1bpp not supported!\n";
00532 return;
00533 }
00534
00535
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
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
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
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
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
00632
00633 if (actCount < 1)
00634 actCount = 1;
00635
00636 else if (actCount > 10)
00637 actCount = 10;
00638
00639
00640
00641 if (actSpeed < 1)
00642 actSpeed = 1;
00643
00644 else if (actSpeed > 100)
00645 actSpeed = 100;
00646
00647
00648
00649
00650
00651
00652 unsigned int actDelay = (1000 * (100 - actSpeed)) / actCount;
00653
00654
00655
00656 QPoint c = rect.center();
00657
00658 QPainter p(widget);
00659
00660
00661 p.setPen(QPen(Qt::black, 2, Qt::DotLine));
00662 p.setRasterOp(Qt::NotROP);
00663
00664
00665
00666
00667
00668
00669
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