1/* vi: ts=8 sts=4 sw=4
2
3 This file is part of the KDE project, module kdecore.
4 SPDX-FileCopyrightText: 2000 Geert Jansen <jansen@kde.org>
5 SPDX-FileCopyrightText: 2007 Daniel M. Duley <daniel.duley@verizon.net>
6
7 with minor additions and based on ideas from
8 SPDX-FileContributor: Torsten Rahn <torsten@kde.org>
9
10 SPDX-License-Identifier: LGPL-2.0-only
11*/
12
13#include "kiconeffect.h"
14#include "debug.h"
15
16#include <KColorScheme>
17#include <KConfigGroup>
18#include <KSharedConfig>
19#include <kicontheme.h>
20
21#include <QDebug>
22#include <QPalette>
23#include <QSysInfo>
24
25#include <qplatformdefs.h>
26
27#include <math.h>
28
29class KIconEffectPrivate
30{
31public:
32 // http://en.cppreference.com/w/cpp/language/zero_initialization
33 KIconEffectPrivate()
34 : effect{{}}
35 , value{{}}
36 , color{{}}
37 , trans{{}}
38 , key{{}}
39 , color2{{}}
40 {
41 }
42
43public:
44 int effect[KIconLoader::LastGroup][KIconLoader::LastState];
45 float value[KIconLoader::LastGroup][KIconLoader::LastState];
46 QColor color[KIconLoader::LastGroup][KIconLoader::LastState];
47 bool trans[KIconLoader::LastGroup][KIconLoader::LastState];
48 QString key[KIconLoader::LastGroup][KIconLoader::LastState];
49 QColor color2[KIconLoader::LastGroup][KIconLoader::LastState];
50};
51
52KIconEffect::KIconEffect()
53 : d(new KIconEffectPrivate)
54{
55 init();
56}
57
58KIconEffect::~KIconEffect() = default;
59
60void KIconEffect::init()
61{
62 KSharedConfig::Ptr config = KSharedConfig::openConfig();
63
64 int i;
65 int j;
66 int effect = -1;
67 // FIXME: this really should be using KIconLoader::metaObject() to guarantee synchronization
68 // performance wise it's also practically guaranteed to be faster
69 QStringList groups;
70 groups += QStringLiteral("Desktop");
71 groups += QStringLiteral("Toolbar");
72 groups += QStringLiteral("MainToolbar");
73 groups += QStringLiteral("Small");
74 groups += QStringLiteral("Panel");
75 groups += QStringLiteral("Dialog");
76
77 QStringList states;
78 states += QStringLiteral("Default");
79 states += QStringLiteral("Active");
80 states += QStringLiteral("Disabled");
81
82 QStringList::ConstIterator it;
83 QStringList::ConstIterator it2;
84 QString _togray(QStringLiteral("togray"));
85 QString _colorize(QStringLiteral("colorize"));
86 QString _desaturate(QStringLiteral("desaturate"));
87 QString _togamma(QStringLiteral("togamma"));
88 QString _none(QStringLiteral("none"));
89 QString _tomonochrome(QStringLiteral("tomonochrome"));
90
91 for (it = groups.constBegin(), i = 0; it != groups.constEnd(); ++it, ++i) {
92 // Default effects
93 d->effect[i][0] = NoEffect;
94 d->effect[i][1] = ((i == 0) || (i == 4)) ? ToGamma : NoEffect;
95 d->effect[i][2] = ToGray;
96
97 d->trans[i][0] = false;
98 d->trans[i][1] = false;
99 d->trans[i][2] = true;
100 d->value[i][0] = 1.0;
101 d->value[i][1] = ((i == 0) || (i == 4)) ? 0.7 : 1.0;
102 d->value[i][2] = 1.0;
103 d->color[i][0] = QColor(144, 128, 248);
104 d->color[i][1] = QColor(169, 156, 255);
105 d->color[i][2] = QColor(34, 202, 0);
106 d->color2[i][0] = QColor(0, 0, 0);
107 d->color2[i][1] = QColor(0, 0, 0);
108 d->color2[i][2] = QColor(0, 0, 0);
109
110 KConfigGroup cg(config, *it + QStringLiteral("Icons"));
111 for (it2 = states.constBegin(), j = 0; it2 != states.constEnd(); ++it2, ++j) {
112 QString tmp = cg.readEntry(key: *it2 + QStringLiteral("Effect"), aDefault: QString());
113 if (tmp == _togray) {
114 effect = ToGray;
115 } else if (tmp == _colorize) {
116 effect = Colorize;
117 } else if (tmp == _desaturate) {
118 effect = DeSaturate;
119 } else if (tmp == _togamma) {
120 effect = ToGamma;
121 } else if (tmp == _tomonochrome) {
122 effect = ToMonochrome;
123 } else if (tmp == _none) {
124 effect = NoEffect;
125 } else {
126 continue;
127 }
128 if (effect != -1) {
129 d->effect[i][j] = effect;
130 }
131 d->value[i][j] = cg.readEntry(key: *it2 + QStringLiteral("Value"), aDefault: 0.0);
132 d->color[i][j] = cg.readEntry(key: *it2 + QStringLiteral("Color"), aDefault: QColor());
133 d->color2[i][j] = cg.readEntry(key: *it2 + QStringLiteral("Color2"), aDefault: QColor());
134 d->trans[i][j] = cg.readEntry(key: *it2 + QStringLiteral("SemiTransparent"), aDefault: false);
135 }
136 }
137}
138
139bool KIconEffect::hasEffect(int group, int state) const
140{
141 if (group < 0 || group >= KIconLoader::LastGroup //
142 || state < 0 || state >= KIconLoader::LastState) {
143 return false;
144 }
145
146 return d->effect[group][state] != NoEffect;
147}
148
149QString KIconEffect::fingerprint(int group, int state) const
150{
151 if (group < 0 || group >= KIconLoader::LastGroup //
152 || state < 0 || state >= KIconLoader::LastState) {
153 return QString();
154 }
155
156 QString cached = d->key[group][state];
157 if (cached.isEmpty()) {
158 QString tmp;
159 cached = tmp.setNum(n: d->effect[group][state]);
160 cached += QLatin1Char(':');
161 cached += tmp.setNum(n: d->value[group][state]);
162 cached += QLatin1Char(':');
163 cached += d->trans[group][state] ? QLatin1String("trans") : QLatin1String("notrans");
164 if (d->effect[group][state] == Colorize || d->effect[group][state] == ToMonochrome) {
165 cached += QLatin1Char(':');
166 cached += d->color[group][state].name();
167 }
168 if (d->effect[group][state] == ToMonochrome) {
169 cached += QLatin1Char(':');
170 cached += d->color2[group][state].name();
171 }
172
173 d->key[group][state] = cached;
174 }
175
176 return cached;
177}
178
179QImage KIconEffect::apply(const QImage &image, int group, int state) const
180{
181 if (state >= KIconLoader::LastState) {
182 qCWarning(KICONTHEMES) << "Invalid icon state:" << state << ", should be one of KIconLoader::States";
183 return image;
184 }
185 if (group >= KIconLoader::LastGroup) {
186 qCWarning(KICONTHEMES) << "Invalid icon group:" << group << ", should be one of KIconLoader::Group";
187 return image;
188 }
189 return apply(src: image, effect: d->effect[group][state], value: d->value[group][state], rgb: d->color[group][state], rgb2: d->color2[group][state], trans: d->trans[group][state]);
190}
191
192QImage KIconEffect::apply(const QImage &image, int effect, float value, const QColor &col, bool trans) const
193{
194 return apply(src: image, effect, value, rgb: col, rgb2: KColorScheme(QPalette::Active, KColorScheme::View).background().color(), trans);
195}
196
197QImage KIconEffect::apply(const QImage &img, int effect, float value, const QColor &col, const QColor &col2, bool trans) const
198{
199 QImage image = img;
200 if (effect >= LastEffect) {
201 qCWarning(KICONTHEMES) << "Invalid icon effect:" << effect << ", should be one of KIconLoader::Effects";
202 return image;
203 }
204 if (value > 1.0) {
205 value = 1.0;
206 } else if (value < 0.0) {
207 value = 0.0;
208 }
209 switch (effect) {
210 case ToGray:
211 toGray(image, value);
212 break;
213 case DeSaturate:
214 deSaturate(image, value);
215 break;
216 case Colorize:
217 colorize(image, col, value);
218 break;
219 case ToGamma:
220 toGamma(image, value);
221 break;
222 case ToMonochrome:
223 toMonochrome(image, black: col, white: col2, value);
224 break;
225 }
226 if (trans == true) {
227 semiTransparent(image);
228 }
229 return image;
230}
231
232QPixmap KIconEffect::apply(const QPixmap &pixmap, int group, int state) const
233{
234 if (state >= KIconLoader::LastState) {
235 qCWarning(KICONTHEMES) << "Invalid icon state:" << state << ", should be one of KIconLoader::States";
236 return pixmap;
237 }
238 if (group >= KIconLoader::LastGroup) {
239 qCWarning(KICONTHEMES) << "Invalid icon group:" << group << ", should be one of KIconLoader::Group";
240 return pixmap;
241 }
242 return apply(src: pixmap, effect: d->effect[group][state], value: d->value[group][state], rgb: d->color[group][state], rgb2: d->color2[group][state], trans: d->trans[group][state]);
243}
244
245QPixmap KIconEffect::apply(const QPixmap &pixmap, int effect, float value, const QColor &col, bool trans) const
246{
247 return apply(src: pixmap, effect, value, rgb: col, rgb2: KColorScheme(QPalette::Active, KColorScheme::View).background().color(), trans);
248}
249
250QPixmap KIconEffect::apply(const QPixmap &pixmap, int effect, float value, const QColor &col, const QColor &col2, bool trans) const
251{
252 QPixmap result;
253
254 if (effect >= LastEffect) {
255 qCWarning(KICONTHEMES) << "Invalid icon effect:" << effect << ", should be one of KIconLoader::Effects";
256 return result;
257 }
258
259 if ((trans == true) && (effect == NoEffect)) {
260 result = pixmap;
261 semiTransparent(pixmap&: result);
262 } else if (effect != NoEffect) {
263 QImage tmpImg = pixmap.toImage();
264 tmpImg = apply(img: tmpImg, effect, value, col, col2, trans);
265 result = QPixmap::fromImage(image: std::move(tmpImg));
266 } else {
267 result = pixmap;
268 }
269
270 return result;
271}
272
273struct KIEImgEdit {
274 QImage &img;
275 QList<QRgb> colors;
276 unsigned int *data;
277 unsigned int pixels;
278
279 KIEImgEdit(QImage &_img)
280 : img(_img)
281 {
282 if (img.depth() > 8) {
283 // Code using data and pixels assumes that the pixels are stored
284 // in 32bit values and that the image is not premultiplied
285 if ((img.format() != QImage::Format_ARGB32) //
286 && (img.format() != QImage::Format_RGB32)) {
287 img.convertTo(f: QImage::Format_ARGB32);
288 }
289 data = (unsigned int *)img.bits();
290 pixels = img.width() * img.height();
291 } else {
292 pixels = img.colorCount();
293 colors = img.colorTable();
294 data = (unsigned int *)colors.data();
295 }
296 }
297
298 ~KIEImgEdit()
299 {
300 if (img.depth() <= 8) {
301 img.setColorTable(colors);
302 }
303 }
304
305 KIEImgEdit(const KIEImgEdit &) = delete;
306 KIEImgEdit &operator=(const KIEImgEdit &) = delete;
307};
308
309// Taken from KImageEffect. We don't want to link kdecore to kdeui! As long
310// as this code is not too big, it doesn't seem much of a problem to me.
311
312void KIconEffect::toGray(QImage &img, float value)
313{
314 if (value == 0.0) {
315 return;
316 }
317
318 KIEImgEdit ii(img);
319 QRgb *data = ii.data;
320 QRgb *end = data + ii.pixels;
321
322 unsigned char gray;
323 if (value == 1.0) {
324 while (data != end) {
325 gray = qGray(rgb: *data);
326 *data = qRgba(r: gray, g: gray, b: gray, a: qAlpha(rgb: *data));
327 ++data;
328 }
329 } else {
330 unsigned char val = (unsigned char)(255.0 * value);
331 while (data != end) {
332 gray = qGray(rgb: *data);
333 *data = qRgba(r: (val * gray + (0xFF - val) * qRed(rgb: *data)) >> 8,
334 g: (val * gray + (0xFF - val) * qGreen(rgb: *data)) >> 8,
335 b: (val * gray + (0xFF - val) * qBlue(rgb: *data)) >> 8,
336 a: qAlpha(rgb: *data));
337 ++data;
338 }
339 }
340}
341
342void KIconEffect::colorize(QImage &img, const QColor &col, float value)
343{
344 if (value == 0.0) {
345 return;
346 }
347
348 KIEImgEdit ii(img);
349 QRgb *data = ii.data;
350 QRgb *end = data + ii.pixels;
351
352 float rcol = col.red();
353 float gcol = col.green();
354 float bcol = col.blue();
355 unsigned char red;
356 unsigned char green;
357 unsigned char blue;
358 unsigned char gray;
359 unsigned char val = (unsigned char)(255.0 * value);
360 while (data != end) {
361 gray = qGray(rgb: *data);
362 if (gray < 128) {
363 red = static_cast<unsigned char>(rcol / 128 * gray);
364 green = static_cast<unsigned char>(gcol / 128 * gray);
365 blue = static_cast<unsigned char>(bcol / 128 * gray);
366 } else if (gray > 128) {
367 red = static_cast<unsigned char>((gray - 128) * (2 - rcol / 128) + rcol - 1);
368 green = static_cast<unsigned char>((gray - 128) * (2 - gcol / 128) + gcol - 1);
369 blue = static_cast<unsigned char>((gray - 128) * (2 - bcol / 128) + bcol - 1);
370 } else {
371 red = static_cast<unsigned char>(rcol);
372 green = static_cast<unsigned char>(gcol);
373 blue = static_cast<unsigned char>(bcol);
374 }
375
376 *data = qRgba(r: (val * red + (0xFF - val) * qRed(rgb: *data)) >> 8,
377 g: (val * green + (0xFF - val) * qGreen(rgb: *data)) >> 8,
378 b: (val * blue + (0xFF - val) * qBlue(rgb: *data)) >> 8,
379 a: qAlpha(rgb: *data));
380 ++data;
381 }
382}
383
384void KIconEffect::toMonochrome(QImage &img, const QColor &black, const QColor &white, float value)
385{
386 if (value == 0.0) {
387 return;
388 }
389
390 KIEImgEdit ii(img);
391 QRgb *data = ii.data;
392 QRgb *end = data + ii.pixels;
393
394 // Step 1: determine the average brightness
395 double values = 0.0;
396 double sum = 0.0;
397 bool grayscale = true;
398 while (data != end) {
399 sum += qGray(rgb: *data) * qAlpha(rgb: *data) + 255 * (255 - qAlpha(rgb: *data));
400 values += 255;
401 if ((qRed(rgb: *data) != qGreen(rgb: *data)) || (qGreen(rgb: *data) != qBlue(rgb: *data))) {
402 grayscale = false;
403 }
404 ++data;
405 }
406 double medium = sum / values;
407
408 // Step 2: Modify the image
409 unsigned char val = (unsigned char)(255.0 * value);
410 int rw = white.red();
411 int gw = white.green();
412 int bw = white.blue();
413 int rb = black.red();
414 int gb = black.green();
415 int bb = black.blue();
416 data = ii.data;
417
418 if (grayscale) {
419 while (data != end) {
420 if (qRed(rgb: *data) <= medium) {
421 *data = qRgba(r: (val * rb + (0xFF - val) * qRed(rgb: *data)) >> 8,
422 g: (val * gb + (0xFF - val) * qGreen(rgb: *data)) >> 8,
423 b: (val * bb + (0xFF - val) * qBlue(rgb: *data)) >> 8,
424 a: qAlpha(rgb: *data));
425 } else {
426 *data = qRgba(r: (val * rw + (0xFF - val) * qRed(rgb: *data)) >> 8,
427 g: (val * gw + (0xFF - val) * qGreen(rgb: *data)) >> 8,
428 b: (val * bw + (0xFF - val) * qBlue(rgb: *data)) >> 8,
429 a: qAlpha(rgb: *data));
430 }
431 ++data;
432 }
433 } else {
434 while (data != end) {
435 if (qGray(rgb: *data) <= medium) {
436 *data = qRgba(r: (val * rb + (0xFF - val) * qRed(rgb: *data)) >> 8,
437 g: (val * gb + (0xFF - val) * qGreen(rgb: *data)) >> 8,
438 b: (val * bb + (0xFF - val) * qBlue(rgb: *data)) >> 8,
439 a: qAlpha(rgb: *data));
440 } else {
441 *data = qRgba(r: (val * rw + (0xFF - val) * qRed(rgb: *data)) >> 8,
442 g: (val * gw + (0xFF - val) * qGreen(rgb: *data)) >> 8,
443 b: (val * bw + (0xFF - val) * qBlue(rgb: *data)) >> 8,
444 a: qAlpha(rgb: *data));
445 }
446 ++data;
447 }
448 }
449}
450
451void KIconEffect::deSaturate(QImage &img, float value)
452{
453 if (value == 0.0) {
454 return;
455 }
456
457 KIEImgEdit ii(img);
458 QRgb *data = ii.data;
459 QRgb *end = data + ii.pixels;
460
461 QColor color;
462 int h;
463 int s;
464 int v;
465 while (data != end) {
466 color.setRgb(*data);
467 color.getHsv(h: &h, s: &s, v: &v);
468 color.setHsv(h, s: (int)(s * (1.0 - value) + 0.5), v);
469 *data = qRgba(r: color.red(), g: color.green(), b: color.blue(), a: qAlpha(rgb: *data));
470 ++data;
471 }
472}
473
474void KIconEffect::toGamma(QImage &img, float value)
475{
476 KIEImgEdit ii(img);
477 QRgb *data = ii.data;
478 QRgb *end = data + ii.pixels;
479
480 float gamma = 1 / (2 * value + 0.5);
481 while (data != end) {
482 *data = qRgba(r: static_cast<unsigned char>(pow(x: static_cast<float>(qRed(rgb: *data)) / 255, y: gamma) * 255),
483 g: static_cast<unsigned char>(pow(x: static_cast<float>(qGreen(rgb: *data)) / 255, y: gamma) * 255),
484 b: static_cast<unsigned char>(pow(x: static_cast<float>(qBlue(rgb: *data)) / 255, y: gamma) * 255),
485 a: qAlpha(rgb: *data));
486 ++data;
487 }
488}
489
490void KIconEffect::semiTransparent(QImage &img)
491{
492 if (img.depth() == 32) {
493 if (img.format() == QImage::Format_ARGB32_Premultiplied) {
494 img.convertTo(f: QImage::Format_ARGB32);
495 }
496 int width = img.width();
497 int height = img.height();
498
499 unsigned char *line;
500 for (int y = 0; y < height; ++y) {
501 if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
502 line = img.scanLine(y);
503 } else {
504 line = img.scanLine(y) + 3;
505 }
506 for (int x = 0; x < width; ++x) {
507 *line >>= 1;
508 line += 4;
509 }
510 }
511 } else if (img.depth() == 8) {
512 // not running on 8 bit, we can safely install a new colorTable
513 QList<QRgb> colorTable = img.colorTable();
514 for (int i = 0; i < colorTable.size(); ++i) {
515 colorTable[i] = (colorTable[i] & 0x00ffffff) | ((colorTable[i] & 0xfe000000) >> 1);
516 }
517 img.setColorTable(colorTable);
518 } else {
519 // Insert transparent pixel into the clut.
520 int transColor = -1;
521
522 // search for a color that is already transparent
523 for (int x = 0; x < img.colorCount(); ++x) {
524 // try to find already transparent pixel
525 if (qAlpha(rgb: img.color(i: x)) < 127) {
526 transColor = x;
527 break;
528 }
529 }
530
531 // FIXME: image must have transparency
532 if (transColor < 0 || transColor >= img.colorCount()) {
533 return;
534 }
535
536 img.setColor(i: transColor, c: 0);
537 unsigned char *line;
538 if (img.depth() == 8) {
539 for (int y = 0; y < img.height(); ++y) {
540 line = img.scanLine(y);
541 for (int x = (y % 2); x < img.width(); x += 2) {
542 line[x] = transColor;
543 }
544 }
545 } else {
546 const bool setOn = (transColor != 0);
547 if (img.format() == QImage::Format_MonoLSB) {
548 for (int y = 0; y < img.height(); ++y) {
549 line = img.scanLine(y);
550 for (int x = (y % 2); x < img.width(); x += 2) {
551 if (!setOn) {
552 *(line + (x >> 3)) &= ~(1 << (x & 7));
553 } else {
554 *(line + (x >> 3)) |= (1 << (x & 7));
555 }
556 }
557 }
558 } else {
559 for (int y = 0; y < img.height(); ++y) {
560 line = img.scanLine(y);
561 for (int x = (y % 2); x < img.width(); x += 2) {
562 if (!setOn) {
563 *(line + (x >> 3)) &= ~(1 << (7 - (x & 7)));
564 } else {
565 *(line + (x >> 3)) |= (1 << (7 - (x & 7)));
566 }
567 }
568 }
569 }
570 }
571 }
572}
573
574void KIconEffect::semiTransparent(QPixmap &pix)
575{
576 QImage img = pix.toImage();
577 semiTransparent(img);
578 pix = QPixmap::fromImage(image: img);
579}
580
581QImage KIconEffect::doublePixels(const QImage &src) const
582{
583 int w = src.width();
584 int h = src.height();
585
586 QImage dst(w * 2, h * 2, src.format());
587
588 if (src.depth() == 1) {
589 qWarning() << "image depth 1 not supported";
590 return QImage();
591 }
592
593 int x;
594 int y;
595 if (src.depth() == 32) {
596 QRgb *l1;
597 QRgb *l2;
598 for (y = 0; y < h; ++y) {
599 l1 = (QRgb *)src.scanLine(y);
600 l2 = (QRgb *)dst.scanLine(y * 2);
601 for (x = 0; x < w; ++x) {
602 l2[x * 2] = l2[x * 2 + 1] = l1[x];
603 }
604 memcpy(dest: dst.scanLine(y * 2 + 1), src: l2, n: dst.bytesPerLine());
605 }
606 } else {
607 for (x = 0; x < src.colorCount(); ++x) {
608 dst.setColor(i: x, c: src.color(i: x));
609 }
610
611 const unsigned char *l1;
612 unsigned char *l2;
613 for (y = 0; y < h; ++y) {
614 l1 = src.scanLine(y);
615 l2 = dst.scanLine(y * 2);
616 for (x = 0; x < w; ++x) {
617 l2[x * 2] = l1[x];
618 l2[x * 2 + 1] = l1[x];
619 }
620 memcpy(dest: dst.scanLine(y * 2 + 1), src: l2, n: dst.bytesPerLine());
621 }
622 }
623 return dst;
624}
625
626void KIconEffect::overlay(QImage &src, QImage &overlay)
627{
628 if (src.depth() != overlay.depth()) {
629 qWarning() << "Image depth src (" << src.depth() << ") != overlay "
630 << "(" << overlay.depth() << ")!";
631 return;
632 }
633 if (src.size() != overlay.size()) {
634 qWarning() << "Image size src != overlay";
635 return;
636 }
637 if (src.format() == QImage::Format_ARGB32_Premultiplied) {
638 src.convertTo(f: QImage::Format_ARGB32);
639 }
640
641 if (overlay.format() == QImage::Format_RGB32) {
642 qWarning() << "Overlay doesn't have alpha buffer!";
643 return;
644 } else if (overlay.format() == QImage::Format_ARGB32_Premultiplied) {
645 overlay.convertTo(f: QImage::Format_ARGB32);
646 }
647
648 int i;
649 int j;
650
651 // We don't do 1 bpp
652
653 if (src.depth() == 1) {
654 qWarning() << "1bpp not supported!";
655 return;
656 }
657
658 // Overlay at 8 bpp doesn't use alpha blending
659
660 if (src.depth() == 8) {
661 if (src.colorCount() + overlay.colorCount() > 255) {
662 qWarning() << "Too many colors in src + overlay!";
663 return;
664 }
665
666 // Find transparent pixel in overlay
667 int trans;
668 for (trans = 0; trans < overlay.colorCount(); trans++) {
669 if (qAlpha(rgb: overlay.color(i: trans)) == 0) {
670 qWarning() << "transparent pixel found at " << trans;
671 break;
672 }
673 }
674 if (trans == overlay.colorCount()) {
675 qWarning() << "transparent pixel not found!";
676 return;
677 }
678
679 // Merge color tables
680 int nc = src.colorCount();
681 src.setColorCount(nc + overlay.colorCount());
682 for (i = 0; i < overlay.colorCount(); ++i) {
683 src.setColor(i: nc + i, c: overlay.color(i));
684 }
685
686 // Overwrite nontransparent pixels.
687 unsigned char *oline;
688 unsigned char *sline;
689 for (i = 0; i < src.height(); ++i) {
690 oline = overlay.scanLine(i);
691 sline = src.scanLine(i);
692 for (j = 0; j < src.width(); ++j) {
693 if (oline[j] != trans) {
694 sline[j] = oline[j] + nc;
695 }
696 }
697 }
698 }
699
700 // Overlay at 32 bpp does use alpha blending
701
702 if (src.depth() == 32) {
703 QRgb *oline;
704 QRgb *sline;
705 int r1;
706 int g1;
707 int b1;
708 int a1;
709 int r2;
710 int g2;
711 int b2;
712 int a2;
713
714 for (i = 0; i < src.height(); ++i) {
715 oline = (QRgb *)overlay.scanLine(i);
716 sline = (QRgb *)src.scanLine(i);
717
718 for (j = 0; j < src.width(); ++j) {
719 r1 = qRed(rgb: oline[j]);
720 g1 = qGreen(rgb: oline[j]);
721 b1 = qBlue(rgb: oline[j]);
722 a1 = qAlpha(rgb: oline[j]);
723
724 r2 = qRed(rgb: sline[j]);
725 g2 = qGreen(rgb: sline[j]);
726 b2 = qBlue(rgb: sline[j]);
727 a2 = qAlpha(rgb: sline[j]);
728
729 r2 = (a1 * r1 + (0xff - a1) * r2) >> 8;
730 g2 = (a1 * g1 + (0xff - a1) * g2) >> 8;
731 b2 = (a1 * b1 + (0xff - a1) * b2) >> 8;
732 a2 = qMax(a: a1, b: a2);
733
734 sline[j] = qRgba(r: r2, g: g2, b: b2, a: a2);
735 }
736 }
737 }
738}
739

source code of kiconthemes/src/kiconeffect.cpp