File indexing completed on 2023-09-24 08:04:18
0001 /* vi: ts=8 sts=4 sw=4 0002 0003 This file is part of the KDE project, module kdecore. 0004 SPDX-FileCopyrightText: 2000 Geert Jansen <jansen@kde.org> 0005 SPDX-FileCopyrightText: 2007 Daniel M. Duley <daniel.duley@verizon.net> 0006 0007 with minor additions and based on ideas from 0008 SPDX-FileContributor: Torsten Rahn <torsten@kde.org> 0009 0010 SPDX-License-Identifier: LGPL-2.0-only 0011 */ 0012 0013 #include "kiconeffect.h" 0014 #include "debug.h" 0015 0016 #include <KColorScheme> 0017 #include <KConfigGroup> 0018 #include <kicontheme.h> 0019 0020 #include <QDebug> 0021 #include <QSysInfo> 0022 0023 #include <qplatformdefs.h> 0024 0025 #include <math.h> 0026 0027 class KIconEffectPrivate 0028 { 0029 public: 0030 // http://en.cppreference.com/w/cpp/language/zero_initialization 0031 KIconEffectPrivate() 0032 : effect{{}} 0033 , value{{}} 0034 , color{{}} 0035 , trans{{}} 0036 , key{{}} 0037 , color2{{}} 0038 { 0039 } 0040 0041 public: 0042 int effect[KIconLoader::LastGroup][KIconLoader::LastState]; 0043 float value[KIconLoader::LastGroup][KIconLoader::LastState]; 0044 QColor color[KIconLoader::LastGroup][KIconLoader::LastState]; 0045 bool trans[KIconLoader::LastGroup][KIconLoader::LastState]; 0046 QString key[KIconLoader::LastGroup][KIconLoader::LastState]; 0047 QColor color2[KIconLoader::LastGroup][KIconLoader::LastState]; 0048 }; 0049 0050 KIconEffect::KIconEffect() 0051 : d(new KIconEffectPrivate) 0052 { 0053 init(); 0054 } 0055 0056 KIconEffect::~KIconEffect() = default; 0057 0058 void KIconEffect::init() 0059 { 0060 KSharedConfig::Ptr config = KSharedConfig::openConfig(); 0061 0062 int i; 0063 int j; 0064 int effect = -1; 0065 // FIXME: this really should be using KIconLoader::metaObject() to guarantee synchronization 0066 // performance wise it's also practically guaranteed to be faster 0067 QStringList groups; 0068 groups += QStringLiteral("Desktop"); 0069 groups += QStringLiteral("Toolbar"); 0070 groups += QStringLiteral("MainToolbar"); 0071 groups += QStringLiteral("Small"); 0072 groups += QStringLiteral("Panel"); 0073 groups += QStringLiteral("Dialog"); 0074 0075 QStringList states; 0076 states += QStringLiteral("Default"); 0077 states += QStringLiteral("Active"); 0078 states += QStringLiteral("Disabled"); 0079 0080 QStringList::ConstIterator it; 0081 QStringList::ConstIterator it2; 0082 QString _togray(QStringLiteral("togray")); 0083 QString _colorize(QStringLiteral("colorize")); 0084 QString _desaturate(QStringLiteral("desaturate")); 0085 QString _togamma(QStringLiteral("togamma")); 0086 QString _none(QStringLiteral("none")); 0087 QString _tomonochrome(QStringLiteral("tomonochrome")); 0088 0089 for (it = groups.constBegin(), i = 0; it != groups.constEnd(); ++it, ++i) { 0090 // Default effects 0091 d->effect[i][0] = NoEffect; 0092 d->effect[i][1] = ((i == 0) || (i == 4)) ? ToGamma : NoEffect; 0093 d->effect[i][2] = ToGray; 0094 0095 d->trans[i][0] = false; 0096 d->trans[i][1] = false; 0097 d->trans[i][2] = true; 0098 d->value[i][0] = 1.0; 0099 d->value[i][1] = ((i == 0) || (i == 4)) ? 0.7 : 1.0; 0100 d->value[i][2] = 1.0; 0101 d->color[i][0] = QColor(144, 128, 248); 0102 d->color[i][1] = QColor(169, 156, 255); 0103 d->color[i][2] = QColor(34, 202, 0); 0104 d->color2[i][0] = QColor(0, 0, 0); 0105 d->color2[i][1] = QColor(0, 0, 0); 0106 d->color2[i][2] = QColor(0, 0, 0); 0107 0108 KConfigGroup cg(config, *it + QStringLiteral("Icons")); 0109 for (it2 = states.constBegin(), j = 0; it2 != states.constEnd(); ++it2, ++j) { 0110 QString tmp = cg.readEntry(*it2 + QStringLiteral("Effect"), QString()); 0111 if (tmp == _togray) { 0112 effect = ToGray; 0113 } else if (tmp == _colorize) { 0114 effect = Colorize; 0115 } else if (tmp == _desaturate) { 0116 effect = DeSaturate; 0117 } else if (tmp == _togamma) { 0118 effect = ToGamma; 0119 } else if (tmp == _tomonochrome) { 0120 effect = ToMonochrome; 0121 } else if (tmp == _none) { 0122 effect = NoEffect; 0123 } else { 0124 continue; 0125 } 0126 if (effect != -1) { 0127 d->effect[i][j] = effect; 0128 } 0129 d->value[i][j] = cg.readEntry(*it2 + QStringLiteral("Value"), 0.0); 0130 d->color[i][j] = cg.readEntry(*it2 + QStringLiteral("Color"), QColor()); 0131 d->color2[i][j] = cg.readEntry(*it2 + QStringLiteral("Color2"), QColor()); 0132 d->trans[i][j] = cg.readEntry(*it2 + QStringLiteral("SemiTransparent"), false); 0133 } 0134 } 0135 } 0136 0137 bool KIconEffect::hasEffect(int group, int state) const 0138 { 0139 if (group < 0 || group >= KIconLoader::LastGroup // 0140 || state < 0 || state >= KIconLoader::LastState) { 0141 return false; 0142 } 0143 0144 return d->effect[group][state] != NoEffect; 0145 } 0146 0147 QString KIconEffect::fingerprint(int group, int state) const 0148 { 0149 if (group < 0 || group >= KIconLoader::LastGroup // 0150 || state < 0 || state >= KIconLoader::LastState) { 0151 return QString(); 0152 } 0153 0154 QString cached = d->key[group][state]; 0155 if (cached.isEmpty()) { 0156 QString tmp; 0157 cached = tmp.setNum(d->effect[group][state]); 0158 cached += QLatin1Char(':'); 0159 cached += tmp.setNum(d->value[group][state]); 0160 cached += QLatin1Char(':'); 0161 cached += d->trans[group][state] ? QLatin1String("trans") : QLatin1String("notrans"); 0162 if (d->effect[group][state] == Colorize || d->effect[group][state] == ToMonochrome) { 0163 cached += QLatin1Char(':'); 0164 cached += d->color[group][state].name(); 0165 } 0166 if (d->effect[group][state] == ToMonochrome) { 0167 cached += QLatin1Char(':'); 0168 cached += d->color2[group][state].name(); 0169 } 0170 0171 d->key[group][state] = cached; 0172 } 0173 0174 return cached; 0175 } 0176 0177 QImage KIconEffect::apply(const QImage &image, int group, int state) const 0178 { 0179 if (state >= KIconLoader::LastState) { 0180 qCWarning(KICONTHEMES) << "Invalid icon state:" << state << ", should be one of KIconLoader::States"; 0181 return image; 0182 } 0183 if (group >= KIconLoader::LastGroup) { 0184 qCWarning(KICONTHEMES) << "Invalid icon group:" << group << ", should be one of KIconLoader::Group"; 0185 return image; 0186 } 0187 return apply(image, d->effect[group][state], d->value[group][state], d->color[group][state], d->color2[group][state], d->trans[group][state]); 0188 } 0189 0190 QImage KIconEffect::apply(const QImage &image, int effect, float value, const QColor &col, bool trans) const 0191 { 0192 return apply(image, effect, value, col, KColorScheme(QPalette::Active, KColorScheme::View).background().color(), trans); 0193 } 0194 0195 QImage KIconEffect::apply(const QImage &img, int effect, float value, const QColor &col, const QColor &col2, bool trans) const 0196 { 0197 QImage image = img; 0198 if (effect >= LastEffect) { 0199 qCWarning(KICONTHEMES) << "Invalid icon effect:" << effect << ", should be one of KIconLoader::Effects"; 0200 return image; 0201 } 0202 if (value > 1.0) { 0203 value = 1.0; 0204 } else if (value < 0.0) { 0205 value = 0.0; 0206 } 0207 switch (effect) { 0208 case ToGray: 0209 toGray(image, value); 0210 break; 0211 case DeSaturate: 0212 deSaturate(image, value); 0213 break; 0214 case Colorize: 0215 colorize(image, col, value); 0216 break; 0217 case ToGamma: 0218 toGamma(image, value); 0219 break; 0220 case ToMonochrome: 0221 toMonochrome(image, col, col2, value); 0222 break; 0223 } 0224 if (trans == true) { 0225 semiTransparent(image); 0226 } 0227 return image; 0228 } 0229 0230 QPixmap KIconEffect::apply(const QPixmap &pixmap, int group, int state) const 0231 { 0232 if (state >= KIconLoader::LastState) { 0233 qCWarning(KICONTHEMES) << "Invalid icon state:" << state << ", should be one of KIconLoader::States"; 0234 return pixmap; 0235 } 0236 if (group >= KIconLoader::LastGroup) { 0237 qCWarning(KICONTHEMES) << "Invalid icon group:" << group << ", should be one of KIconLoader::Group"; 0238 return pixmap; 0239 } 0240 return apply(pixmap, d->effect[group][state], d->value[group][state], d->color[group][state], d->color2[group][state], d->trans[group][state]); 0241 } 0242 0243 QPixmap KIconEffect::apply(const QPixmap &pixmap, int effect, float value, const QColor &col, bool trans) const 0244 { 0245 return apply(pixmap, effect, value, col, KColorScheme(QPalette::Active, KColorScheme::View).background().color(), trans); 0246 } 0247 0248 QPixmap KIconEffect::apply(const QPixmap &pixmap, int effect, float value, const QColor &col, const QColor &col2, bool trans) const 0249 { 0250 QPixmap result; 0251 0252 if (effect >= LastEffect) { 0253 qCWarning(KICONTHEMES) << "Invalid icon effect:" << effect << ", should be one of KIconLoader::Effects"; 0254 return result; 0255 } 0256 0257 if ((trans == true) && (effect == NoEffect)) { 0258 result = pixmap; 0259 semiTransparent(result); 0260 } else if (effect != NoEffect) { 0261 QImage tmpImg = pixmap.toImage(); 0262 tmpImg = apply(tmpImg, effect, value, col, col2, trans); 0263 result = QPixmap::fromImage(tmpImg); 0264 } else { 0265 result = pixmap; 0266 } 0267 0268 return result; 0269 } 0270 0271 struct KIEImgEdit { 0272 QImage &img; 0273 QVector<QRgb> colors; 0274 unsigned int *data; 0275 unsigned int pixels; 0276 0277 KIEImgEdit(QImage &_img) 0278 : img(_img) 0279 { 0280 if (img.depth() > 8) { 0281 // Code using data and pixels assumes that the pixels are stored 0282 // in 32bit values and that the image is not premultiplied 0283 if ((img.format() != QImage::Format_ARGB32) // 0284 && (img.format() != QImage::Format_RGB32)) { 0285 img = img.convertToFormat(QImage::Format_ARGB32); 0286 } 0287 data = (unsigned int *)img.bits(); 0288 pixels = img.width() * img.height(); 0289 } else { 0290 pixels = img.colorCount(); 0291 colors = img.colorTable(); 0292 data = (unsigned int *)colors.data(); 0293 } 0294 } 0295 0296 ~KIEImgEdit() 0297 { 0298 if (img.depth() <= 8) { 0299 img.setColorTable(colors); 0300 } 0301 } 0302 0303 KIEImgEdit(const KIEImgEdit &) = delete; 0304 KIEImgEdit &operator=(const KIEImgEdit &) = delete; 0305 }; 0306 0307 // Taken from KImageEffect. We don't want to link kdecore to kdeui! As long 0308 // as this code is not too big, it doesn't seem much of a problem to me. 0309 0310 void KIconEffect::toGray(QImage &img, float value) 0311 { 0312 if (value == 0.0) { 0313 return; 0314 } 0315 0316 KIEImgEdit ii(img); 0317 QRgb *data = ii.data; 0318 QRgb *end = data + ii.pixels; 0319 0320 unsigned char gray; 0321 if (value == 1.0) { 0322 while (data != end) { 0323 gray = qGray(*data); 0324 *data = qRgba(gray, gray, gray, qAlpha(*data)); 0325 ++data; 0326 } 0327 } else { 0328 unsigned char val = (unsigned char)(255.0 * value); 0329 while (data != end) { 0330 gray = qGray(*data); 0331 *data = qRgba((val * gray + (0xFF - val) * qRed(*data)) >> 8, 0332 (val * gray + (0xFF - val) * qGreen(*data)) >> 8, 0333 (val * gray + (0xFF - val) * qBlue(*data)) >> 8, 0334 qAlpha(*data)); 0335 ++data; 0336 } 0337 } 0338 } 0339 0340 void KIconEffect::colorize(QImage &img, const QColor &col, float value) 0341 { 0342 if (value == 0.0) { 0343 return; 0344 } 0345 0346 KIEImgEdit ii(img); 0347 QRgb *data = ii.data; 0348 QRgb *end = data + ii.pixels; 0349 0350 float rcol = col.red(); 0351 float gcol = col.green(); 0352 float bcol = col.blue(); 0353 unsigned char red; 0354 unsigned char green; 0355 unsigned char blue; 0356 unsigned char gray; 0357 unsigned char val = (unsigned char)(255.0 * value); 0358 while (data != end) { 0359 gray = qGray(*data); 0360 if (gray < 128) { 0361 red = static_cast<unsigned char>(rcol / 128 * gray); 0362 green = static_cast<unsigned char>(gcol / 128 * gray); 0363 blue = static_cast<unsigned char>(bcol / 128 * gray); 0364 } else if (gray > 128) { 0365 red = static_cast<unsigned char>((gray - 128) * (2 - rcol / 128) + rcol - 1); 0366 green = static_cast<unsigned char>((gray - 128) * (2 - gcol / 128) + gcol - 1); 0367 blue = static_cast<unsigned char>((gray - 128) * (2 - bcol / 128) + bcol - 1); 0368 } else { 0369 red = static_cast<unsigned char>(rcol); 0370 green = static_cast<unsigned char>(gcol); 0371 blue = static_cast<unsigned char>(bcol); 0372 } 0373 0374 *data = qRgba((val * red + (0xFF - val) * qRed(*data)) >> 8, 0375 (val * green + (0xFF - val) * qGreen(*data)) >> 8, 0376 (val * blue + (0xFF - val) * qBlue(*data)) >> 8, 0377 qAlpha(*data)); 0378 ++data; 0379 } 0380 } 0381 0382 void KIconEffect::toMonochrome(QImage &img, const QColor &black, const QColor &white, float value) 0383 { 0384 if (value == 0.0) { 0385 return; 0386 } 0387 0388 KIEImgEdit ii(img); 0389 QRgb *data = ii.data; 0390 QRgb *end = data + ii.pixels; 0391 0392 // Step 1: determine the average brightness 0393 double values = 0.0; 0394 double sum = 0.0; 0395 bool grayscale = true; 0396 while (data != end) { 0397 sum += qGray(*data) * qAlpha(*data) + 255 * (255 - qAlpha(*data)); 0398 values += 255; 0399 if ((qRed(*data) != qGreen(*data)) || (qGreen(*data) != qBlue(*data))) { 0400 grayscale = false; 0401 } 0402 ++data; 0403 } 0404 double medium = sum / values; 0405 0406 // Step 2: Modify the image 0407 unsigned char val = (unsigned char)(255.0 * value); 0408 int rw = white.red(); 0409 int gw = white.green(); 0410 int bw = white.blue(); 0411 int rb = black.red(); 0412 int gb = black.green(); 0413 int bb = black.blue(); 0414 data = ii.data; 0415 0416 if (grayscale) { 0417 while (data != end) { 0418 if (qRed(*data) <= medium) { 0419 *data = qRgba((val * rb + (0xFF - val) * qRed(*data)) >> 8, 0420 (val * gb + (0xFF - val) * qGreen(*data)) >> 8, 0421 (val * bb + (0xFF - val) * qBlue(*data)) >> 8, 0422 qAlpha(*data)); 0423 } else { 0424 *data = qRgba((val * rw + (0xFF - val) * qRed(*data)) >> 8, 0425 (val * gw + (0xFF - val) * qGreen(*data)) >> 8, 0426 (val * bw + (0xFF - val) * qBlue(*data)) >> 8, 0427 qAlpha(*data)); 0428 } 0429 ++data; 0430 } 0431 } else { 0432 while (data != end) { 0433 if (qGray(*data) <= medium) { 0434 *data = qRgba((val * rb + (0xFF - val) * qRed(*data)) >> 8, 0435 (val * gb + (0xFF - val) * qGreen(*data)) >> 8, 0436 (val * bb + (0xFF - val) * qBlue(*data)) >> 8, 0437 qAlpha(*data)); 0438 } else { 0439 *data = qRgba((val * rw + (0xFF - val) * qRed(*data)) >> 8, 0440 (val * gw + (0xFF - val) * qGreen(*data)) >> 8, 0441 (val * bw + (0xFF - val) * qBlue(*data)) >> 8, 0442 qAlpha(*data)); 0443 } 0444 ++data; 0445 } 0446 } 0447 } 0448 0449 void KIconEffect::deSaturate(QImage &img, float value) 0450 { 0451 if (value == 0.0) { 0452 return; 0453 } 0454 0455 KIEImgEdit ii(img); 0456 QRgb *data = ii.data; 0457 QRgb *end = data + ii.pixels; 0458 0459 QColor color; 0460 int h; 0461 int s; 0462 int v; 0463 while (data != end) { 0464 color.setRgb(*data); 0465 color.getHsv(&h, &s, &v); 0466 color.setHsv(h, (int)(s * (1.0 - value) + 0.5), v); 0467 *data = qRgba(color.red(), color.green(), color.blue(), qAlpha(*data)); 0468 ++data; 0469 } 0470 } 0471 0472 void KIconEffect::toGamma(QImage &img, float value) 0473 { 0474 KIEImgEdit ii(img); 0475 QRgb *data = ii.data; 0476 QRgb *end = data + ii.pixels; 0477 0478 float gamma = 1 / (2 * value + 0.5); 0479 while (data != end) { 0480 *data = qRgba(static_cast<unsigned char>(pow(static_cast<float>(qRed(*data)) / 255, gamma) * 255), 0481 static_cast<unsigned char>(pow(static_cast<float>(qGreen(*data)) / 255, gamma) * 255), 0482 static_cast<unsigned char>(pow(static_cast<float>(qBlue(*data)) / 255, gamma) * 255), 0483 qAlpha(*data)); 0484 ++data; 0485 } 0486 } 0487 0488 void KIconEffect::semiTransparent(QImage &img) 0489 { 0490 if (img.depth() == 32) { 0491 if (img.format() == QImage::Format_ARGB32_Premultiplied) { 0492 img = img.convertToFormat(QImage::Format_ARGB32); 0493 } 0494 int width = img.width(); 0495 int height = img.height(); 0496 0497 unsigned char *line; 0498 for (int y = 0; y < height; ++y) { 0499 if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { 0500 line = img.scanLine(y); 0501 } else { 0502 line = img.scanLine(y) + 3; 0503 } 0504 for (int x = 0; x < width; ++x) { 0505 *line >>= 1; 0506 line += 4; 0507 } 0508 } 0509 } else if (img.depth() == 8) { 0510 // not running on 8 bit, we can safely install a new colorTable 0511 QVector<QRgb> colorTable = img.colorTable(); 0512 for (int i = 0; i < colorTable.size(); ++i) { 0513 colorTable[i] = (colorTable[i] & 0x00ffffff) | ((colorTable[i] & 0xfe000000) >> 1); 0514 } 0515 img.setColorTable(colorTable); 0516 } else { 0517 // Insert transparent pixel into the clut. 0518 int transColor = -1; 0519 0520 // search for a color that is already transparent 0521 for (int x = 0; x < img.colorCount(); ++x) { 0522 // try to find already transparent pixel 0523 if (qAlpha(img.color(x)) < 127) { 0524 transColor = x; 0525 break; 0526 } 0527 } 0528 0529 // FIXME: image must have transparency 0530 if (transColor < 0 || transColor >= img.colorCount()) { 0531 return; 0532 } 0533 0534 img.setColor(transColor, 0); 0535 unsigned char *line; 0536 if (img.depth() == 8) { 0537 for (int y = 0; y < img.height(); ++y) { 0538 line = img.scanLine(y); 0539 for (int x = (y % 2); x < img.width(); x += 2) { 0540 line[x] = transColor; 0541 } 0542 } 0543 } else { 0544 const bool setOn = (transColor != 0); 0545 if (img.format() == QImage::Format_MonoLSB) { 0546 for (int y = 0; y < img.height(); ++y) { 0547 line = img.scanLine(y); 0548 for (int x = (y % 2); x < img.width(); x += 2) { 0549 if (!setOn) { 0550 *(line + (x >> 3)) &= ~(1 << (x & 7)); 0551 } else { 0552 *(line + (x >> 3)) |= (1 << (x & 7)); 0553 } 0554 } 0555 } 0556 } else { 0557 for (int y = 0; y < img.height(); ++y) { 0558 line = img.scanLine(y); 0559 for (int x = (y % 2); x < img.width(); x += 2) { 0560 if (!setOn) { 0561 *(line + (x >> 3)) &= ~(1 << (7 - (x & 7))); 0562 } else { 0563 *(line + (x >> 3)) |= (1 << (7 - (x & 7))); 0564 } 0565 } 0566 } 0567 } 0568 } 0569 } 0570 } 0571 0572 void KIconEffect::semiTransparent(QPixmap &pix) 0573 { 0574 QImage img = pix.toImage(); 0575 semiTransparent(img); 0576 pix = QPixmap::fromImage(img); 0577 } 0578 0579 QImage KIconEffect::doublePixels(const QImage &src) const 0580 { 0581 int w = src.width(); 0582 int h = src.height(); 0583 0584 QImage dst(w * 2, h * 2, src.format()); 0585 0586 if (src.depth() == 1) { 0587 qWarning() << "image depth 1 not supported"; 0588 return QImage(); 0589 } 0590 0591 int x; 0592 int y; 0593 if (src.depth() == 32) { 0594 QRgb *l1; 0595 QRgb *l2; 0596 for (y = 0; y < h; ++y) { 0597 l1 = (QRgb *)src.scanLine(y); 0598 l2 = (QRgb *)dst.scanLine(y * 2); 0599 for (x = 0; x < w; ++x) { 0600 l2[x * 2] = l2[x * 2 + 1] = l1[x]; 0601 } 0602 memcpy(dst.scanLine(y * 2 + 1), l2, dst.bytesPerLine()); 0603 } 0604 } else { 0605 for (x = 0; x < src.colorCount(); ++x) { 0606 dst.setColor(x, src.color(x)); 0607 } 0608 0609 const unsigned char *l1; 0610 unsigned char *l2; 0611 for (y = 0; y < h; ++y) { 0612 l1 = src.scanLine(y); 0613 l2 = dst.scanLine(y * 2); 0614 for (x = 0; x < w; ++x) { 0615 l2[x * 2] = l1[x]; 0616 l2[x * 2 + 1] = l1[x]; 0617 } 0618 memcpy(dst.scanLine(y * 2 + 1), l2, dst.bytesPerLine()); 0619 } 0620 } 0621 return dst; 0622 } 0623 0624 void KIconEffect::overlay(QImage &src, QImage &overlay) 0625 { 0626 if (src.depth() != overlay.depth()) { 0627 qWarning() << "Image depth src (" << src.depth() << ") != overlay " 0628 << "(" << overlay.depth() << ")!"; 0629 return; 0630 } 0631 if (src.size() != overlay.size()) { 0632 qWarning() << "Image size src != overlay"; 0633 return; 0634 } 0635 if (src.format() == QImage::Format_ARGB32_Premultiplied) { 0636 src = src.convertToFormat(QImage::Format_ARGB32); 0637 } 0638 0639 if (overlay.format() == QImage::Format_RGB32) { 0640 qWarning() << "Overlay doesn't have alpha buffer!"; 0641 return; 0642 } else if (overlay.format() == QImage::Format_ARGB32_Premultiplied) { 0643 overlay = overlay.convertToFormat(QImage::Format_ARGB32); 0644 } 0645 0646 int i; 0647 int j; 0648 0649 // We don't do 1 bpp 0650 0651 if (src.depth() == 1) { 0652 qWarning() << "1bpp not supported!"; 0653 return; 0654 } 0655 0656 // Overlay at 8 bpp doesn't use alpha blending 0657 0658 if (src.depth() == 8) { 0659 if (src.colorCount() + overlay.colorCount() > 255) { 0660 qWarning() << "Too many colors in src + overlay!"; 0661 return; 0662 } 0663 0664 // Find transparent pixel in overlay 0665 int trans; 0666 for (trans = 0; trans < overlay.colorCount(); trans++) { 0667 if (qAlpha(overlay.color(trans)) == 0) { 0668 qWarning() << "transparent pixel found at " << trans; 0669 break; 0670 } 0671 } 0672 if (trans == overlay.colorCount()) { 0673 qWarning() << "transparent pixel not found!"; 0674 return; 0675 } 0676 0677 // Merge color tables 0678 int nc = src.colorCount(); 0679 src.setColorCount(nc + overlay.colorCount()); 0680 for (i = 0; i < overlay.colorCount(); ++i) { 0681 src.setColor(nc + i, overlay.color(i)); 0682 } 0683 0684 // Overwrite nontransparent pixels. 0685 unsigned char *oline; 0686 unsigned char *sline; 0687 for (i = 0; i < src.height(); ++i) { 0688 oline = overlay.scanLine(i); 0689 sline = src.scanLine(i); 0690 for (j = 0; j < src.width(); ++j) { 0691 if (oline[j] != trans) { 0692 sline[j] = oline[j] + nc; 0693 } 0694 } 0695 } 0696 } 0697 0698 // Overlay at 32 bpp does use alpha blending 0699 0700 if (src.depth() == 32) { 0701 QRgb *oline; 0702 QRgb *sline; 0703 int r1; 0704 int g1; 0705 int b1; 0706 int a1; 0707 int r2; 0708 int g2; 0709 int b2; 0710 int a2; 0711 0712 for (i = 0; i < src.height(); ++i) { 0713 oline = (QRgb *)overlay.scanLine(i); 0714 sline = (QRgb *)src.scanLine(i); 0715 0716 for (j = 0; j < src.width(); ++j) { 0717 r1 = qRed(oline[j]); 0718 g1 = qGreen(oline[j]); 0719 b1 = qBlue(oline[j]); 0720 a1 = qAlpha(oline[j]); 0721 0722 r2 = qRed(sline[j]); 0723 g2 = qGreen(sline[j]); 0724 b2 = qBlue(sline[j]); 0725 a2 = qAlpha(sline[j]); 0726 0727 r2 = (a1 * r1 + (0xff - a1) * r2) >> 8; 0728 g2 = (a1 * g1 + (0xff - a1) * g2) >> 8; 0729 b2 = (a1 * b1 + (0xff - a1) * b2) >> 8; 0730 a2 = qMax(a1, a2); 0731 0732 sline[j] = qRgba(r2, g2, b2, a2); 0733 } 0734 } 0735 } 0736 }