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}
53
54void InvertColorBlending::blend( QImage * const bottom, TextureTile const * const top ) const
55{
56 Q_ASSERT( bottom );
57 Q_ASSERT( top );
58 Q_ASSERT( top->image() );
59 Q_ASSERT( bottom->size() == top->image()->size() );
60 Q_ASSERT( bottom->format() == QImage::Format_ARGB32_Premultiplied );
61 QImage const topImagePremult = top->image()->convertToFormat( QImage::Format_ARGB32_Premultiplied );
62
63 // Draw an inverted version of the bottom image
64 int const width = bottom->width();
65 int const height = bottom->height();
66
67 for ( int y = 0; y < height; ++y ) {
68 for ( int x = 0; x < width; ++x ) {
69 QRgb const topPixel = topImagePremult.pixel( x, y );
70 QRgb const invertedPixel = qRgb( 255 - qRed(topPixel), 255 - qGreen(topPixel), 255 - qBlue(topPixel) );
71 bottom->setPixel( x, y, invertedPixel );
72 }
73 }
74}
75
76void InvertHueBlending::blend( QImage * const bottom, TextureTile const * const top ) const
77{
78 Q_ASSERT( bottom );
79 Q_ASSERT( top );
80 Q_ASSERT( top->image() );
81 Q_ASSERT( bottom->size() == top->image()->size() );
82 Q_ASSERT( bottom->format() == QImage::Format_ARGB32_Premultiplied );
83 QImage const topImagePremult = top->image()->convertToFormat( QImage::Format_ARGB32_Premultiplied );
84
85 // Draw an inverted version of the bottom image
86 int const width = bottom->width();
87 int const height = bottom->height();
88
89 for ( int y = 0; y < height; ++y ) {
90 for ( int x = 0; x < width; ++x ) {
91 QRgb const topPixel = topImagePremult.pixel( x, y );
92 QColor invertedColor(255 - qRed(topPixel), 255 - qGreen(topPixel), 255 - qBlue(topPixel));
93 int hue = invertedColor.hslHue();
94 int saturation = invertedColor.hslSaturation();
95 int lightness = invertedColor.lightness();
96 QColor naturalInvertedColor = QColor::fromHsl((hue + 195) % 255 , saturation, lightness);
97 QRgb naturalInvertedPixel = naturalInvertedColor.rgb();
98 bottom->setPixel( x, y, naturalInvertedPixel );
99 }
100 }
101}
102
103// pre-conditions:
104// - bottom and top image have the same size
105// - bottom image format is ARGB32_Premultiplied
106void IndependentChannelBlending::blend( QImage * const bottom,
107 TextureTile const * const top ) const
108{
109 QImage const * const topImage = top->image();
110 Q_ASSERT( topImage );
111 Q_ASSERT( bottom->size() == topImage->size() );
112 Q_ASSERT( bottom->format() == QImage::Format_ARGB32_Premultiplied );
113
114 int const width = bottom->width();
115 int const height = bottom->height();
116 QImage const topImagePremult = topImage->convertToFormat( QImage::Format_ARGB32_Premultiplied );
117 for ( int y = 0; y < height; ++y ) {
118 for ( int x = 0; x < width; ++x ) {
119 QRgb const bottomPixel = bottom->pixel( x, y );
120 QRgb const topPixel = topImagePremult.pixel( x, y );
121 qreal const resultRed = blendChannel( qRed( bottomPixel ) / 255.0,
122 qRed( topPixel ) / 255.0 );
123 qreal const resultGreen = blendChannel( qGreen( bottomPixel ) / 255.0,
124 qGreen( topPixel ) / 255.0 );
125 qreal const resultBlue = blendChannel( qBlue( bottomPixel ) / 255.0,
126 qBlue( topPixel ) / 255.0 );
127 bottom->setPixel( x, y, qRgb( resultRed * 255.0,
128 resultGreen * 255.0,
129 resultBlue * 255.0 ));
130 }
131 }
132}
133
134
135// Neutral blendings
136
137qreal AllanonBlending::blendChannel( qreal const bottomColorIntensity,
138 qreal const topColorIntensity ) const
139{
140 return ( bottomColorIntensity + topColorIntensity ) / 2.0;
141}
142
143qreal ArcusTangentBlending::blendChannel( qreal const bottomColorIntensity,
144 qreal const topColorIntensity ) const
145{
146 return 2.0 * atan( topColorIntensity / bottomColorIntensity ) / M_PI;
147}
148
149qreal GeometricMeanBlending::blendChannel( qreal const bottomColorIntensity,
150 qreal const topColorIntensity ) const
151{
152 return sqrt( bottomColorIntensity * topColorIntensity );
153}
154
155qreal LinearLightBlending::blendChannel( qreal const bottomColorIntensity,
156 qreal const topColorIntensity ) const
157{
158 return qMin( qreal( 1.0 ),
159 qMax( qreal( 0.0 ), qreal( bottomColorIntensity + 2.0 * topColorIntensity - 1.0 )));
160}
161
162qreal OverlayBlending::blendChannel( qreal const bottomColorIntensity,
163 qreal const topColorIntensity ) const
164{
165 if ( bottomColorIntensity < 0.5 )
166 return 2.0 * bottomColorIntensity * topColorIntensity;
167 else
168 return 1.0 - 2.0 * ( 1.0 - bottomColorIntensity ) * ( 1.0 - topColorIntensity );
169}
170
171qreal ParallelBlending::blendChannel( qreal const bottomColorIntensity,
172 qreal const topColorIntensity ) const
173{
174 Q_UNUSED(bottomColorIntensity);
175 Q_UNUSED(topColorIntensity);
176 // FIXME: return qMin( qMax( 2.0 / ( 1.0 / bottomColorIntensity + 1.0 / topColorIntensity )), 0.0, 1.0 );
177 return 0.0;
178}
179
180qreal TextureBlending::blendChannel( qreal const bottomColorIntensity,
181 qreal const topColorIntensity ) const
182{
183 Q_UNUSED(bottomColorIntensity);
184 Q_UNUSED(topColorIntensity);
185 // FIXME: return qMax( qMin( topColorIntensity + bottomColorIntensity ) - 0.5 ), 1.0 ), 0.0 );
186 return 0.0;
187}
188
189
190// Darkening blendings
191
192qreal ColorBurnBlending::blendChannel( qreal const bottomColorIntensity,
193 qreal const topColorIntensity ) const
194{
195 Q_UNUSED(bottomColorIntensity);
196 Q_UNUSED(topColorIntensity);
197 // FIXME: check if this formula makes sense
198 return qMin( qreal( 1.0 ),
199 qMax( qreal( 0.0 ), qreal( 1.0 - ( 1.0 - bottomColorIntensity ) / topColorIntensity )));
200}
201
202qreal DarkBlending::blendChannel( qreal const bottomColorIntensity,
203 qreal const topColorIntensity ) const
204{
205 return ( bottomColorIntensity + 1.0 - topColorIntensity ) * topColorIntensity;
206}
207
208qreal DarkenBlending::blendChannel( qreal const bottomColorIntensity,
209 qreal const topColorIntensity ) const
210{
211 // FIXME: is this really ok? not vice versa?
212 return bottomColorIntensity > topColorIntensity ? topColorIntensity : bottomColorIntensity;
213}
214
215qreal DivideBlending::blendChannel( qreal const bottomColorIntensity,
216 qreal const topColorIntensity ) const
217{
218 return log1p( bottomColorIntensity / ( 1.0 - topColorIntensity ) / 8.0) / log(2.0);
219}
220
221qreal GammaDarkBlending::blendChannel( qreal const bottomColorIntensity,
222 qreal const topColorIntensity ) const
223{
224 return pow( bottomColorIntensity, 1.0 / topColorIntensity );
225}
226
227qreal LinearBurnBlending::blendChannel( qreal const bottomColorIntensity,
228 qreal const topColorIntensity ) const
229{
230 return qMax( qreal(0.0), bottomColorIntensity + topColorIntensity - qreal( 1.0 ) );
231}
232
233qreal MultiplyBlending::blendChannel( qreal const bottomColorIntensity,
234 qreal const topColorIntensity ) const
235{
236 return bottomColorIntensity * topColorIntensity;
237}
238
239qreal SubtractiveBlending::blendChannel( qreal const bottomColorIntensity,
240 qreal const topColorIntensity ) const
241{
242 return qMax( bottomColorIntensity - topColorIntensity, qreal(0.0) );
243}
244
245
246// Lightening blendings
247
248qreal AdditiveBlending::blendChannel( qreal const bottomColorIntensity,
249 qreal const topColorIntensity ) const
250{
251 return qMin( topColorIntensity + bottomColorIntensity, qreal(1.0) );
252}
253
254qreal ColorDodgeBlending::blendChannel( qreal const bottomColorIntensity,
255 qreal const topColorIntensity ) const
256{
257 return qMin( qreal( 1.0 ),
258 qMax( qreal( 0.0 ), qreal( bottomColorIntensity / ( 1.0 - topColorIntensity ))));
259}
260
261qreal GammaLightBlending::blendChannel( qreal const bottomColorIntensity,
262 qreal const topColorIntensity ) const
263{
264 return pow( bottomColorIntensity, topColorIntensity );
265}
266
267qreal HardLightBlending::blendChannel( qreal const bottomColorIntensity,
268 qreal const topColorIntensity ) const
269{
270 return topColorIntensity < 0.5
271 ? 2.0 * bottomColorIntensity * topColorIntensity
272 : 1.0 - 2.0 * ( 1.0 - bottomColorIntensity ) * ( 1.0 - topColorIntensity );
273}
274
275qreal LightBlending::blendChannel( qreal const bottomColorIntensity,
276 qreal const topColorIntensity ) const
277{
278 return bottomColorIntensity * ( 1.0 - topColorIntensity ) + pow( topColorIntensity, 2 );
279}
280
281qreal LightenBlending::blendChannel( qreal const bottomColorIntensity,
282 qreal const topColorIntensity ) const
283{
284 // is this ok?
285 return bottomColorIntensity < topColorIntensity ? topColorIntensity : bottomColorIntensity;
286}
287
288qreal PinLightBlending::blendChannel( qreal const bottomColorIntensity,
289 qreal const topColorIntensity ) const
290{
291 return qMax( qreal(0.0), qMax( qreal(2.0 + topColorIntensity - 1.0),
292 qMin( bottomColorIntensity, qreal(2.0 * topColorIntensity ))));
293}
294
295qreal ScreenBlending::blendChannel( qreal const bottomColorIntensity,
296 qreal const topColorIntensity ) const
297{
298 return 1.0 - ( 1.0 - bottomColorIntensity ) * ( 1.0 - topColorIntensity );
299}
300
301qreal SoftLightBlending::blendChannel( qreal const bottomColorIntensity,
302 qreal const topColorIntensity ) const
303{
304 return pow( bottomColorIntensity, pow( 2.0, ( 2.0 * ( 0.5 - topColorIntensity ))));
305}
306
307qreal VividLightBlending::blendChannel( qreal const bottomColorIntensity,
308 qreal const topColorIntensity ) const
309{
310 return topColorIntensity < 0.5
311 ? qMin( qreal( 1.0 ),
312 qMax( qreal( 0.0 ), qreal( 1.0 - ( 1.0 - bottomColorIntensity ) / ( 2.0 * topColorIntensity ))))
313 : qMin( qreal( 1.0 ),
314 qMax( qreal( 0.0 ), qreal( bottomColorIntensity / ( 2.0 * ( 1.0 - topColorIntensity )))));
315}
316
317
318// Inverter blendings
319
320qreal AdditiveSubtractiveBlending::blendChannel( qreal const bottomColorIntensity,
321 qreal const topColorIntensity ) const
322{
323 Q_UNUSED(bottomColorIntensity);
324 Q_UNUSED(topColorIntensity);
325 // FIXME:
326 // return qMin( 1.0, qMax( 0.0, abs( bottomColorIntensity * bottomColorIntensity
327 // - topColorIntensity * topColorIntensity )));
328 return 0.0;
329}
330
331qreal BleachBlending::blendChannel( qreal const bottomColorIntensity,
332 qreal const topColorIntensity ) const
333{
334 // FIXME: "why this is the same formula as Screen Blending? Please correct.)"
335 return 1.0 - ( 1.0 - bottomColorIntensity ) * ( 1.0 - topColorIntensity );
336}
337
338qreal DifferenceBlending::blendChannel( qreal const bottomColorIntensity,
339 qreal const topColorIntensity ) const
340{
341 return qMax( qMin( qreal( 1.0 ), qreal( bottomColorIntensity - topColorIntensity + 0.5 )),
342 qreal( 0.0 ));
343}
344
345qreal EquivalenceBlending::blendChannel( qreal const bottomColorIntensity,
346 qreal const topColorIntensity ) const
347{
348 return 1.0 - qAbs( bottomColorIntensity - topColorIntensity );
349}
350
351qreal HalfDifferenceBlending::blendChannel( qreal const bottomColorIntensity,
352 qreal const topColorIntensity ) const
353{
354 return bottomColorIntensity + topColorIntensity
355 - 2.0 * ( bottomColorIntensity * topColorIntensity );
356}
357
358
359// Special purpose blendings
360
361void CloudsBlending::blend( QImage * const bottom, TextureTile const * const top ) const
362{
363 QImage const * const topImage = top->image();
364 Q_ASSERT( topImage );
365 Q_ASSERT( bottom->size() == topImage->size() );
366 int const width = bottom->width();
367 int const height = bottom->height();
368 for ( int y = 0; y < height; ++y ) {
369 for ( int x = 0; x < width; ++x ) {
370 qreal const c = qRed( topImage->pixel( x, y )) / 255.0;
371 QRgb const bottomPixel = bottom->pixel( x, y );
372 int const bottomRed = qRed( bottomPixel );
373 int const bottomGreen = qGreen( bottomPixel );
374 int const bottomBlue = qBlue( bottomPixel );
375 bottom->setPixel( x, y, qRgb(( int )( bottomRed + ( 255 - bottomRed ) * c ),
376 ( int )( bottomGreen + ( 255 - bottomGreen ) * c ),
377 ( int )( bottomBlue + ( 255 - bottomBlue ) * c )));
378 }
379 }
380}
381
382
383}
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 Tue Mar 26 2024 11:18:16 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.