Marble

BlendingAlgorithms.cpp
1// SPDX-FileCopyrightText: 2010 Jens-Michael Hoffmann <jmho@c-xx.com>
2//
3// SPDX-License-Identifier: LGPL-2.1-or-later
4
5#include "BlendingAlgorithms.h"
6
7#include "TextureTile.h"
8
9#include <cmath>
10
11#include <QImage>
12#include <QPainter>
13
14namespace Marble
15{
16
17void OverpaintBlending::blend(QImage *const bottom, TextureTile const *const top) const
18{
19 Q_ASSERT(bottom);
20 Q_ASSERT(top);
21 Q_ASSERT(top->image());
22 Q_ASSERT(bottom->size() == top->image()->size());
23 Q_ASSERT(bottom->format() == QImage::Format_ARGB32_Premultiplied);
24
25 QPainter painter(bottom);
26
27 painter.drawImage(0, 0, *top->image());
28}
29
30void GrayscaleBlending::blend(QImage *const bottom, TextureTile const *const top) const
31{
32 Q_ASSERT(bottom);
33 Q_ASSERT(top);
34 Q_ASSERT(top->image());
35 Q_ASSERT(bottom->size() == top->image()->size());
36 Q_ASSERT(bottom->format() == QImage::Format_ARGB32_Premultiplied);
37 QImage const topImagePremult = top->image()->convertToFormat(QImage::Format_ARGB32_Premultiplied);
38
39 // Draw a grayscale version of the bottom image
40 int const width = bottom->width();
41 int const height = bottom->height();
42
43 for (int y = 0; y < height; ++y) {
44 for (int x = 0; x < width; ++x) {
45 QRgb const topPixel = topImagePremult.pixel(x, y);
46 int const gray = qGray(topPixel);
47 QRgb const grayPixel = qRgb(gray, gray, gray);
48 bottom->setPixel(x, y, grayPixel);
49 }
50 }
51}
52
53void InvertColorBlending::blend(QImage *const bottom, TextureTile const *const top) const
54{
55 Q_ASSERT(bottom);
56 Q_ASSERT(top);
57 Q_ASSERT(top->image());
58 Q_ASSERT(bottom->size() == top->image()->size());
59 Q_ASSERT(bottom->format() == QImage::Format_ARGB32_Premultiplied);
60 QImage const topImagePremult = top->image()->convertToFormat(QImage::Format_ARGB32_Premultiplied);
61
62 // Draw an inverted version of the bottom image
63 int const width = bottom->width();
64 int const height = bottom->height();
65
66 for (int y = 0; y < height; ++y) {
67 for (int x = 0; x < width; ++x) {
68 QRgb const topPixel = topImagePremult.pixel(x, y);
69 QRgb const invertedPixel = qRgb(255 - qRed(topPixel), 255 - qGreen(topPixel), 255 - qBlue(topPixel));
70 bottom->setPixel(x, y, invertedPixel);
71 }
72 }
73}
74
75void InvertHueBlending::blend(QImage *const bottom, TextureTile const *const top) const
76{
77 Q_ASSERT(bottom);
78 Q_ASSERT(top);
79 Q_ASSERT(top->image());
80 Q_ASSERT(bottom->size() == top->image()->size());
81 Q_ASSERT(bottom->format() == QImage::Format_ARGB32_Premultiplied);
82 QImage const topImagePremult = top->image()->convertToFormat(QImage::Format_ARGB32_Premultiplied);
83
84 // Draw an inverted version of the bottom image
85 int const width = bottom->width();
86 int const height = bottom->height();
87
88 for (int y = 0; y < height; ++y) {
89 for (int x = 0; x < width; ++x) {
90 QRgb const topPixel = topImagePremult.pixel(x, y);
91 QColor invertedColor(255 - qRed(topPixel), 255 - qGreen(topPixel), 255 - qBlue(topPixel));
92 int hue = invertedColor.hslHue();
93 int saturation = invertedColor.hslSaturation();
94 int lightness = invertedColor.lightness();
95 QColor naturalInvertedColor = QColor::fromHsl((hue + 195) % 255, saturation, lightness);
96 QRgb naturalInvertedPixel = naturalInvertedColor.rgb();
97 bottom->setPixel(x, y, naturalInvertedPixel);
98 }
99 }
100}
101
102// pre-conditions:
103// - bottom and top image have the same size
104// - bottom image format is ARGB32_Premultiplied
105void IndependentChannelBlending::blend(QImage *const bottom, TextureTile const *const top) const
106{
107 QImage const *const topImage = top->image();
108 Q_ASSERT(topImage);
109 Q_ASSERT(bottom->size() == topImage->size());
110 Q_ASSERT(bottom->format() == QImage::Format_ARGB32_Premultiplied);
111
112 int const width = bottom->width();
113 int const height = bottom->height();
114 QImage const topImagePremult = topImage->convertToFormat(QImage::Format_ARGB32_Premultiplied);
115 for (int y = 0; y < height; ++y) {
116 for (int x = 0; x < width; ++x) {
117 QRgb const bottomPixel = bottom->pixel(x, y);
118 QRgb const topPixel = topImagePremult.pixel(x, y);
119 qreal const resultRed = blendChannel(qRed(bottomPixel) / 255.0, qRed(topPixel) / 255.0);
120 qreal const resultGreen = blendChannel(qGreen(bottomPixel) / 255.0, qGreen(topPixel) / 255.0);
121 qreal const resultBlue = blendChannel(qBlue(bottomPixel) / 255.0, qBlue(topPixel) / 255.0);
122 bottom->setPixel(x, y, qRgb(resultRed * 255.0, resultGreen * 255.0, resultBlue * 255.0));
123 }
124 }
125}
126
127// Neutral blendings
128
129qreal AllanonBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
130{
131 return (bottomColorIntensity + topColorIntensity) / 2.0;
132}
133
134qreal ArcusTangentBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
135{
136 return 2.0 * atan(topColorIntensity / bottomColorIntensity) / M_PI;
137}
138
139qreal GeometricMeanBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
140{
141 return sqrt(bottomColorIntensity * topColorIntensity);
142}
143
144qreal LinearLightBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
145{
146 return qMin(qreal(1.0), qMax(qreal(0.0), qreal(bottomColorIntensity + 2.0 * topColorIntensity - 1.0)));
147}
148
149qreal OverlayBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
150{
151 if (bottomColorIntensity < 0.5)
152 return 2.0 * bottomColorIntensity * topColorIntensity;
153 else
154 return 1.0 - 2.0 * (1.0 - bottomColorIntensity) * (1.0 - topColorIntensity);
155}
156
157qreal ParallelBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
158{
159 Q_UNUSED(bottomColorIntensity);
160 Q_UNUSED(topColorIntensity);
161 // FIXME: return qMin( qMax( 2.0 / ( 1.0 / bottomColorIntensity + 1.0 / topColorIntensity )), 0.0, 1.0 );
162 return 0.0;
163}
164
165qreal TextureBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
166{
167 Q_UNUSED(bottomColorIntensity);
168 Q_UNUSED(topColorIntensity);
169 // FIXME: return qMax( qMin( topColorIntensity + bottomColorIntensity ) - 0.5 ), 1.0 ), 0.0 );
170 return 0.0;
171}
172
173// Darkening blendings
174
175qreal ColorBurnBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
176{
177 Q_UNUSED(bottomColorIntensity);
178 Q_UNUSED(topColorIntensity);
179 // FIXME: check if this formula makes sense
180 return qMin(qreal(1.0), qMax(qreal(0.0), qreal(1.0 - (1.0 - bottomColorIntensity) / topColorIntensity)));
181}
182
183qreal DarkBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
184{
185 return (bottomColorIntensity + 1.0 - topColorIntensity) * topColorIntensity;
186}
187
188qreal DarkenBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
189{
190 // FIXME: is this really ok? not vice versa?
191 return bottomColorIntensity > topColorIntensity ? topColorIntensity : bottomColorIntensity;
192}
193
194qreal DivideBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
195{
196 return log1p(bottomColorIntensity / (1.0 - topColorIntensity) / 8.0) / log(2.0);
197}
198
199qreal GammaDarkBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
200{
201 return pow(bottomColorIntensity, 1.0 / topColorIntensity);
202}
203
204qreal LinearBurnBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
205{
206 return qMax(qreal(0.0), bottomColorIntensity + topColorIntensity - qreal(1.0));
207}
208
209qreal MultiplyBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
210{
211 return bottomColorIntensity * topColorIntensity;
212}
213
214qreal SubtractiveBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
215{
216 return qMax(bottomColorIntensity - topColorIntensity, qreal(0.0));
217}
218
219// Lightening blendings
220
221qreal AdditiveBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
222{
223 return qMin(topColorIntensity + bottomColorIntensity, qreal(1.0));
224}
225
226qreal ColorDodgeBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
227{
228 return qMin(qreal(1.0), qMax(qreal(0.0), qreal(bottomColorIntensity / (1.0 - topColorIntensity))));
229}
230
231qreal GammaLightBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
232{
233 return pow(bottomColorIntensity, topColorIntensity);
234}
235
236qreal HardLightBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
237{
238 return topColorIntensity < 0.5 ? 2.0 * bottomColorIntensity * topColorIntensity : 1.0 - 2.0 * (1.0 - bottomColorIntensity) * (1.0 - topColorIntensity);
239}
240
241qreal LightBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
242{
243 return bottomColorIntensity * (1.0 - topColorIntensity) + pow(topColorIntensity, 2);
244}
245
246qreal LightenBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
247{
248 // is this ok?
249 return bottomColorIntensity < topColorIntensity ? topColorIntensity : bottomColorIntensity;
250}
251
252qreal PinLightBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
253{
254 return qMax(qreal(0.0), qMax(qreal(2.0 + topColorIntensity - 1.0), qMin(bottomColorIntensity, qreal(2.0 * topColorIntensity))));
255}
256
257qreal ScreenBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
258{
259 return 1.0 - (1.0 - bottomColorIntensity) * (1.0 - topColorIntensity);
260}
261
262qreal SoftLightBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
263{
264 return pow(bottomColorIntensity, pow(2.0, (2.0 * (0.5 - topColorIntensity))));
265}
266
267qreal VividLightBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
268{
269 return topColorIntensity < 0.5 ? qMin(qreal(1.0), qMax(qreal(0.0), qreal(1.0 - (1.0 - bottomColorIntensity) / (2.0 * topColorIntensity))))
270 : qMin(qreal(1.0), qMax(qreal(0.0), qreal(bottomColorIntensity / (2.0 * (1.0 - topColorIntensity)))));
271}
272
273// Inverter blendings
274
275qreal AdditiveSubtractiveBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
276{
277 Q_UNUSED(bottomColorIntensity);
278 Q_UNUSED(topColorIntensity);
279 // FIXME:
280 // return qMin( 1.0, qMax( 0.0, abs( bottomColorIntensity * bottomColorIntensity
281 // - topColorIntensity * topColorIntensity )));
282 return 0.0;
283}
284
285qreal BleachBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
286{
287 // FIXME: "why this is the same formula as Screen Blending? Please correct.)"
288 return 1.0 - (1.0 - bottomColorIntensity) * (1.0 - topColorIntensity);
289}
290
291qreal DifferenceBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
292{
293 return qMax(qMin(qreal(1.0), qreal(bottomColorIntensity - topColorIntensity + 0.5)), qreal(0.0));
294}
295
296qreal EquivalenceBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
297{
298 return 1.0 - qAbs(bottomColorIntensity - topColorIntensity);
299}
300
301qreal HalfDifferenceBlending::blendChannel(qreal const bottomColorIntensity, qreal const topColorIntensity) const
302{
303 return bottomColorIntensity + topColorIntensity - 2.0 * (bottomColorIntensity * topColorIntensity);
304}
305
306// Special purpose blendings
307
308void CloudsBlending::blend(QImage *const bottom, TextureTile const *const top) const
309{
310 QImage const *const topImage = top->image();
311 Q_ASSERT(topImage);
312 Q_ASSERT(bottom->size() == topImage->size());
313 int const width = bottom->width();
314 int const height = bottom->height();
315 for (int y = 0; y < height; ++y) {
316 for (int x = 0; x < width; ++x) {
317 qreal const c = qRed(topImage->pixel(x, y)) / 255.0;
318 QRgb const bottomPixel = bottom->pixel(x, y);
319 int const bottomRed = qRed(bottomPixel);
320 int const bottomGreen = qGreen(bottomPixel);
321 int const bottomBlue = qBlue(bottomPixel);
322 bottom->setPixel(
323 x,
324 y,
325 qRgb((int)(bottomRed + (255 - bottomRed) * c), (int)(bottomGreen + (255 - bottomGreen) * c), (int)(bottomBlue + (255 - bottomBlue) * c)));
326 }
327 }
328}
329
330}
KGUIADDONS_EXPORT qreal hue(const QColor &)
Binds a QML item to a specific geodetic location in screen coordinates.
QColor fromHsl(int h, int s, int l, int a)
QRgb rgb() const const
Format_ARGB32_Premultiplied
QImage convertToFormat(Format format, Qt::ImageConversionFlags flags) &&
Format format() const const
int height() const const
QRgb pixel(const QPoint &position) const const
void setPixel(const QPoint &position, uint index_or_rgb)
QSize size() const const
int width() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 8 2024 12:02:42 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.