File indexing completed on 2024-04-21 03:54: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 <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 }