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