File indexing completed on 2024-07-21 12:10:32

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 }